From 5264eed642f46644e5274348bb6ce62db4d9bab9 Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Tue, 30 Jun 2026 21:07:00 +0200 Subject: [PATCH 01/58] new buy/sell flow (wip) --- .../buy_sell/buy_sell_selector_modal.dart | 94 +++++++++++++++++++ .../widgets/coins_page/cards/cards_view.dart | 5 +- res/pictures/plus.svg | 3 + res/pictures/sell.svg | 3 + res/values/strings_en.arb | 6 ++ 5 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 lib/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart create mode 100644 res/pictures/plus.svg create mode 100644 res/pictures/sell.svg diff --git a/lib/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart b/lib/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart new file mode 100644 index 000000000..88ddcf986 --- /dev/null +++ b/lib/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart @@ -0,0 +1,94 @@ +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/new-ui/modal_navigator.dart'; +import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart'; +import 'package:cake_wallet/src/widgets/cake_image_widget.dart'; +import 'package:cake_wallet/themes/core/theme_extension.dart'; +import 'package:flutter/material.dart'; + +enum BuySellPageMode { buy, sell } + +class BuySellSelectorModal extends StatelessWidget { + const BuySellSelectorModal({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.vertical(top: Radius.circular(18)), + gradient: LinearGradient( + colors: [ + Theme.of(context).colorScheme.surface, + Theme.of(context).colorScheme.surfaceDim, + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + child: SafeArea( + top: false, + child: Column(spacing:24, mainAxisSize: MainAxisSize.min, children: [ + SizedBox.shrink(), + Text(S.of(context).buy_or_sell, style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500),), + Text(S.of(context).buy_or_sell_desc, style: TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant),), + Padding( + padding: EdgeInsets.symmetric(horizontal: 18.0), + child: Column(spacing: 12,children: [ + BuySellSelectorModalButton(title: S.of(context).buy_crypto, description: S.of(context).buy_crypto_desc, iconPath: "assets/new-ui/plus.svg", onTap: ()=>openBuySellPage(context, BuySellPageMode.buy),), + BuySellSelectorModalButton(title: S.of(context).sell_crypto, description: S.of(context).sell_crypto_desc, iconPath: "assets/new-ui/sell.svg", onTap: ()=>openBuySellPage(context, BuySellPageMode.sell)) + ],), + ), + SizedBox.shrink() + ]) + ) + ); + } + + void openBuySellPage(BuildContext context, BuySellPageMode mode) { + Navigator.of(context).pop(); + // showModalBottomSheet(context: context, builder: ModalNavigator(rootPage: ,)) + } +} + + +class BuySellSelectorModalButton extends StatelessWidget { + const BuySellSelectorModalButton({super.key, required this.title, required this.description, required this.iconPath, required this.onTap}); + + final String title; + final String description; + final String iconPath; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + border: Border.all( + width: 1, color: Theme.of(context).colorScheme.surfaceContainerHigh, + + ), + gradient: LinearGradient( + colors: [ + context.customColors.cardGradientColorPrimary, + context.customColors.cardGradientColorSecondary + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + child: Padding(padding: EdgeInsets.all(24), child: Row(spacing: 20, children: [ + + CakeImageWidget(imageUrl: iconPath, width: 55, height: 55, colorFilter: ColorFilter.mode(Theme.of(context).colorScheme.onSurfaceVariant,BlendMode.srcIn),), + Column(spacing: 8, crossAxisAlignment: CrossAxisAlignment.start,children: [ + Text(title, style: TextStyle(fontWeight: FontWeight.w500, fontSize: 16),), + Text(description, style: TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant),) + ],) + + ],),), + + ), + ); + } +} diff --git a/lib/new-ui/widgets/coins_page/cards/cards_view.dart b/lib/new-ui/widgets/coins_page/cards/cards_view.dart index 238f20502..ca6cb6bb8 100644 --- a/lib/new-ui/widgets/coins_page/cards/cards_view.dart +++ b/lib/new-ui/widgets/coins_page/cards/cards_view.dart @@ -7,6 +7,7 @@ import 'package:cake_wallet/entities/bitcoin_amount_display_mode.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/new-ui/modal_navigator.dart'; import 'package:cake_wallet/new-ui/pages/send_page.dart'; +import 'package:cake_wallet/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/utils/feature_flag.dart'; import 'package:cake_wallet/utils/payment_request.dart'; @@ -177,7 +178,9 @@ class _CardsViewState extends State { label: S.current.buy, icon: Icons.arrow_forward_ios_rounded, iconSize: 12, - onTap: () => Navigator.of(context).pushNamed(Routes.buySellPage), + onTap: () { + showModalBottomSheet(context: context, builder: (context)=>BuySellSelectorModal()); + }, ) ] : []; diff --git a/res/pictures/plus.svg b/res/pictures/plus.svg new file mode 100644 index 000000000..e6b5f64e3 --- /dev/null +++ b/res/pictures/plus.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/pictures/sell.svg b/res/pictures/sell.svg new file mode 100644 index 000000000..50c8271e7 --- /dev/null +++ b/res/pictures/sell.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 1debd0a8f..8cf1c7835 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -135,7 +135,11 @@ "buy": "Buy", "buy_alert_content": "Currently we only support the purchase of Bitcoin, Ethereum, Litecoin, and Monero. Please create or switch to your Bitcoin, Ethereum, Litecoin, or Monero wallet.", "buy_bitcoin": "Buy Bitcoin", + "buy_crypto": "Buy Crypto", + "buy_crypto_desc": "Fund your wallet with fiat", "buy_now": "Buy Now", + "buy_or_sell": "Buy or Sell", + "buy_or_sell_desc": "Exchange fiat for crypto thanks to our providers", "buy_provider_unavailable": "Provider currently unavailable.", "buy_sell_pair_is_not_supported_warning": "This currency pair isn’t supported by any provider for the selected payment method. Please choose a different pair or try changing the payment method.", "buy_with": "Buy with", @@ -988,6 +992,8 @@ "selected_trocador_provider": "selected Trocador provider", "sell": "Sell", "sell_alert_content": "We currently only support the sale of Bitcoin, Ethereum and Litecoin. Please create or switch to your Bitcoin, Ethereum or Litecoin wallet.", + "sell_crypto": "Sell Crypto", + "sell_crypto_desc": "Get fiat for your crypto", "sell_monero_com_alert_content": "Selling Monero is not supported yet", "send": "Send", "send_address": "${cryptoCurrency} address", From 0c469f20900b36e6b31ffa16ac776897592a4e8a Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Tue, 30 Jun 2026 22:06:47 +0200 Subject: [PATCH 02/58] wip --- lib/buy/buy_provider.dart | 3 +- lib/di.dart | 5 ++ .../pages/buy_sell/buy_sell_amount_page.dart | 50 +++++++++++++++++++ .../buy_sell/buy_sell_selector_modal.dart | 5 +- 4 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart diff --git a/lib/buy/buy_provider.dart b/lib/buy/buy_provider.dart index 7cc92eb9b..5053a7ef8 100644 --- a/lib/buy/buy_provider.dart +++ b/lib/buy/buy_provider.dart @@ -43,8 +43,7 @@ abstract class BuyProvider { required double amount, required bool isBuyAction, required String cryptoCurrencyAddress, - String? countryCode}) => - null; + String? countryCode}); Future requestUrl(String amount, String sourceCurrency) => throw UnimplementedError(); diff --git a/lib/di.dart b/lib/di.dart index 821f51130..a4ae25ca9 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -56,6 +56,7 @@ import 'package:cake_wallet/new-ui/pages/account_customizer.dart'; import 'package:cake_wallet/new-ui/pages/bridge/bridge_amount_page.dart'; import 'package:cake_wallet/new-ui/pages/bridge/bridge_network_page.dart'; import 'package:cake_wallet/new-ui/pages/bridge/bridge_receiving_wallet_page.dart'; +import 'package:cake_wallet/new-ui/pages/buy_sell/buy_sell_amount_page.dart'; import 'package:cake_wallet/new-ui/pages/coin_control_page.dart'; import 'package:cake_wallet/new-ui/pages/addresses_page.dart'; import 'package:cake_wallet/new-ui/pages/home_page.dart'; @@ -64,6 +65,7 @@ import 'package:cake_wallet/new-ui/pages/lightning_username_page.dart'; import 'package:cake_wallet/new-ui/pages/receive_page.dart'; import 'package:cake_wallet/new-ui/viewmodels/lightning_username/lightning_username_bloc.dart'; import 'package:cake_wallet/new-ui/widgets/addresses_page/address_label_input.dart'; +import 'package:cake_wallet/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart'; import 'package:cake_wallet/new-ui/widgets/coins_page/assets_history/transaction_details_modal.dart'; import 'package:cake_wallet/new-ui/widgets/receive_page/receive_label_modal.dart'; import 'package:cake_wallet/new-ui/pages/swap_page.dart'; @@ -1494,6 +1496,9 @@ Future setup({ getIt.registerFactory(() => BuySellViewModel(getIt.get())); + getIt.registerFactoryParam((mode, _) => + NewBuySellAmountPage(mode: mode, buySellViewModel: getIt.get())); + getIt.registerFactory(() => BuySellPage(getIt.get())); getIt.registerFactoryParam, void>((List args, _) { diff --git a/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart b/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart new file mode 100644 index 000000000..b29a747db --- /dev/null +++ b/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart @@ -0,0 +1,50 @@ +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart'; +import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart'; +import 'package:cake_wallet/view_model/buy/buy_sell_view_model.dart'; +import 'package:flutter/material.dart'; + +class NewBuySellAmountPage extends StatelessWidget { + const NewBuySellAmountPage({super.key, required this.mode, required this.buySellViewModel}); + + final BuySellPageMode mode; + final BuySellViewModel buySellViewModel; + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.vertical(top: Radius.circular(18)), + gradient: LinearGradient( + colors: [ + Theme.of(context).colorScheme.surface, + Theme.of(context).colorScheme.surfaceDim, + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + child: SafeArea( + child: Column(children: [ + ModalTopBar(title: _pageTitle, leadingIcon: Icon(Icons.close), onLeadingPressed: Navigator.of(context).pop,), + Expanded(child: GridView.builder(itemCount: 6, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + crossAxisSpacing: 10, + mainAxisSpacing: 10, + mainAxisExtent: 150), itemBuilder: (context, index){ + + + })) + ],), + ), + ); + } + + String get _pageTitle => + mode == BuySellPageMode.buy ? S.current.buy : S.current.sell + + ((buySellViewModel.cryptoCurrencies.length == 1) + ? " ${buySellViewModel.cryptoCurrencies.first.fullName}" + : ""); +} + + diff --git a/lib/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart b/lib/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart index 88ddcf986..85b82566a 100644 --- a/lib/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart +++ b/lib/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart @@ -1,6 +1,7 @@ +import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/new-ui/modal_navigator.dart'; -import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart'; +import 'package:cake_wallet/new-ui/pages/buy_sell/buy_sell_amount_page.dart'; import 'package:cake_wallet/src/widgets/cake_image_widget.dart'; import 'package:cake_wallet/themes/core/theme_extension.dart'; import 'package:flutter/material.dart'; @@ -45,7 +46,7 @@ class BuySellSelectorModal extends StatelessWidget { void openBuySellPage(BuildContext context, BuySellPageMode mode) { Navigator.of(context).pop(); - // showModalBottomSheet(context: context, builder: ModalNavigator(rootPage: ,)) + showModalBottomSheet(isScrollControlled: true, context: context, builder: (modalContext)=>ModalNavigator(rootPage: getIt.get(param1: mode), parentContext: context,)); } } From a2a0500213788270016e221ff779b8b08fe55846 Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Wed, 1 Jul 2026 09:56:20 +0200 Subject: [PATCH 03/58] wip --- lib/buy/buy_provider.dart | 2 +- .../pages/buy_sell/buy_sell_amount_page.dart | 168 ++++++++++++++++-- .../buy_sell/buy_sell_selector_modal.dart | 2 +- lib/view_model/buy/buy_sell_view_model.dart | 53 ++++++ res/values/strings_en.arb | 2 + 5 files changed, 209 insertions(+), 18 deletions(-) diff --git a/lib/buy/buy_provider.dart b/lib/buy/buy_provider.dart index 5053a7ef8..d2a4bc966 100644 --- a/lib/buy/buy_provider.dart +++ b/lib/buy/buy_provider.dart @@ -43,7 +43,7 @@ abstract class BuyProvider { required double amount, required bool isBuyAction, required String cryptoCurrencyAddress, - String? countryCode}); + String? countryCode}) => null; Future requestUrl(String amount, String sourceCurrency) => throw UnimplementedError(); diff --git a/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart b/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart index b29a747db..3b36c5834 100644 --- a/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart +++ b/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart @@ -1,15 +1,25 @@ +import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart'; import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart'; +import 'package:cake_wallet/themes/core/theme_extension.dart'; import 'package:cake_wallet/view_model/buy/buy_sell_view_model.dart'; +import 'package:cw_core/amount/money.dart'; import 'package:flutter/material.dart'; -class NewBuySellAmountPage extends StatelessWidget { +class NewBuySellAmountPage extends StatefulWidget { const NewBuySellAmountPage({super.key, required this.mode, required this.buySellViewModel}); final BuySellPageMode mode; final BuySellViewModel buySellViewModel; + @override + State createState() => _NewBuySellAmountPageState(); +} + +class _NewBuySellAmountPageState extends State { + bool _customAmountMode = false; + @override Widget build(BuildContext context) { return Container( @@ -25,26 +35,152 @@ class NewBuySellAmountPage extends StatelessWidget { ), ), child: SafeArea( - child: Column(children: [ - ModalTopBar(title: _pageTitle, leadingIcon: Icon(Icons.close), onLeadingPressed: Navigator.of(context).pop,), - Expanded(child: GridView.builder(itemCount: 6, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - crossAxisSpacing: 10, - mainAxisSpacing: 10, - mainAxisExtent: 150), itemBuilder: (context, index){ - - - })) - ],), + child: Column( + children: [ + ModalTopBar( + title: _pageTitle, + leadingIcon: Icon(Icons.close), + onLeadingPressed: Navigator.of(context).pop, + ), + Expanded( + child: BuySellDefaultAmountSelector( + defaultAmounts: widget.buySellViewModel.defaultAmounts, + currency: widget.buySellViewModel.fiatCurrency, + mode: widget.mode, + onSelected: (amount) { + if (amount == null) { + setState(() { + _customAmountMode = true; + }); + } else { + widget.buySellViewModel.changeFiatAmount(amount: amount); + } + }, + )) + ], + ), ), ); } - String get _pageTitle => - mode == BuySellPageMode.buy ? S.current.buy : S.current.sell + - ((buySellViewModel.cryptoCurrencies.length == 1) - ? " ${buySellViewModel.cryptoCurrencies.first.fullName}" + String get _pageTitle => widget.mode == BuySellPageMode.buy + ? S.current.buy + : S.current.sell + + ((widget.buySellViewModel.cryptoCurrencies.length == 1) + ? " ${widget.buySellViewModel.cryptoCurrencies.first.fullName}" : ""); } +class BuySellDefaultAmountSelector extends StatelessWidget { + const BuySellDefaultAmountSelector( + {super.key, + required this.defaultAmounts, + required this.currency, + required this.mode, + required this.onSelected}); + final List defaultAmounts; + final FiatCurrency currency; + final BuySellPageMode mode; + final Function(String?) onSelected; + + @override + Widget build(BuildContext context) { + return Column( + spacing: 24, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + mode == BuySellPageMode.sell + ? S.of(context).choose_amount_to_sell + : S.of(context).choose_amount_to_buy, + style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 18.0), + child: GridView.builder( + shrinkWrap: true, + // +1 for "custom" option + itemCount: defaultAmounts.length + 1, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, crossAxisSpacing: 8, mainAxisSpacing: 16, mainAxisExtent: 105), + itemBuilder: (context, index) { + final String? item = index == defaultAmounts.length ? null : defaultAmounts[index]; + + return BuySellAmountPill( + amount: item == null ? null : Money.parse(item, currency), + onTap: () => onSelected(item), + ); + }), + ), + ], + ); + } +} + +class BuySellAmountPill extends StatelessWidget { + const BuySellAmountPill({super.key, this.amount, required this.onTap}); + + final Money? amount; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(9999999999), + border: Border.all( + width: 1, + color: Theme.of(context).colorScheme.surfaceContainerHigh, + ), + gradient: LinearGradient( + colors: [ + context.customColors.cardGradientColorPrimary, + context.customColors.cardGradientColorSecondary + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + child: Material( + color: Colors.transparent, + borderRadius: BorderRadius.circular(9999999999), + child: InkWell( + borderRadius: BorderRadius.circular(9999999999), + onTap: onTap, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + spacing: 4, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (amount != null) + Text( + amount!.toStringWithPrecision(fractionalDigits: 0), + style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600), + ), + Text( + amount?.currency.symbol ?? S.of(context).custom, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: amount == null + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.onSurfaceVariant), + ) + ], + ), + if (amount == null) + Text( + S.of(context).enter_amount, + style: TextStyle( + fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant), + ) + ], + ), + ), + ), + ); + } +} diff --git a/lib/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart b/lib/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart index 85b82566a..7e6efd004 100644 --- a/lib/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart +++ b/lib/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart @@ -46,7 +46,7 @@ class BuySellSelectorModal extends StatelessWidget { void openBuySellPage(BuildContext context, BuySellPageMode mode) { Navigator.of(context).pop(); - showModalBottomSheet(isScrollControlled: true, context: context, builder: (modalContext)=>ModalNavigator(rootPage: getIt.get(param1: mode), parentContext: context,)); + showModalBottomSheet(useSafeArea:true, isScrollControlled: true, context: context, builder: (modalContext)=>ModalNavigator(rootPage: getIt.get(param1: mode), parentContext: context,)); } } diff --git a/lib/view_model/buy/buy_sell_view_model.dart b/lib/view_model/buy/buy_sell_view_model.dart index a299aebac..4d181d7f6 100644 --- a/lib/view_model/buy/buy_sell_view_model.dart +++ b/lib/view_model/buy/buy_sell_view_model.dart @@ -149,6 +149,59 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S @observable bool skipIsReadyToTradeReaction = false; + + + // based on usd values, should have roughly equal worth (was done with ai though so it's subject to correction) + static final Map> _defaultAmountsMap = { + FiatCurrency.amd: ["20000", "40000", "200000", "400000", "1000000"], + FiatCurrency.aud: ["100", "200", "1000", "2000", "5000"], + FiatCurrency.bgn: ["100", "200", "1000", "2000", "5000"], + FiatCurrency.brl: ["250", "500", "2500", "5000", "12500"], + FiatCurrency.cad: ["50", "100", "500", "1000", "2500"], + FiatCurrency.chf: ["50", "100", "500", "1000", "2500"], + FiatCurrency.clp: ["50000", "100000", "500000", "1000000", "2500000"], + FiatCurrency.cop: ["200000", "400000", "2000000", "4000000", "10000000"], + FiatCurrency.czk: ["1000", "2000", "10000", "20000", "50000"], + FiatCurrency.dkk: ["400", "800", "4000", "8000", "20000"], + FiatCurrency.egp: ["2500", "5000", "25000", "50000", "125000"], + FiatCurrency.eur: ["50", "100", "500", "1000", "2500"], + FiatCurrency.gbp: ["50", "100", "500", "1000", "2500"], + FiatCurrency.gtq: ["400", "800", "4000", "8000", "20000"], + FiatCurrency.hkd: ["400", "800", "4000", "8000", "20000"], + FiatCurrency.hrk: ["400", "800", "4000", "8000", "20000"], + FiatCurrency.huf: ["20000", "40000", "200000", "400000", "1000000"], + FiatCurrency.idr: ["800000", "1600000", "8000000", "16000000", "40000000"], + FiatCurrency.ils: ["200", "400", "2000", "4000", "10000"], + FiatCurrency.inr: ["5000", "10000", "50000", "100000", "250000"], + FiatCurrency.isk: ["7000", "14000", "70000", "140000", "350000"], + FiatCurrency.jpy: ["10000", "20000", "100000", "200000", "500000"], + FiatCurrency.krw: ["50000", "100000", "500000", "1000000", "2500000"], + FiatCurrency.mad: ["500", "1000", "5000", "10000", "25000"], + FiatCurrency.mxn: ["1000", "2000", "10000", "20000", "50000"], + FiatCurrency.myr: ["250", "500", "2500", "5000", "12500"], + FiatCurrency.ngn: ["50000", "100000", "500000", "1000000", "2500000"], + FiatCurrency.nok: ["500", "1000", "5000", "10000", "25000"], + FiatCurrency.nzd: ["100", "200", "1000", "2000", "5000"], + FiatCurrency.php: ["3000", "6000", "30000", "60000", "150000"], + FiatCurrency.pkr: ["15000", "30000", "150000", "300000", "750000"], + FiatCurrency.pln: ["200", "400", "2000", "4000", "10000"], + FiatCurrency.ron: ["250", "500", "2500", "5000", "12500"], + FiatCurrency.sek: ["500", "1000", "5000", "10000", "25000"], + FiatCurrency.sgd: ["50", "100", "500", "1000", "2500"], + FiatCurrency.thb: ["2000", "4000", "20000", "40000", "100000"], + FiatCurrency.tur: ["1500", "3000", "15000", "30000", "75000"], + FiatCurrency.twd: ["1500", "3000", "15000", "30000", "75000"], + FiatCurrency.usd: ["50", "100", "500", "1000", "2500"], + FiatCurrency.vnd: ["1000000", "2000000", "10000000", "20000000", "50000000"], + FiatCurrency.zar: ["1000", "2000", "10000", "20000", "50000"], + FiatCurrency.kes: ["5000", "10000", "50000", "100000", "250000"], + }; + + // the fallback is just the usd values. + // not great but this fallback shouldn't be triggered anyway + List get defaultAmounts => + _defaultAmountsMap[fiatCurrency] ?? ["50", "100", "500", "1000", "2500"]; + @computed bool get isReadyToTrade { final hasSelectedQuote = selectedQuote != null; diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 8cf1c7835..207ee288c 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -199,6 +199,8 @@ "choose_a_provider": "Choose a provider", "choose_account": "Choose account", "choose_address": "\n\nPlease choose the address:", + "choose_amount_to_buy": "Choose amount to buy", + "choose_amount_to_sell": "Choose amount to sell", "choose_card_value": "Choose a card value", "choose_derivation": "Choose Wallet Derivation", "choose_from_available_options": "Choose from the available options:", From afbc6db06621dd8a0d67153f2d44bff4d7f628b5 Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Wed, 1 Jul 2026 16:49:19 +0200 Subject: [PATCH 04/58] wip --- lib/di.dart | 5 +- .../pages/buy_sell/buy_sell_amount_page.dart | 110 +++++++++++++++--- .../buy_sell/buy_sell_provider_page.dart | 45 +++++++ lib/new-ui/widgets/floating_amount_input.dart | 92 +++++++++++++++ lib/src/screens/buy/buy_sell_page.dart | 45 +------ lib/view_model/buy/buy_sell_view_model.dart | 33 +++--- 6 files changed, 255 insertions(+), 75 deletions(-) create mode 100644 lib/new-ui/pages/buy_sell/buy_sell_provider_page.dart create mode 100644 lib/new-ui/widgets/floating_amount_input.dart diff --git a/lib/di.dart b/lib/di.dart index a4ae25ca9..faf6196af 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -1494,10 +1494,11 @@ Future setup({ getIt.registerFactory(() => BuyAmountViewModel()); - getIt.registerFactory(() => BuySellViewModel(getIt.get())); + getIt.registerFactoryParam( + (mode, _) => BuySellViewModel(mode: mode, getIt.get())); getIt.registerFactoryParam((mode, _) => - NewBuySellAmountPage(mode: mode, buySellViewModel: getIt.get())); + NewBuySellAmountPage(buySellViewModel: getIt.get(param1: mode))); getIt.registerFactory(() => BuySellPage(getIt.get())); diff --git a/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart b/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart index 3b36c5834..327008938 100644 --- a/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart +++ b/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart @@ -1,16 +1,19 @@ import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart'; +import 'package:cake_wallet/new-ui/widgets/floating_amount_input.dart'; +import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart'; import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart'; import 'package:cake_wallet/themes/core/theme_extension.dart'; import 'package:cake_wallet/view_model/buy/buy_sell_view_model.dart'; import 'package:cw_core/amount/money.dart'; +import 'package:cw_core/crypto_currency.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; class NewBuySellAmountPage extends StatefulWidget { - const NewBuySellAmountPage({super.key, required this.mode, required this.buySellViewModel}); + const NewBuySellAmountPage({super.key, required this.buySellViewModel}); - final BuySellPageMode mode; final BuySellViewModel buySellViewModel; @override @@ -19,6 +22,7 @@ class NewBuySellAmountPage extends StatefulWidget { class _NewBuySellAmountPageState extends State { bool _customAmountMode = false; + final customInputController = TextEditingController(); @override Widget build(BuildContext context) { @@ -40,22 +44,37 @@ class _NewBuySellAmountPageState extends State { ModalTopBar( title: _pageTitle, leadingIcon: Icon(Icons.close), - onLeadingPressed: Navigator.of(context).pop, + onLeadingPressed: Navigator.of(context, rootNavigator: true).pop, ), Expanded( - child: BuySellDefaultAmountSelector( - defaultAmounts: widget.buySellViewModel.defaultAmounts, - currency: widget.buySellViewModel.fiatCurrency, - mode: widget.mode, - onSelected: (amount) { - if (amount == null) { - setState(() { - _customAmountMode = true; - }); - } else { - widget.buySellViewModel.changeFiatAmount(amount: amount); - } - }, + child: AnimatedSwitcher( + duration: Duration(milliseconds: 300), + child: _customAmountMode + ? Observer( + builder: (_) => BuySellCustomAmountInput( + fiatCurrency: widget.buySellViewModel.fiatCurrency, + cryptoCurrency: widget.buySellViewModel.cryptoCurrency, + cryptoAmount: widget.buySellViewModel.cryptoAmount, + controller: customInputController, + onContinuePressed: () {}, + onChanged: (amount) => + widget.buySellViewModel.changeFiatAmount(amount: amount), + )) + : BuySellDefaultAmountSelector( + key: ValueKey(0), + defaultAmounts: widget.buySellViewModel.defaultAmounts, + currency: widget.buySellViewModel.fiatCurrency, + mode: widget.buySellViewModel.mode, + onSelected: (amount) { + if (amount == null) { + setState(() { + _customAmountMode = true; + }); + } else { + widget.buySellViewModel.changeFiatAmount(amount: amount); + } + }, + ), )) ], ), @@ -63,7 +82,7 @@ class _NewBuySellAmountPageState extends State { ); } - String get _pageTitle => widget.mode == BuySellPageMode.buy + String get _pageTitle => widget.buySellViewModel.mode == BuySellPageMode.buy ? S.current.buy : S.current.sell + ((widget.buySellViewModel.cryptoCurrencies.length == 1) @@ -71,6 +90,62 @@ class _NewBuySellAmountPageState extends State { : ""); } +class BuySellCustomAmountInput extends StatelessWidget { + const BuySellCustomAmountInput( + {super.key, + required this.fiatCurrency, + required this.cryptoCurrency, + required this.cryptoAmount, + required this.controller, + required this.onContinuePressed, + required this.onChanged}); + + final FiatCurrency fiatCurrency; + final CryptoCurrency cryptoCurrency; + final String cryptoAmount; + final TextEditingController controller; + final VoidCallback onContinuePressed; + final Function(String) onChanged; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SizedBox.shrink(), + Column( + spacing: 8, + children: [ + FloatingAmountInput( + currency: fiatCurrency, + controller: controller, + onChanged: onChanged, + ), + Opacity( + opacity: cryptoAmount.isEmpty ? 0 : 1, + child: Text( + "≈ ${cryptoAmount} ${cryptoCurrency.symbol}", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: Theme.of(context).colorScheme.onSurfaceVariant), + ), + ) + ], + ), + Padding( + padding: const EdgeInsets.all(18.0), + child: NewPrimaryButton( + onPressed: onContinuePressed, + text: S.of(context).continue_text, + color: Theme.of(context).colorScheme.primary, + textColor: Theme.of(context).colorScheme.onPrimary), + ) + ], + ); + } +} + class BuySellDefaultAmountSelector extends StatelessWidget { const BuySellDefaultAmountSelector( {super.key, @@ -100,6 +175,7 @@ class BuySellDefaultAmountSelector extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 18.0), child: GridView.builder( shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), // +1 for "custom" option itemCount: defaultAmounts.length + 1, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( diff --git a/lib/new-ui/pages/buy_sell/buy_sell_provider_page.dart b/lib/new-ui/pages/buy_sell/buy_sell_provider_page.dart new file mode 100644 index 000000000..acbc7f71e --- /dev/null +++ b/lib/new-ui/pages/buy_sell/buy_sell_provider_page.dart @@ -0,0 +1,45 @@ +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart'; +import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart'; +import 'package:cake_wallet/view_model/buy/buy_sell_view_model.dart'; +import 'package:flutter/material.dart'; + +class BuySellProviderPage extends StatelessWidget { + const BuySellProviderPage({super.key, required this.buySellViewModel}); + + final BuySellViewModel buySellViewModel; + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.vertical(top: Radius.circular(18)), + gradient: LinearGradient( + colors: [ + Theme.of(context).colorScheme.surface, + Theme.of(context).colorScheme.surfaceDim, + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + child: SafeArea( + child: Column( + children: [ + ModalTopBar( + title: _pageTitle, + leadingIcon: Icon(Icons.arrow_back_ios_new), + onLeadingPressed: Navigator.of(context).pop, + ), + Expanded(child: Column( + + )) + ], + )), + ); + } + + String get _pageTitle => buySellViewModel.mode == BuySellPageMode.buy + ? S.current.buy + : S.current.sell + " " + (buySellViewModel.cryptoCurrency.fullName ?? ""); +} diff --git a/lib/new-ui/widgets/floating_amount_input.dart b/lib/new-ui/widgets/floating_amount_input.dart new file mode 100644 index 000000000..3367d0a3a --- /dev/null +++ b/lib/new-ui/widgets/floating_amount_input.dart @@ -0,0 +1,92 @@ +import 'package:cw_core/currency.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class FloatingAmountInput extends StatefulWidget { + const FloatingAmountInput({super.key, required this.currency, required this.controller, this.focusNode, this.inputFormatters, this.onChanged, this.validator}); + + final Currency currency; + final TextEditingController controller; + final FocusNode? focusNode; + final List? inputFormatters; + final Function(String)? onChanged; + final FormFieldValidator? validator; + + @override + State createState() => _FloatingAmountInputState(); +} + +class _FloatingAmountInputState extends State { + bool _amountFocused = false; + late FocusNode focusNode = widget.focusNode ?? FocusNode(); + + @override + void initState() { + super.initState(); + focusNode.addListener(() => setState(() => _amountFocused = focusNode.hasFocus)); + } + + @override + Widget build(BuildContext context) { + return Center( + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.baseline, + textBaseline: TextBaseline.alphabetic, + children: [ + IntrinsicWidth( + child: TextFormField( + controller: widget.controller, + focusNode: focusNode, + maxLines: 1, + onChanged: widget.onChanged, + autovalidateMode: AutovalidateMode.always, + validator: widget.validator, + keyboardType: TextInputType.numberWithOptions( + signed: false, + decimal: true, + ), + inputFormatters: [ + FilteringTextInputFormatter.allow( + RegExp(r'^\d*[.,]?\d*$'), + ), + ], + decoration: InputDecoration( + isDense: true, + isCollapsed: true, + contentPadding: EdgeInsets.zero, + fillColor: Colors.transparent, + hoverColor: Colors.transparent, + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, + hintText: _amountFocused || widget.controller.text.isNotEmpty + ? null + : "0.00", + hintStyle: Theme.of(context).textTheme.displayMedium?.copyWith( + fontWeight: FontWeight.w400, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + style: Theme.of(context).textTheme.displayMedium?.copyWith( + fontWeight: FontWeight.w400, + fontSize: 45, + color: Theme.of(context).colorScheme.onSurface, + ), + ), + ), + const SizedBox(width: 8), + Text( + widget.currency.symbol, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.displayMedium?.copyWith( + fontWeight: FontWeight.w400, + fontSize: 45, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + ], + ), + ); + } +} diff --git a/lib/src/screens/buy/buy_sell_page.dart b/lib/src/screens/buy/buy_sell_page.dart index 73c238b57..9c05309c6 100644 --- a/lib/src/screens/buy/buy_sell_page.dart +++ b/lib/src/screens/buy/buy_sell_page.dart @@ -3,6 +3,7 @@ import 'package:cake_wallet/core/address_validator.dart'; import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/entities/parse_address_from_domain.dart'; import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart'; import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_args.dart'; import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_sheet.dart'; import 'package:cake_wallet/new-ui/widgets/currency_picker/fiat_currency_picker_sheet.dart'; @@ -412,7 +413,7 @@ class BuySellPage extends BasePage { borderColor: Theme.of(context).colorScheme.outlineVariant, onPushPasteButton: (context) async {}, onPushAddressBookButton: (context) async {}, - fillColor: buySellViewModel.isBuyAction + fillColor: buySellViewModel.mode == BuySellPageMode.buy ? Theme.of(context).colorScheme.surfaceContainer : Theme.of(context).colorScheme.surfaceContainerLow, ), @@ -451,7 +452,7 @@ class BuySellPage extends BasePage { addressTextFieldValidator: AddressValidator(type: buySellViewModel.cryptoCurrency), onPushPasteButton: (context) async {}, onPushAddressBookButton: (context) async {}, - fillColor: buySellViewModel.isBuyAction + fillColor: buySellViewModel.mode == BuySellPageMode.buy ? Theme.of(context).colorScheme.surfaceContainerLow : Theme.of(context).colorScheme.surfaceContainer, useSatoshis: buySellViewModel.useSatoshi, @@ -461,50 +462,14 @@ class BuySellPage extends BasePage { if (responsiveLayoutUtil.shouldRenderMobileUI) { return Observer( builder: (_) { - if (buySellViewModel.isBuyAction) { - return MobileExchangeCardsSection( - firstExchangeCard: fiatExchangeCard, - secondExchangeCard: cryptoExchangeCard, - onBuyTap: () => null, - onSellTap: () => - buySellViewModel.isBuyAction ? buySellViewModel.changeBuySellAction() : null, - isBuySellOption: true, - ); - } else { - return MobileExchangeCardsSection( - firstExchangeCard: cryptoExchangeCard, - secondExchangeCard: fiatExchangeCard, - onBuyTap: () => - !buySellViewModel.isBuyAction ? buySellViewModel.changeBuySellAction() : null, - onSellTap: () => null, - isBuySellOption: true, - ); - } + return Placeholder(); }, ); } return Observer( builder: (_) { - if (buySellViewModel.isBuyAction) { - return DesktopExchangeCardsSection( - firstExchangeCard: fiatExchangeCard, - secondExchangeCard: cryptoExchangeCard, - onBuyTap: () => null, - onSellTap: () => - buySellViewModel.isBuyAction ? buySellViewModel.changeBuySellAction() : null, - isBuySellOption: true, - ); - } else { - return DesktopExchangeCardsSection( - firstExchangeCard: cryptoExchangeCard, - secondExchangeCard: fiatExchangeCard, - onBuyTap: () => - !buySellViewModel.isBuyAction ? buySellViewModel.changeBuySellAction() : null, - onSellTap: () => null, - isBuySellOption: true, - ); - } + return Placeholder(); }, ); } diff --git a/lib/view_model/buy/buy_sell_view_model.dart b/lib/view_model/buy/buy_sell_view_model.dart index 4d181d7f6..100f13329 100644 --- a/lib/view_model/buy/buy_sell_view_model.dart +++ b/lib/view_model/buy/buy_sell_view_model.dart @@ -11,10 +11,13 @@ import 'package:cake_wallet/core/wallet_change_listener_view_model.dart'; import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/entities/provider_types.dart'; import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart'; import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/src/screens/buy/buy_sell_page.dart'; import 'package:cake_wallet/store/app_store.dart'; import 'package:cw_core/crypto_amount_format.dart'; import 'package:cw_core/crypto_currency.dart'; +import 'package:cw_core/utils/print_verbose.dart'; import 'package:flutter/cupertino.dart'; import 'package:mobx/mobx.dart'; @@ -25,6 +28,7 @@ class BuySellViewModel = BuySellViewModelBase with _$BuySellViewModel; abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with Store { BuySellViewModelBase( AppStore appStore, + {required this.mode} ) : _cryptoAmount = '', fiatAmount = '', cryptoCurrencyAddress = '', @@ -81,7 +85,7 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S final formattedFiatAmount = double.tryParse(fiatAmount); final formattedCryptoAmount = double.tryParse(_cryptoAmount); - return isBuyAction + return mode == BuySellPageMode.buy ? formattedFiatAmount ?? 200.0 : formattedCryptoAmount ?? (cryptoCurrency == CryptoCurrency.btc ? 0.001 : 1); } @@ -100,8 +104,8 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S @observable List fiatCurrencies; - @observable - bool isBuyAction = true; + final BuySellPageMode mode; + @observable List providerList; @@ -234,11 +238,6 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S _initialize(); } - @action - void changeBuySellAction() { - isBuyAction = !isBuyAction; - _initialize(); - } @action void changeFiatCurrency({required FiatCurrency currency}) { @@ -267,16 +266,18 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S } if (!isReadyToTrade && !isBuySellQuoteFailed) { - _cryptoAmount = S.current.fetching; + _cryptoAmount = "..."; return; } else if (isBuySellQuoteFailed) { _cryptoAmount = ''; return; } + printV(bestRateQuote); if (bestRateQuote != null) { final enteredAmount = double.tryParse(fiatAmount.replaceAll(',', '.')) ?? 0; final amount = enteredAmount / bestRateQuote!.rate; + printV(amount); _cryptoAmount = amount.toString().withMaxDecimals(cryptoCurrency.decimals); } else { @@ -391,7 +392,7 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S } void _setProviders() => - providerList = isBuyAction ? availableBuyProviders : availableSellProviders; + providerList = mode == BuySellPageMode.buy ? availableBuyProviders : availableSellProviders; Future _initialize() async { _setProviders(); @@ -420,7 +421,7 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S paymentMethodState = PaymentMethodLoading(); selectedPaymentMethod = null; final result = await Future.wait(providerList.map((element) => - element.getAvailablePaymentTypes(fiatCurrency.title, cryptoCurrency, isBuyAction).timeout( + element.getAvailablePaymentTypes(fiatCurrency.title, cryptoCurrency, mode == BuySellPageMode.buy).timeout( Duration(seconds: 10), onTimeout: () => [], ))); @@ -456,7 +457,7 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S buySellQuotState = BuySellQuotLoading(); final List validProviders = providerList.where((provider) { - if (isBuyAction) { + if (mode == BuySellPageMode.buy) { return provider.supportedCryptoList .any((pair) => pair.from == cryptoCurrency && pair.to == fiatCurrency); } else { @@ -476,7 +477,7 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S fiatCurrency: fiatCurrency, amount: amount, paymentType: selectedPaymentMethod?.paymentMethodType, - isBuyAction: isBuyAction, + isBuyAction: mode == BuySellPageMode.buy, walletAddress: wallet.walletAddresses.address, customPaymentMethodType: selectedPaymentMethod?.customPaymentMethodType, ) @@ -498,7 +499,7 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S return; } - if (isBuyAction) { + if (mode == BuySellPageMode.buy) { validQuotes.sort((a, b) => b.payout.compareTo(a.payout)); } else { validQuotes.sort((a, b) => a.payout.compareTo(b.payout)); @@ -541,7 +542,7 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S final Quote effectiveBestRateQuote = sortedRecommendedQuotes.reduce((a, b) { - return isBuyAction ? a.rate < b.rate ? a : b : a.rate > b.rate ? a : b; + return mode == BuySellPageMode.buy ? a.rate < b.rate ? a : b : a.rate > b.rate ? a : b; }); @@ -563,7 +564,7 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S context: context, quote: selectedQuote!, amount: amount, - isBuyAction: isBuyAction, + isBuyAction: mode == BuySellPageMode.buy, cryptoCurrencyAddress: cryptoCurrencyAddress, ); } catch (e) { From 72c2899195f657580d049208ce4438e4fba01717 Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Fri, 3 Jul 2026 12:54:07 +0200 Subject: [PATCH 05/58] new buy/sell --- assets/images/skrill.svg | 4 +- lib/buy/buy_quote.dart | 5 +- lib/buy/payment_method.dart | 20 +- lib/di.dart | 2 +- .../list_item/list_item_regular_row.dart | 4 + .../pages/buy_sell/buy_sell_amount_page.dart | 303 ++++++++++++++---- .../buy_sell/buy_sell_confirmation_page.dart | 131 ++++++++ .../buy_sell_payment_method_page.dart | 71 ++++ .../buy_sell/buy_sell_provider_page.dart | 113 ++++++- .../buy_sell/buy_sell_redirecting_page.dart | 125 ++++++++ .../buy_sell/buy_sell_selector_modal.dart | 10 +- .../widgets/receive_page/receive_top_bar.dart | 25 +- lib/src/widgets/cake_image_widget.dart | 5 +- .../list_item_dropdown_widget.dart | 42 ++- .../list_item_regular_row_widget.dart | 22 +- .../new_list_row/new_list_section.dart | 2 + lib/view_model/buy/buy_sell_view_model.dart | 58 +++- pubspec_base.yaml | 1 + .../buy_payment_methods/all_methods.svg | 3 + .../buy_payment_methods/apple_pay.svg | 8 + .../buy_payment_methods/bank_transfer.svg | 3 + .../buy_payment_methods/debit_card.svg | 3 + .../buy_payment_methods/google_pay.svg | 11 + res/pictures/buy_payment_methods/paypal.svg | 68 ++++ res/values/strings_en.arb | 12 + 25 files changed, 926 insertions(+), 125 deletions(-) create mode 100644 lib/new-ui/pages/buy_sell/buy_sell_confirmation_page.dart create mode 100644 lib/new-ui/pages/buy_sell/buy_sell_payment_method_page.dart create mode 100644 lib/new-ui/pages/buy_sell/buy_sell_redirecting_page.dart create mode 100644 res/pictures/buy_payment_methods/all_methods.svg create mode 100644 res/pictures/buy_payment_methods/apple_pay.svg create mode 100644 res/pictures/buy_payment_methods/bank_transfer.svg create mode 100644 res/pictures/buy_payment_methods/debit_card.svg create mode 100644 res/pictures/buy_payment_methods/google_pay.svg create mode 100644 res/pictures/buy_payment_methods/paypal.svg diff --git a/assets/images/skrill.svg b/assets/images/skrill.svg index b264b57eb..542c00c12 100644 --- a/assets/images/skrill.svg +++ b/assets/images/skrill.svg @@ -1,9 +1,9 @@ - + - + diff --git a/lib/buy/buy_quote.dart b/lib/buy/buy_quote.dart index 1e154de6b..ee301e82f 100644 --- a/lib/buy/buy_quote.dart +++ b/lib/buy/buy_quote.dart @@ -5,6 +5,7 @@ import 'package:cake_wallet/entities/calculate_fiat_amount.dart'; import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/entities/provider_types.dart'; import 'package:cake_wallet/exchange/limits.dart'; +import 'package:cake_wallet/generated/i18n.dart'; import 'package:cw_core/crypto_currency.dart'; enum ProviderRecommendation { bestRate, lowKyc, successRate } @@ -13,11 +14,11 @@ extension RecommendationTitle on ProviderRecommendation { String get title { switch (this) { case ProviderRecommendation.bestRate: - return 'BEST RATE'; + return S.current.best_rate; case ProviderRecommendation.lowKyc: return 'LOW KYC'; case ProviderRecommendation.successRate: - return 'HIGHEST SUCCESS RATE'; + return S.current.highest_success_rate; } } } diff --git a/lib/buy/payment_method.dart b/lib/buy/payment_method.dart index 06f1cfe34..b6b23093d 100644 --- a/lib/buy/payment_method.dart +++ b/lib/buy/payment_method.dart @@ -132,17 +132,21 @@ extension PaymentTypeTitle on PaymentType { String? get darkIconPath { switch (this) { case PaymentType.all: - return 'assets/images/usd_round_dark.svg'; + return 'assets/new-ui/buy_payment_methods/all_methods.svg'; case PaymentType.creditCard: case PaymentType.debitCard: case PaymentType.yellowCardBankTransfer: - return 'assets/images/card_dark.svg'; + return 'assets/new-ui/buy_payment_methods/debit_card.svg'; case PaymentType.bankTransfer: - return 'assets/images/bank_dark.svg'; + return 'assets/new-ui/buy_payment_methods/bank_transfer.svg'; case PaymentType.skrill: return 'assets/images/skrill.svg'; case PaymentType.applePay: - return 'assets/images/apple_pay_round_dark.svg'; + return 'assets/new-ui/buy_payment_methods/apple_pay.svg'; + case PaymentType.googlePay: + return "assets/new-ui/buy_payment_methods/google_pay.svg"; + case PaymentType.paypal: + return "assets/new-ui/buy_payment_methods/paypal.svg"; case PaymentType.revolutPay: return 'assets/images/revolut_dark.svg'; default: @@ -150,6 +154,12 @@ extension PaymentTypeTitle on PaymentType { } } + bool get isMonochromeIcon => [ + "assets/new-ui/buy_payment_methods/all_methods.svg", + "assets/new-ui/buy_payment_methods/debit_card.svg", + "assets/new-ui/buy_payment_methods/bank_transfer.svg" + ].contains(darkIconPath); + String? get description { switch (this) { default: @@ -190,7 +200,7 @@ class PaymentMethod extends SelectableOption { return PaymentMethod( paymentMethodType: PaymentType.all, customTitle: 'All Payment Methods', - customIconPath: 'assets/images/dollar_coin.svg'); + customIconPath: 'assets/new-ui/buy_payment_methods/all_methods.svg'); } factory PaymentMethod.fromOnramperJson(Map json) { diff --git a/lib/di.dart b/lib/di.dart index faf6196af..4439a68bd 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -1495,7 +1495,7 @@ Future setup({ getIt.registerFactory(() => BuyAmountViewModel()); getIt.registerFactoryParam( - (mode, _) => BuySellViewModel(mode: mode, getIt.get())); + (mode, _) => BuySellViewModel(mode: mode, getIt.get(), fiatConversionStore: getIt.get())); getIt.registerFactoryParam((mode, _) => NewBuySellAmountPage(buySellViewModel: getIt.get(param1: mode))); diff --git a/lib/entities/new_ui_entities/list_item/list_item_regular_row.dart b/lib/entities/new_ui_entities/list_item/list_item_regular_row.dart index 4660199ff..bfa2571d8 100644 --- a/lib/entities/new_ui_entities/list_item/list_item_regular_row.dart +++ b/lib/entities/new_ui_entities/list_item/list_item_regular_row.dart @@ -22,6 +22,8 @@ class ListItemRegularRow extends ListItem { this.leadingIconSize, this.badgeIconSize, this.iconColor, + this.secondaryLabel, + this.subtitleColor, }); final String? subtitle; @@ -31,10 +33,12 @@ class ListItemRegularRow extends ListItem { final String? badgeIconPath; final String? copyableText; final VoidCallback? onTap; + final String? secondaryLabel; final bool showArrow; final Widget? bottomWidget; final Widget? trailingWidget; final bool truncateTrailingText; + final Color? subtitleColor; final Color? foregroundColor; final double? trailingIconSize; final Widget? leadingIconErrorWidget; diff --git a/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart b/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart index 327008938..ec22421b1 100644 --- a/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart +++ b/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart @@ -1,15 +1,27 @@ +import 'dart:async'; + +import 'package:cake_wallet/buy/payment_method.dart'; +import 'package:cake_wallet/buy/sell_buy_states.dart'; import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/new-ui/pages/buy_sell/buy_sell_provider_page.dart'; import 'package:cake_wallet/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart'; +import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_args.dart'; +import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_sheet.dart'; import 'package:cake_wallet/new-ui/widgets/floating_amount_input.dart'; import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart'; import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart'; +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; +import 'package:cake_wallet/src/widgets/cake_image_widget.dart'; import 'package:cake_wallet/themes/core/theme_extension.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/buy/buy_sell_view_model.dart'; import 'package:cw_core/amount/money.dart'; import 'package:cw_core/crypto_currency.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:mobx/mobx.dart'; class NewBuySellAmountPage extends StatefulWidget { const NewBuySellAmountPage({super.key, required this.buySellViewModel}); @@ -22,7 +34,24 @@ class NewBuySellAmountPage extends StatefulWidget { class _NewBuySellAmountPageState extends State { bool _customAmountMode = false; + bool _isLoadingPaymentMethods = false; final customInputController = TextEditingController(); + final customInputFocusNode = FocusNode(); + + @override + void initState() { + super.initState(); + + // this is a hack for the "up to" display to show the actual upper limit. + // limits are loaded alongside quotes, and quotes for some providers only load properly with an amount and payment method selected. + // it'll be refactored soon anyway, i guess. + widget.buySellViewModel.changeFiatAmount(amount: "1000"); + widget.buySellViewModel.calculateBestRate(); + when( + (_) => widget.buySellViewModel.paymentMethodState is PaymentMethodLoaded, + () => widget.buySellViewModel.selectedPaymentMethod = widget.buySellViewModel.paymentMethods + .firstWhere((item) => item.paymentMethodType == PaymentType.all)); + } @override Widget build(BuildContext context) { @@ -41,40 +70,61 @@ class _NewBuySellAmountPageState extends State { child: SafeArea( child: Column( children: [ - ModalTopBar( - title: _pageTitle, - leadingIcon: Icon(Icons.close), - onLeadingPressed: Navigator.of(context, rootNavigator: true).pop, + Observer( + builder: (_) => ModalTopBar( + title: _pageTitle, + bottomText: !_customAmountMode || widget.buySellViewModel.maxFiatAmount == null + ? null + : "${S.of(context).up_to} ~${widget.buySellViewModel.maxFiatAmount} ${widget.buySellViewModel.fiatCurrency.title}", + leadingIcon: Icon(Icons.close), + onLeadingPressed: Navigator.of(context, rootNavigator: true).pop, + ), ), Expanded( - child: AnimatedSwitcher( - duration: Duration(milliseconds: 300), - child: _customAmountMode - ? Observer( - builder: (_) => BuySellCustomAmountInput( - fiatCurrency: widget.buySellViewModel.fiatCurrency, - cryptoCurrency: widget.buySellViewModel.cryptoCurrency, - cryptoAmount: widget.buySellViewModel.cryptoAmount, - controller: customInputController, - onContinuePressed: () {}, - onChanged: (amount) => - widget.buySellViewModel.changeFiatAmount(amount: amount), - )) - : BuySellDefaultAmountSelector( - key: ValueKey(0), - defaultAmounts: widget.buySellViewModel.defaultAmounts, - currency: widget.buySellViewModel.fiatCurrency, - mode: widget.buySellViewModel.mode, - onSelected: (amount) { - if (amount == null) { - setState(() { - _customAmountMode = true; - }); - } else { - widget.buySellViewModel.changeFiatAmount(amount: amount); - } - }, - ), + child: Observer( + builder: (_) => AnimatedSwitcher( + duration: Duration(milliseconds: 300), + child: _customAmountMode + ? BuySellCustomAmountInput( + fiatCurrency: widget.buySellViewModel.fiatCurrency, + cryptoCurrency: widget.buySellViewModel.cryptoCurrency, + cryptoAmount: widget.buySellViewModel.cryptoAmount, + isLoading: _isLoadingPaymentMethods, + hasCurrencySelector: widget.buySellViewModel.hasMultipleCurrencies, + onCurrencySelectorPressed: () => selectCryptoCurrency(context), + controller: customInputController, + focusNode: customInputFocusNode, + onContinuePressed: () { + navigateToProviders(context); + }, + onChanged: (amount) => + widget.buySellViewModel.changeFiatAmount(amount: amount), + ) + : BuySellDefaultAmountSelector( + key: ValueKey(0), + defaultAmounts: widget.buySellViewModel.defaultAmounts, + fiatCurrency: widget.buySellViewModel.fiatCurrency, + currentAmount: widget.buySellViewModel.fiatAmount, + hasCurrencySelector: widget.buySellViewModel.hasMultipleCurrencies, + onCurrencySelectorPressed: () => selectCryptoCurrency(context), + cryptoCurrency: widget.buySellViewModel.cryptoCurrency, + isLoading: _isLoadingPaymentMethods, + mode: widget.buySellViewModel.mode, + onSelected: (amount) async { + if (amount == null) { + // this resets the rate and prevents showing 0 usd = 0.something btc + await widget.buySellViewModel.changeFiatAmount(amount: ""); + setState(() { + _customAmountMode = true; + }); + customInputFocusNode.requestFocus(); + } else { + await widget.buySellViewModel.changeFiatAmount(amount: amount); + navigateToProviders(context); + } + }, + ), + ), )) ], ), @@ -82,12 +132,60 @@ class _NewBuySellAmountPageState extends State { ); } + void selectCryptoCurrency(BuildContext context) => CurrencyPickerSheet.show( + context: context, + args: CurrencyPickerArgs( + items: widget.buySellViewModel.activeWalletCurrencies.toList(), + onSelected: (item) => widget.buySellViewModel.changeCryptoCurrency(currency: item), + symbolResolver: widget.buySellViewModel.amountParsingProxy.getCryptoSymbol, + )); + String get _pageTitle => widget.buySellViewModel.mode == BuySellPageMode.buy ? S.current.buy : S.current.sell + ((widget.buySellViewModel.cryptoCurrencies.length == 1) ? " ${widget.buySellViewModel.cryptoCurrencies.first.fullName}" : ""); + + Future navigateToProviders(BuildContext context) async { + if (_isLoadingPaymentMethods) { + return; + } + + try { + setState(() { + _isLoadingPaymentMethods = true; + }); + + await asyncWhen((_) => [PaymentMethodLoaded, PaymentMethodFailed] + .contains(widget.buySellViewModel.paymentMethodState.runtimeType)); + + if (widget.buySellViewModel.paymentMethodState is PaymentMethodFailed) { + showPopUp( + context: context, + builder: (context) => AlertWithOneAction( + alertTitle: S.of(context).failed_to_load_payment_methods, + alertContent: S.of(context).please_try_again_later, + buttonText: "OK", + buttonAction: Navigator.of(context).pop)); + return; + } + + widget.buySellViewModel.selectedPaymentMethod = widget.buySellViewModel.paymentMethods + .firstWhere((item) => item.paymentMethodType == PaymentType.all); + + // unawaited because BuySellProviderPage has a nice little "loading rates..." ui thingie + unawaited(widget.buySellViewModel.calculateBestRate()); + + final page = BuySellProviderPage(buySellViewModel: widget.buySellViewModel); + Navigator.of(context).push(CupertinoPageRoute( + builder: (context) => Material(color: Colors.transparent, child: page))); + } finally { + setState(() { + _isLoadingPaymentMethods = false; + }); + } + } } class BuySellCustomAmountInput extends StatelessWidget { @@ -98,12 +196,20 @@ class BuySellCustomAmountInput extends StatelessWidget { required this.cryptoAmount, required this.controller, required this.onContinuePressed, - required this.onChanged}); + required this.isLoading, + required this.onChanged, + required this.focusNode, + required this.hasCurrencySelector, + required this.onCurrencySelectorPressed}); final FiatCurrency fiatCurrency; final CryptoCurrency cryptoCurrency; final String cryptoAmount; final TextEditingController controller; + final FocusNode focusNode; + final bool isLoading; + final bool hasCurrencySelector; + final VoidCallback onCurrencySelectorPressed; final VoidCallback onContinuePressed; final Function(String) onChanged; @@ -116,8 +222,11 @@ class BuySellCustomAmountInput extends StatelessWidget { Column( spacing: 8, children: [ + BuySellCurrencyPickerPill(curr: cryptoCurrency, onTap: onCurrencySelectorPressed), + SizedBox.shrink(), FloatingAmountInput( currency: fiatCurrency, + focusNode: focusNode, controller: controller, onChanged: onChanged, ), @@ -137,6 +246,7 @@ class BuySellCustomAmountInput extends StatelessWidget { padding: const EdgeInsets.all(18.0), child: NewPrimaryButton( onPressed: onContinuePressed, + isLoading: isLoading, text: S.of(context).continue_text, color: Theme.of(context).colorScheme.primary, textColor: Theme.of(context).colorScheme.onPrimary), @@ -150,12 +260,22 @@ class BuySellDefaultAmountSelector extends StatelessWidget { const BuySellDefaultAmountSelector( {super.key, required this.defaultAmounts, - required this.currency, + required this.fiatCurrency, required this.mode, - required this.onSelected}); + required this.onSelected, + this.currentAmount, + required this.isLoading, + required this.hasCurrencySelector, + required this.onCurrencySelectorPressed, + required this.cryptoCurrency}); final List defaultAmounts; - final FiatCurrency currency; + final String? currentAmount; + final bool isLoading; + final bool hasCurrencySelector; + final VoidCallback onCurrencySelectorPressed; + final CryptoCurrency cryptoCurrency; + final FiatCurrency fiatCurrency; final BuySellPageMode mode; final Function(String?) onSelected; @@ -165,6 +285,7 @@ class BuySellDefaultAmountSelector extends StatelessWidget { spacing: 24, mainAxisAlignment: MainAxisAlignment.center, children: [ + BuySellCurrencyPickerPill(curr: cryptoCurrency, onTap: onCurrencySelectorPressed), Text( mode == BuySellPageMode.sell ? S.of(context).choose_amount_to_sell @@ -184,7 +305,8 @@ class BuySellDefaultAmountSelector extends StatelessWidget { final String? item = index == defaultAmounts.length ? null : defaultAmounts[index]; return BuySellAmountPill( - amount: item == null ? null : Money.parse(item, currency), + isLoading: isLoading && item == currentAmount, + amount: item == null ? null : Money.parse(item, fiatCurrency), onTap: () => onSelected(item), ); }), @@ -195,10 +317,11 @@ class BuySellDefaultAmountSelector extends StatelessWidget { } class BuySellAmountPill extends StatelessWidget { - const BuySellAmountPill({super.key, this.amount, required this.onTap}); + const BuySellAmountPill({super.key, this.amount, required this.onTap, required this.isLoading}); final Money? amount; final VoidCallback onTap; + final bool isLoading; @override Widget build(BuildContext context) { @@ -224,35 +347,85 @@ class BuySellAmountPill extends StatelessWidget { child: InkWell( borderRadius: BorderRadius.circular(9999999999), onTap: onTap, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Row( - spacing: 4, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (amount != null) - Text( - amount!.toStringWithPrecision(fractionalDigits: 0), - style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600), + child: isLoading + ? CupertinoActivityIndicator() + : Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + spacing: 4, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (amount != null) + Text( + amount!.toStringWithPrecision(fractionalDigits: 0), + style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600), + ), + Text( + amount?.currency.symbol ?? S.of(context).custom, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: amount == null + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.onSurfaceVariant), + ) + ], ), - Text( - amount?.currency.symbol ?? S.of(context).custom, - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w600, - color: amount == null - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.onSurfaceVariant), - ) - ], + if (amount == null) + Text( + S.of(context).enter_amount, + style: TextStyle( + fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant), + ) + ], + ), + ), + ), + ); + } +} + +class BuySellCurrencyPickerPill extends StatelessWidget { + const BuySellCurrencyPickerPill({super.key, required this.curr, required this.onTap}); + + final CryptoCurrency curr; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surfaceContainerHigh, + borderRadius: BorderRadius.circular(999999999), + ), + child: Padding( + padding: EdgeInsets.only(top: 5, bottom: 5, left: 5, right: 10), + child: Row( + mainAxisSize: MainAxisSize.min, + spacing: 10, + children: [ + CakeImageWidget( + imageUrl: curr.iconPath, + width: 30, + height: 30, + ), + Text( + curr.fullName ?? curr.title, + style: TextStyle(fontSize: 16), + ), + RotatedBox( + quarterTurns: 2, + child: CakeImageWidget( + imageUrl: "assets/new-ui/dropdown_arrow.svg", + width: 8, + height: 8, + colorFilter: + ColorFilter.mode(Theme.of(context).colorScheme.primary, BlendMode.srcIn), + ), ), - if (amount == null) - Text( - S.of(context).enter_amount, - style: TextStyle( - fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant), - ) ], ), ), diff --git a/lib/new-ui/pages/buy_sell/buy_sell_confirmation_page.dart b/lib/new-ui/pages/buy_sell/buy_sell_confirmation_page.dart new file mode 100644 index 000000000..5de696f7b --- /dev/null +++ b/lib/new-ui/pages/buy_sell/buy_sell_confirmation_page.dart @@ -0,0 +1,131 @@ +import 'package:cake_wallet/buy/payment_method.dart'; +import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/new-ui/pages/buy_sell/buy_sell_redirecting_page.dart'; +import 'package:cake_wallet/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart'; +import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart'; +import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart'; +import 'package:cake_wallet/src/widgets/cake_image_widget.dart'; +import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart'; +import 'package:cake_wallet/view_model/buy/buy_sell_view_model.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; + +class BuySellConfirmationPage extends StatelessWidget { + const BuySellConfirmationPage({super.key, required this.buySellViewModel}); + + final BuySellViewModel buySellViewModel; + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.vertical(top: Radius.circular(18)), + gradient: LinearGradient( + colors: [ + Theme.of(context).colorScheme.surface, + Theme.of(context).colorScheme.surfaceDim, + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + child: SafeArea( + child: Column( + children: [ + ModalTopBar( + title: _pageTitle, + leadingIcon: Icon(Icons.arrow_back_ios_new), + onLeadingPressed: Navigator.of(context).pop, + ), + Expanded(child: Observer( + builder: (_) { + return Column( + spacing: 24, + children: [ + Column( + spacing: 4, + children: [ + Text( + "${buySellViewModel.fiatAmount} ${buySellViewModel.fiatCurrency}", + style: TextStyle(fontSize: 32), + ), + Text( + "≈ ${buySellViewModel.amountForQuote(buySellViewModel.selectedQuote!).toStringWithSymbol(fractionalDigits: 8)}", + style: TextStyle( + color: Theme.of(context).colorScheme.onSurfaceVariant, + fontWeight: FontWeight.w500), + ) + ], + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 18.0), + child: NewListSections(sections: { + "": [ + ListItemRegularRow( + keyValue: "provider", + label: S.of(context).provider, + trailingWidget: Row( + spacing: 8, + children: [ + CakeImageWidget( + imageUrl: buySellViewModel.selectedQuote!.darkIconPath, + width: 24, + height: 24, + ), + Text( + buySellViewModel.selectedQuote!.rampName ?? + buySellViewModel.selectedQuote!.provider.title, + style: TextStyle( + color: Theme.of(context).colorScheme.onSurfaceVariant, + fontWeight: FontWeight.w500), + ), + SizedBox.shrink() + ], + )), + ListItemRegularRow( + showArrow: false, + keyValue: "payment method", + label: S.of(context).payment_method, + trailingText: buySellViewModel.selectedQuote!.paymentType.title), + ListItemRegularRow( + showArrow: false, + keyValue: "rate", + label: S.of(context).rate, + trailingText: buySellViewModel.selectedQuote!.topLeftSubTitle) + ] + }), + ) + ], + ); + }, + )), + Padding( + padding: EdgeInsets.all(18), + child: NewPrimaryButton( + onPressed: () => confirm(context), + text: S.of(context).proceed, + color: Theme.of(context).colorScheme.primary, + textColor: Theme.of(context).colorScheme.onPrimary), + ) + ], + ), + ), + ); + } + + String get _pageTitle => + (buySellViewModel.mode == BuySellPageMode.buy ? S.current.buy : S.current.sell) + + " " + + (buySellViewModel.cryptoCurrency.fullName ?? ""); + + void confirm(BuildContext context) { + final page = BuySellRedirectingPage(buySellViewModel: buySellViewModel); + Navigator.of(context, rootNavigator: true).pop(); + Navigator.of(context, rootNavigator: true).push(CupertinoPageRoute( + builder: (context) => Material( + child: page, + ))); + } +} diff --git a/lib/new-ui/pages/buy_sell/buy_sell_payment_method_page.dart b/lib/new-ui/pages/buy_sell/buy_sell_payment_method_page.dart new file mode 100644 index 000000000..e1ca3f449 --- /dev/null +++ b/lib/new-ui/pages/buy_sell/buy_sell_payment_method_page.dart @@ -0,0 +1,71 @@ +import 'package:cake_wallet/buy/payment_method.dart'; +import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart'; +import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart'; +import 'package:cake_wallet/view_model/buy/buy_sell_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; + +class BuySellPaymentMethodPage extends StatelessWidget { + const BuySellPaymentMethodPage({super.key, required this.buySellViewModel}); + + final BuySellViewModel buySellViewModel; + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.vertical(top: Radius.circular(18)), + gradient: LinearGradient( + colors: [ + Theme.of(context).colorScheme.surface, + Theme.of(context).colorScheme.surfaceDim, + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + child: SafeArea( + child: Column( + children: [ + ModalTopBar( + title: S.of(context).payment_method, + leadingIcon: Icon(Icons.arrow_back_ios_new), + onLeadingPressed: Navigator.of(context).pop, + ), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 18.0), + child: Observer( + builder: (_) => NewListSections(sections: { + "": buySellViewModel.paymentMethods + .map((item) => ListItemRegularRow( + keyValue: item.title, + label: item.title, + showArrow: false, + iconPath: item.darkIconPath, + iconColor: item.paymentMethodType.isMonochromeIcon + ? Theme.of(context).colorScheme.onSurfaceVariant + : null, + trailingWidget: buySellViewModel.selectedPaymentMethod == item + ? Icon( + Icons.check, + color: Theme.of(context).colorScheme.primary, + size: 16, + ) + : null, + onTap: () async { + buySellViewModel.changeOption(item); + buySellViewModel.calculateBestRate(); + Navigator.of(context).pop(); + })) + .toList() + }), + ), + )) + ], + )), + ); + } +} diff --git a/lib/new-ui/pages/buy_sell/buy_sell_provider_page.dart b/lib/new-ui/pages/buy_sell/buy_sell_provider_page.dart index acbc7f71e..f96545951 100644 --- a/lib/new-ui/pages/buy_sell/buy_sell_provider_page.dart +++ b/lib/new-ui/pages/buy_sell/buy_sell_provider_page.dart @@ -1,14 +1,31 @@ +import 'package:cake_wallet/buy/buy_quote.dart'; +import 'package:cake_wallet/buy/sell_buy_states.dart'; +import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item.dart'; +import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_dropdown.dart'; +import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart'; import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/new-ui/pages/buy_sell/buy_sell_confirmation_page.dart'; +import 'package:cake_wallet/new-ui/pages/buy_sell/buy_sell_payment_method_page.dart'; import 'package:cake_wallet/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart'; import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart'; +import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart'; import 'package:cake_wallet/view_model/buy/buy_sell_view_model.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; -class BuySellProviderPage extends StatelessWidget { +class BuySellProviderPage extends StatefulWidget { const BuySellProviderPage({super.key, required this.buySellViewModel}); final BuySellViewModel buySellViewModel; + @override + State createState() => _BuySellProviderPageState(); +} + +class _BuySellProviderPageState extends State { + bool _allProvidersExpanded = false; + @override Widget build(BuildContext context) { return Container( @@ -31,15 +48,103 @@ class BuySellProviderPage extends StatelessWidget { leadingIcon: Icon(Icons.arrow_back_ios_new), onLeadingPressed: Navigator.of(context).pop, ), - Expanded(child: Column( + Expanded( + child: Observer( + builder: (_) { + if(widget.buySellViewModel.buySellQuotState is BuySellQuotFailed) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + spacing: 24, + children: [ + Icon(Icons.warning_amber_outlined, size: 48), + Column( + spacing: 10, + children: [ + Text( + S.of(context).could_not_load_quotes, + style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500), + ), + Text((widget.buySellViewModel.buySellQuotState as BuySellQuotFailed).errorMessage ?? + S.of(context).please_try_again_later) + ], + ) + ], + ); + } + if(widget.buySellViewModel.buySellQuotState is BuySellQuotLoading) { + return Center(child: Row(mainAxisAlignment:MainAxisAlignment.center, spacing:8, children: [ + CupertinoActivityIndicator(), + Text(S.of(context).loading_rates, style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),) + ],),); + } + + return Padding( + padding: EdgeInsets.symmetric(horizontal: 18), + child: NewListSections(showHeader: true, sections: { + "": [ + ListItemRegularRow( + keyValue: "payment method", + label: S.of(context).payment_method, + showArrow: true, + onTap: (){ + + final page =BuySellPaymentMethodPage(buySellViewModel: widget.buySellViewModel); + Navigator.of(context).push(CupertinoPageRoute(builder: (context)=>Material(color: Colors.transparent,child: page,))); + }, + trailingText: widget.buySellViewModel.selectedPaymentMethod?.title) + ], + S.of(context).available_providers: [ + ...widget.buySellViewModel.sortedRecommendedQuotes.map(quoteListItem), + ListItemDropdown( + keyValue: "more options", + label: S.of(context).more_options, + onTap: () { + setState(() { + _allProvidersExpanded = !_allProvidersExpanded; + }); + }), + if (_allProvidersExpanded) + ...widget.buySellViewModel.sortedQuotes.map(quoteListItem) + ] + }), + ); + }, )) ], )), ); } - String get _pageTitle => buySellViewModel.mode == BuySellPageMode.buy + String get _pageTitle => (widget.buySellViewModel.mode == BuySellPageMode.buy ? S.current.buy - : S.current.sell + " " + (buySellViewModel.cryptoCurrency.fullName ?? ""); + : S.current.sell) + " " + (widget.buySellViewModel.cryptoCurrency.fullName ?? ""); + + ListItem quoteListItem(Quote quote) => ListItemRegularRow( + keyValue: quote.provider.title, + label: quote.rampName ?? quote.provider.title, + secondaryLabel: quote.rampName != null? quote.provider.title : null, + subtitle: quote.badges.isEmpty ? null : quote.badges.join(" - "), + subtitleColor: Theme.of(context).colorScheme.primary, + iconPath: quote.darkIconPath, + onTap: (){ + widget.buySellViewModel.changeOption(quote); + navigateToConfirmation(context); + }, + leadingIconSize: 32, + trailingWidget: Column( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.center, + spacing: 4, + children: [ + Text(widget.buySellViewModel.amountForQuote(quote).toStringWithSymbol(fractionalDigits: 8)), + Text("= ${widget.buySellViewModel.fiatAmountForQuote(quote).toStringWithSymbol(fractionalDigits: 2, trimZeros: false)}", style: TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant)) + + ],)); + + void navigateToConfirmation(BuildContext context) { + + final page = BuySellConfirmationPage(buySellViewModel: widget.buySellViewModel); + Navigator.of(context).push(CupertinoPageRoute(builder: (context)=>Material(color: Colors.transparent,child: page,))); + } } diff --git a/lib/new-ui/pages/buy_sell/buy_sell_redirecting_page.dart b/lib/new-ui/pages/buy_sell/buy_sell_redirecting_page.dart new file mode 100644 index 000000000..f96531b82 --- /dev/null +++ b/lib/new-ui/pages/buy_sell/buy_sell_redirecting_page.dart @@ -0,0 +1,125 @@ +import 'package:cake_wallet/buy/sell_buy_states.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart'; +import 'package:cake_wallet/src/widgets/cake_image_widget.dart'; +import 'package:cake_wallet/view_model/buy/buy_sell_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; + +class BuySellRedirectingPage extends StatefulWidget { + const BuySellRedirectingPage({super.key, required this.buySellViewModel}); + + final BuySellViewModel buySellViewModel; + + @override + State createState() => _BuySellRedirectingPageState(); +} + +class _BuySellRedirectingPageState extends State { + bool _hasRedirected = false; + + @override + void initState() { + super.initState(); + Future.delayed(Duration(seconds: 2)).then((_) async { + WidgetsBinding.instance + .addPostFrameCallback((_) => widget.buySellViewModel.launchTrade(context)); + setState(() { + _hasRedirected = true; + }); + }); + } + + @override + Widget build(BuildContext context) { + return PopScope( + canPop: false, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.vertical(top: Radius.circular(18)), + gradient: LinearGradient( + colors: [ + Theme.of(context).colorScheme.surface, + Theme.of(context).colorScheme.surfaceDim, + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + child: Observer( + builder: (_) { + final showExitButton = + _hasRedirected || widget.buySellViewModel.buySellQuotState is BuySellQuotFailed; + + return SafeArea( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SizedBox.shrink(), + Observer( + builder: (_) { + if (widget.buySellViewModel.buySellQuotState is BuySellQuotFailed) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + spacing: 24, + children: [ + Icon(Icons.warning_amber_outlined, size: 48), + Column( + spacing: 10, + children: [ + Text( + S.of(context).could_not_proceed_with_trade, + style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500), + ), + Text((widget.buySellViewModel.buySellQuotState as BuySellQuotFailed) + .errorMessage ?? + S.of(context).please_try_again_later) + ], + ) + ], + ); + } + return Column( + mainAxisAlignment: MainAxisAlignment.center, + spacing: 24, + children: [ + CakeImageWidget( + imageUrl: widget.buySellViewModel.selectedQuote!.darkIconPath, + width: 64, + height: 64, + ), + Column( + spacing: 10, + children: [ + Text( + "${S.of(context).connecting_you_to} ${widget.buySellViewModel.selectedQuote!.provider.title}...", + style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500), + ), + Text( + "${widget.buySellViewModel.fiatAmount} ${widget.buySellViewModel.fiatCurrency} → ${widget.buySellViewModel.amountForQuote(widget.buySellViewModel.selectedQuote!).toStringWithSymbol(fractionalDigits: 8)}") + ], + ) + ], + ); + }, + ), + showExitButton + ? Padding( + padding: EdgeInsets.all(18), + child: NewPrimaryButton( + onPressed: Navigator.of(context).pop, + text: S.of(context).close, + color: Theme.of(context).colorScheme.primary, + textColor: Theme.of(context).colorScheme.onPrimary, + ), + ) + : SizedBox.shrink() + ], + ), + ); + }, + ), + ), + ); + } +} diff --git a/lib/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart b/lib/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart index 7e6efd004..6d5343ccc 100644 --- a/lib/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart +++ b/lib/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart @@ -46,7 +46,15 @@ class BuySellSelectorModal extends StatelessWidget { void openBuySellPage(BuildContext context, BuySellPageMode mode) { Navigator.of(context).pop(); - showModalBottomSheet(useSafeArea:true, isScrollControlled: true, context: context, builder: (modalContext)=>ModalNavigator(rootPage: getIt.get(param1: mode), parentContext: context,)); + final page = getIt.get(param1: mode); + showModalBottomSheet( + useSafeArea: true, + isScrollControlled: true, + context: context, + builder: (modalContext) => ModalNavigator( + rootPage: page, + parentContext: context, + )); } } diff --git a/lib/new-ui/widgets/receive_page/receive_top_bar.dart b/lib/new-ui/widgets/receive_page/receive_top_bar.dart index b41eb9c20..d345156dd 100644 --- a/lib/new-ui/widgets/receive_page/receive_top_bar.dart +++ b/lib/new-ui/widgets/receive_page/receive_top_bar.dart @@ -12,6 +12,7 @@ class ModalTopBar extends StatelessWidget { this.onTrailingPressed=nothing, this.leadingIcon, this.trailingIcon, + this.bottomText, this.padding, this.leadingWidget, this.trailingWidget}) { @@ -25,6 +26,7 @@ class ModalTopBar extends StatelessWidget { final String title; final String? subtitle; + final String? bottomText; final VoidCallback onLeadingPressed; final VoidCallback onTrailingPressed; final Widget? leadingIcon; @@ -37,25 +39,32 @@ class ModalTopBar extends StatelessWidget { @override Widget build(BuildContext context) { + final hasBottomText =bottomText != null && bottomText!.isNotEmpty; return Padding( padding: padding??EdgeInsets.all(18), child: Stack( alignment: Alignment.topCenter, children: [ Positioned( - top: 6, + top: hasBottomText ? -4 : 6, child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, spacing: 4, children: [ - AnimatedSwitcher( - duration: const Duration(milliseconds: 200), - child: Text( - title, - key: ValueKey(title), - style: Theme.of(context).textTheme.headlineMedium, - ), + Column( + children: [ + AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + child: Text( + title, + key: ValueKey(title), + style: TextStyle(fontSize: hasBottomText ? 16 : 18, fontWeight: FontWeight.w600), + ), + ), + if(hasBottomText) + Text(bottomText!, style: TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant),) + ], ), if (subtitle != null && subtitle!.isNotEmpty) Text( diff --git a/lib/src/widgets/cake_image_widget.dart b/lib/src/widgets/cake_image_widget.dart index f714bcd0c..5b865324c 100644 --- a/lib/src/widgets/cake_image_widget.dart +++ b/lib/src/widgets/cake_image_widget.dart @@ -1,3 +1,4 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:vector_graphics/vector_graphics.dart'; @@ -86,7 +87,7 @@ class CakeImageWidget extends StatelessWidget { allowDrawingOutsideViewBox: allowDrawingOutsideViewBox ?? false, fit: fit ?? BoxFit.contain, placeholderBuilder: (_) { - return loadingWidget ?? const Center(child: CircularProgressIndicator()); + return loadingWidget ?? SizedBox(height: height, width: width, child: Center(child: CupertinoActivityIndicator())); }, errorBuilder: (_, __, ___) => _buildErrorWidget(context), ) @@ -99,7 +100,7 @@ class CakeImageWidget extends StatelessWidget { filterQuality: filterQuality ?? FilterQuality.medium, loadingBuilder: (_, Widget child, ImageChunkEvent? progress) { if (progress == null) return child; - return loadingWidget ?? const Center(child: CircularProgressIndicator()); + return loadingWidget ?? SizedBox(height: height, width: width, child: Center(child: CupertinoActivityIndicator())); }, errorBuilder: (_, __, ___) => _buildErrorWidget(context), ); diff --git a/lib/src/widgets/new_list_row/list_item_dropdown_widget.dart b/lib/src/widgets/new_list_row/list_item_dropdown_widget.dart index 011f90ea9..1a0ef17de 100644 --- a/lib/src/widgets/new_list_row/list_item_dropdown_widget.dart +++ b/lib/src/widgets/new_list_row/list_item_dropdown_widget.dart @@ -29,31 +29,29 @@ class ListItemDropdownWidget extends StatelessWidget { return ListItemStyleWrapper( isFirstInSection: isFirstInSection, isLastInSection: isLastInSection, + onTap: onTap, builder: (context, textStyle, labelStyle) { - return InkWell( - onTap: onTap, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(label, style: textStyle), - Row( - children: [ - if (trailingText != null) - Padding( - padding: const EdgeInsets.only(right: 8.0), - child: Text( - trailingText!, - style: labelStyle, - ), + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(label, style: textStyle), + Row( + children: [ + if (trailingText != null) + Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Text( + trailingText!, + style: labelStyle, ), - Icon( - Icons.keyboard_arrow_down, - color: Theme.of(context).colorScheme.onSurfaceVariant, ), - ], - ), - ], - ), + Icon( + Icons.keyboard_arrow_down, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ], + ), + ], ); }, ); diff --git a/lib/src/widgets/new_list_row/list_item_regular_row_widget.dart b/lib/src/widgets/new_list_row/list_item_regular_row_widget.dart index b2301b881..dc552fd66 100644 --- a/lib/src/widgets/new_list_row/list_item_regular_row_widget.dart +++ b/lib/src/widgets/new_list_row/list_item_regular_row_widget.dart @@ -23,22 +23,25 @@ class ListItemRegularRowWidget extends StatelessWidget { this.foregroundColor, this.trailingIconSize, this.bottomWidget, + this.subtitleColor, this.trailingWidget, this.copyableText, this.leadingIconErrorWidget, this.leadingIconSize, this.badgeIconSize, - this.iconColor}); + this.iconColor, this.secondaryLabel}); final String keyValue; final String label; final String? subtitle; final String? trailingText; + final String? secondaryLabel; final String? iconPath; final String? badgeIconPath; final VoidCallback? onTap; final bool isFirstInSection; final bool isLastInSection; + final Color? subtitleColor; final bool showArrow; final String? trailingIconPath; final Widget? bottomWidget; @@ -131,14 +134,21 @@ class ListItemRegularRowWidget extends StatelessWidget { color: Theme.of(context).colorScheme.primary), ) else - Text(label, - style: foregroundColor == null - ? textStyle - : textStyle.copyWith(color: foregroundColor)), + Row( + spacing: 4, + children: [ + Text(label, + style: foregroundColor == null + ? textStyle + : textStyle.copyWith(color: foregroundColor)), + if(secondaryLabel != null) + Text(secondaryLabel!, style: textStyle.copyWith(color: Theme.of(context).colorScheme.onSurfaceVariant),) + ], + ), if (subtitle != null) Text( subtitle!, - style: labelStyle.copyWith(fontSize: 12), + style: subtitleColor == null ? labelStyle.copyWith(fontSize: 12) : labelStyle.copyWith(fontSize: 12, color: subtitleColor), ), ], ), diff --git a/lib/src/widgets/new_list_row/new_list_section.dart b/lib/src/widgets/new_list_row/new_list_section.dart index 8abef379d..e5097e0a7 100644 --- a/lib/src/widgets/new_list_row/new_list_section.dart +++ b/lib/src/widgets/new_list_row/new_list_section.dart @@ -98,6 +98,8 @@ class NewListSections extends StatelessWidget { iconPath: item.iconPath, badgeIconPath: item.badgeIconPath, trailingIconPath: item.trailingIconPath, + secondaryLabel: item.secondaryLabel, + subtitleColor: item.subtitleColor, onTap: tapHandlers[item.keyValue] ?? item.onTap, isFirstInSection: isFirst, isLastInSection: isLast, diff --git a/lib/view_model/buy/buy_sell_view_model.dart b/lib/view_model/buy/buy_sell_view_model.dart index 100f13329..25e8ef862 100644 --- a/lib/view_model/buy/buy_sell_view_model.dart +++ b/lib/view_model/buy/buy_sell_view_model.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:math'; import 'package:cake_wallet/buy/buy_provider.dart'; import 'package:cake_wallet/buy/buy_quote.dart'; @@ -13,8 +14,9 @@ import 'package:cake_wallet/entities/provider_types.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart'; import 'package:cake_wallet/routes.dart'; -import 'package:cake_wallet/src/screens/buy/buy_sell_page.dart'; import 'package:cake_wallet/store/app_store.dart'; +import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart'; +import 'package:cw_core/amount/money.dart'; import 'package:cw_core/crypto_amount_format.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/utils/print_verbose.dart'; @@ -28,7 +30,7 @@ class BuySellViewModel = BuySellViewModelBase with _$BuySellViewModel; abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with Store { BuySellViewModelBase( AppStore appStore, - {required this.mode} + {required this.mode, required this.fiatConversionStore} ) : _cryptoAmount = '', fiatAmount = '', cryptoCurrencyAddress = '', @@ -60,6 +62,8 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S late Timer bestRateSync; + final FiatConversionStore fiatConversionStore; + List get availableBuyProviders { final providerTypes = ProvidersHelper.getAvailableBuyProviderTypes(); return providerTypes @@ -153,7 +157,28 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S @observable bool skipIsReadyToTradeReaction = false; + @computed + String? get maxFiatAmount { + if ((sortedQuotes.isEmpty && sortedRecommendedQuotes.isEmpty) || + buySellQuotState is! BuySellQuotLoaded) { + return null; + } + final allQuotes = sortedRecommendedQuotes.followedBy(sortedQuotes); + + final maxAmount = allQuotes.fold(0.0, (current, item) { + final limitMax = item.limits?.max?.toDouble() ?? 0.0; + return max(current, limitMax); + }); + + return maxAmount.toStringAsFixed(2); + } + + Money amountForQuote(Quote quote) => Money.parse(double.parse(fiatAmount)/quote.rate, cryptoCurrency); + + Money fiatAmountForQuote(Quote quote) { + return Money.parse((fiatConversionStore.prices[cryptoCurrency]! * double.parse(amountForQuote(quote).toString())).toStringAsFixed(2), fiatCurrency); + } // based on usd values, should have roughly equal worth (was done with ai though so it's subject to correction) static final Map> _defaultAmountsMap = { @@ -252,6 +277,13 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S isCryptoCurrencyAddressEnabled = !(cryptoCurrency == wallet.currency); } + + @computed + Iterable get activeWalletCurrencies => wallet.balance.keys; + + @computed + bool get hasMultipleCurrencies => activeWalletCurrencies.length > 1; + @action void changeCryptoCurrencyAddress(String address) => cryptoCurrencyAddress = address; @@ -401,6 +433,9 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S cryptoCurrencyAddress = _getInitialCryptoCurrencyAddress(); paymentMethodState = InitialPaymentMethod(); buySellQuotState = InitialBuySellQuotState(); + sortedRecommendedQuotes.clear(); + sortedQuotes.clear(); + paymentMethods.clear(); await _getAvailablePaymentTypes(); await calculateBestRate(); } @@ -458,16 +493,25 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S final List validProviders = providerList.where((provider) { if (mode == BuySellPageMode.buy) { - return provider.supportedCryptoList - .any((pair) => pair.from == cryptoCurrency && pair.to == fiatCurrency); + return provider.supportedCryptoList.any((pair) => + pair.from.symbol == cryptoCurrency.symbol && + pair.from.tag == cryptoCurrency.tag && + pair.to.symbol == fiatCurrency.symbol && + pair.to.tag == fiatCurrency.tag + ); } else { - return provider.supportedFiatList - .any((pair) => pair.from == fiatCurrency && pair.to == cryptoCurrency); + return provider.supportedFiatList.any((pair) => + pair.from.symbol == fiatCurrency.symbol && + pair.from.tag == fiatCurrency.tag && + pair.to.symbol == cryptoCurrency.symbol && + pair.to.tag == cryptoCurrency.tag + ); } }).toList(); + if (validProviders.isEmpty) { - buySellQuotState = BuySellQuotFailed(); + buySellQuotState = BuySellQuotFailed(errorMessage: "Couldn't find a provider that supports ${cryptoCurrency.fullName}."); return; } diff --git a/pubspec_base.yaml b/pubspec_base.yaml index c5525007b..c46043726 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -295,6 +295,7 @@ flutter: - assets/new-ui/crypto_full_icons/ - assets/new-ui/hardware_wallets/ - assets/new-ui/node_speed_badges/ + - assets/new-ui/buy_payment_methods/ fonts: - family: Lato diff --git a/res/pictures/buy_payment_methods/all_methods.svg b/res/pictures/buy_payment_methods/all_methods.svg new file mode 100644 index 000000000..b3e2adade --- /dev/null +++ b/res/pictures/buy_payment_methods/all_methods.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/pictures/buy_payment_methods/apple_pay.svg b/res/pictures/buy_payment_methods/apple_pay.svg new file mode 100644 index 000000000..dd7f18b62 --- /dev/null +++ b/res/pictures/buy_payment_methods/apple_pay.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/res/pictures/buy_payment_methods/bank_transfer.svg b/res/pictures/buy_payment_methods/bank_transfer.svg new file mode 100644 index 000000000..2eccd60ca --- /dev/null +++ b/res/pictures/buy_payment_methods/bank_transfer.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/pictures/buy_payment_methods/debit_card.svg b/res/pictures/buy_payment_methods/debit_card.svg new file mode 100644 index 000000000..4b68f70c6 --- /dev/null +++ b/res/pictures/buy_payment_methods/debit_card.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/pictures/buy_payment_methods/google_pay.svg b/res/pictures/buy_payment_methods/google_pay.svg new file mode 100644 index 000000000..836c8acb1 --- /dev/null +++ b/res/pictures/buy_payment_methods/google_pay.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/res/pictures/buy_payment_methods/paypal.svg b/res/pictures/buy_payment_methods/paypal.svg new file mode 100644 index 000000000..d723ede3e --- /dev/null +++ b/res/pictures/buy_payment_methods/paypal.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 207ee288c..9929af118 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -93,6 +93,7 @@ "available": "Available", "available_balance": "Available Balance", "available_balance_description": "The “Available Balance” or “Confirmed Balance” are funds that can be spent immediately. If funds appear in the lower balance but not the top balance, then you must wait a few minutes for the incoming funds to get more network confirmations. After they get more confirmations, they will be spendable.", + "available_providers": "Available Providers", "avg_savings": "Avg. Savings", "awaitDAppProcessing": "Kindly wait for the dApp to finish processing.", "awaiting_payment_confirmation": "Awaiting Payment Confirmation", @@ -254,6 +255,7 @@ "connect_your_hardware_wallet_usb": "Connect your hardware wallet using USB", "connected": "Connected", "connection_sync": "Connection and sync", + "connecting_you_to": "Connecting you to", "connections": "Connections", "connections_desc": "Manage connections to services and third party APIs.", "connectWalletPrompt": "Connect your wallet with WalletConnect to make transactions", @@ -279,6 +281,8 @@ "copy_payjoin_url": "Copy Payjoin URL", "copyWalletConnectLink": "Copy the WalletConnect link from dApp and paste here", "corrupted_seed_notice": "The files for this wallet are corrupted and are unable to be opened. Please view the seed phrase, save it, and restore the wallet.\n\nIf the value is empty, then the seed was unable to be correctly recovered.", + "could_not_load_quotes": "Could not load quotes", + "could_not_proceed_with_trade": "Could not proceed with trade", "countries": "Countries", "create_account": "Create Account", "create_backup": "Create backup", @@ -480,6 +484,7 @@ "extra_id": "Extra ID:", "extracted_address_content": "You will be sending funds to\n${recipient_name}", "failed_authentication": "Failed authentication. ${state_error}", + "failed_to_load_payment_methods": "Failed to load payment methods", "faq": "FAQ", "favorite_token": "Favorite token", "favorite_token_desc": "The favorite token's balance will show on the balance card.", @@ -538,6 +543,7 @@ "hide_address": "Hide address", "hide_details": "Hide Details", "high_contrast_theme": "High Contrast Theme", + "highest_success_rate": "Highest success rate", "history": "History", "home": "Home", "home_screen_settings": "Home screen settings", @@ -614,6 +620,7 @@ "live_fee_rates": "Live fee rates via API", "load_more": "Load more", "loading": "Loading", + "loading_rates": "Loading rates...", "loading_your_wallet": "Loading your wallet", "login": "Login", "logout": "Logout", @@ -765,6 +772,7 @@ "payment_id": "Payment ID: ", "payment_invoices": "Payment Invoices", "payment_made_easy": "Payments made easy", + "payment_method": "Payment Method", "payment_was_received": "Your payment was received.", "payments": "Payments", "pending": " (pending)", @@ -796,6 +804,7 @@ "please_reference_document": "Please reference the documents below for more information.", "please_select": "Please select:", "please_select_backup_file": "Please select backup file and enter backup password.", + "please_try_again_later": "Please try again later.", "please_try_to_connect_to_another_node": "Please try to connect to another node", "please_wait": "Please wait", "polygonscan_history": "PolygonScan history", @@ -818,6 +827,7 @@ "private_key": "Private key", "private_memo_optional": "Private Memo (optional)", "proceed_after_one_minute": "If the screen doesn’t proceed after 1 minute, check your email.", + "proceed": "Proceed", "proceed_on_device": "Proceed on your device", "proceed_on_device_description": "Please follow the instructions prompted on your hardware wallet", "processing": "Processing", @@ -837,6 +847,7 @@ "qr_payment_amount": "This QR code contains a payment amount. Do you want to overwrite the current value?", "quantity": "Quantity", "question_to_disable_2fa": "Are you sure that you want to disable Cake 2FA? A 2FA code will no longer be needed to access the wallet and certain functions.", + "rate": "Rate", "receivable_balance": "Receivable Balance", "receive": "Receive", "receive_amount": "Amount", @@ -1314,6 +1325,7 @@ "unsupported_asset": "We don't support this action for this asset. Please create or switch to a wallet of a supported asset type.", "uptime": "Uptime", "upto": "up to ${value}", + "up_to": "Up to", "usb": "USB", "use": "Switch to ", "use_blink_protection": "Use Blink Protection", From 1f4054d63245058b2cfa18e0464cff10cb0edf7b Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Fri, 3 Jul 2026 14:09:52 +0200 Subject: [PATCH 06/58] reformat --- .../pages/buy_sell/buy_sell_amount_page.dart | 832 +++++++++--------- .../buy_sell/buy_sell_provider_page.dart | 119 +-- .../buy_sell/buy_sell_selector_modal.dart | 123 ++- .../widgets/coins_page/cards/cards_view.dart | 3 +- lib/new-ui/widgets/floating_amount_input.dart | 37 +- lib/utils/exception_handler.dart | 2 +- lib/view_model/buy/buy_sell_view_model.dart | 9 +- 7 files changed, 599 insertions(+), 526 deletions(-) diff --git a/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart b/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart index ec22421b1..772cfc77a 100644 --- a/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart +++ b/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart @@ -1,435 +1,435 @@ -import 'dart:async'; + import 'dart:async'; -import 'package:cake_wallet/buy/payment_method.dart'; -import 'package:cake_wallet/buy/sell_buy_states.dart'; -import 'package:cake_wallet/entities/fiat_currency.dart'; -import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/new-ui/pages/buy_sell/buy_sell_provider_page.dart'; -import 'package:cake_wallet/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart'; -import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_args.dart'; -import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_sheet.dart'; -import 'package:cake_wallet/new-ui/widgets/floating_amount_input.dart'; -import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart'; -import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart'; -import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; -import 'package:cake_wallet/src/widgets/cake_image_widget.dart'; -import 'package:cake_wallet/themes/core/theme_extension.dart'; -import 'package:cake_wallet/utils/show_pop_up.dart'; -import 'package:cake_wallet/view_model/buy/buy_sell_view_model.dart'; -import 'package:cw_core/amount/money.dart'; -import 'package:cw_core/crypto_currency.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_mobx/flutter_mobx.dart'; -import 'package:mobx/mobx.dart'; + import 'package:cake_wallet/buy/payment_method.dart'; + import 'package:cake_wallet/buy/sell_buy_states.dart'; + import 'package:cake_wallet/entities/fiat_currency.dart'; + import 'package:cake_wallet/generated/i18n.dart'; + import 'package:cake_wallet/new-ui/pages/buy_sell/buy_sell_provider_page.dart'; + import 'package:cake_wallet/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart'; + import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_args.dart'; + import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_sheet.dart'; + import 'package:cake_wallet/new-ui/widgets/floating_amount_input.dart'; + import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart'; + import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart'; + import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; + import 'package:cake_wallet/src/widgets/cake_image_widget.dart'; + import 'package:cake_wallet/themes/core/theme_extension.dart'; + import 'package:cake_wallet/utils/show_pop_up.dart'; + import 'package:cake_wallet/view_model/buy/buy_sell_view_model.dart'; + import 'package:cw_core/amount/money.dart'; + import 'package:cw_core/crypto_currency.dart'; + import 'package:flutter/cupertino.dart'; + import 'package:flutter/material.dart'; + import 'package:flutter_mobx/flutter_mobx.dart'; + import 'package:mobx/mobx.dart'; -class NewBuySellAmountPage extends StatefulWidget { - const NewBuySellAmountPage({super.key, required this.buySellViewModel}); + class NewBuySellAmountPage extends StatefulWidget { + const NewBuySellAmountPage({super.key, required this.buySellViewModel}); - final BuySellViewModel buySellViewModel; + final BuySellViewModel buySellViewModel; - @override - State createState() => _NewBuySellAmountPageState(); -} - -class _NewBuySellAmountPageState extends State { - bool _customAmountMode = false; - bool _isLoadingPaymentMethods = false; - final customInputController = TextEditingController(); - final customInputFocusNode = FocusNode(); - - @override - void initState() { - super.initState(); - - // this is a hack for the "up to" display to show the actual upper limit. - // limits are loaded alongside quotes, and quotes for some providers only load properly with an amount and payment method selected. - // it'll be refactored soon anyway, i guess. - widget.buySellViewModel.changeFiatAmount(amount: "1000"); - widget.buySellViewModel.calculateBestRate(); - when( - (_) => widget.buySellViewModel.paymentMethodState is PaymentMethodLoaded, - () => widget.buySellViewModel.selectedPaymentMethod = widget.buySellViewModel.paymentMethods - .firstWhere((item) => item.paymentMethodType == PaymentType.all)); + @override + State createState() => _NewBuySellAmountPageState(); } - @override - Widget build(BuildContext context) { - return Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.vertical(top: Radius.circular(18)), - gradient: LinearGradient( - colors: [ - Theme.of(context).colorScheme.surface, - Theme.of(context).colorScheme.surfaceDim, - ], - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - ), - ), - child: SafeArea( - child: Column( - children: [ - Observer( - builder: (_) => ModalTopBar( - title: _pageTitle, - bottomText: !_customAmountMode || widget.buySellViewModel.maxFiatAmount == null - ? null - : "${S.of(context).up_to} ~${widget.buySellViewModel.maxFiatAmount} ${widget.buySellViewModel.fiatCurrency.title}", - leadingIcon: Icon(Icons.close), - onLeadingPressed: Navigator.of(context, rootNavigator: true).pop, - ), - ), - Expanded( - child: Observer( - builder: (_) => AnimatedSwitcher( - duration: Duration(milliseconds: 300), - child: _customAmountMode - ? BuySellCustomAmountInput( - fiatCurrency: widget.buySellViewModel.fiatCurrency, - cryptoCurrency: widget.buySellViewModel.cryptoCurrency, - cryptoAmount: widget.buySellViewModel.cryptoAmount, - isLoading: _isLoadingPaymentMethods, - hasCurrencySelector: widget.buySellViewModel.hasMultipleCurrencies, - onCurrencySelectorPressed: () => selectCryptoCurrency(context), - controller: customInputController, - focusNode: customInputFocusNode, - onContinuePressed: () { - navigateToProviders(context); - }, - onChanged: (amount) => - widget.buySellViewModel.changeFiatAmount(amount: amount), - ) - : BuySellDefaultAmountSelector( - key: ValueKey(0), - defaultAmounts: widget.buySellViewModel.defaultAmounts, - fiatCurrency: widget.buySellViewModel.fiatCurrency, - currentAmount: widget.buySellViewModel.fiatAmount, - hasCurrencySelector: widget.buySellViewModel.hasMultipleCurrencies, - onCurrencySelectorPressed: () => selectCryptoCurrency(context), - cryptoCurrency: widget.buySellViewModel.cryptoCurrency, - isLoading: _isLoadingPaymentMethods, - mode: widget.buySellViewModel.mode, - onSelected: (amount) async { - if (amount == null) { - // this resets the rate and prevents showing 0 usd = 0.something btc - await widget.buySellViewModel.changeFiatAmount(amount: ""); - setState(() { - _customAmountMode = true; - }); - customInputFocusNode.requestFocus(); - } else { - await widget.buySellViewModel.changeFiatAmount(amount: amount); - navigateToProviders(context); - } - }, - ), - ), - )) - ], - ), - ), - ); - } + class _NewBuySellAmountPageState extends State { + bool _customAmountMode = false; + bool _isLoadingPaymentMethods = false; + final customInputController = TextEditingController(); + final customInputFocusNode = FocusNode(); - void selectCryptoCurrency(BuildContext context) => CurrencyPickerSheet.show( - context: context, - args: CurrencyPickerArgs( - items: widget.buySellViewModel.activeWalletCurrencies.toList(), - onSelected: (item) => widget.buySellViewModel.changeCryptoCurrency(currency: item), - symbolResolver: widget.buySellViewModel.amountParsingProxy.getCryptoSymbol, - )); + @override + void initState() { + super.initState(); - String get _pageTitle => widget.buySellViewModel.mode == BuySellPageMode.buy - ? S.current.buy - : S.current.sell + - ((widget.buySellViewModel.cryptoCurrencies.length == 1) - ? " ${widget.buySellViewModel.cryptoCurrencies.first.fullName}" - : ""); - - Future navigateToProviders(BuildContext context) async { - if (_isLoadingPaymentMethods) { - return; + // this is a hack for the "up to" display to show the actual upper limit. + // limits are loaded alongside quotes, and quotes for some providers only load properly with an amount and payment method selected. + // it'll be refactored soon anyway, i guess. + widget.buySellViewModel.changeFiatAmount(amount: "1000"); + widget.buySellViewModel.calculateBestRate(); + when( + (_) => widget.buySellViewModel.paymentMethodState is PaymentMethodLoaded, + () => widget.buySellViewModel.selectedPaymentMethod = widget.buySellViewModel.paymentMethods + .firstWhere((item) => item.paymentMethodType == PaymentType.all)); } - try { - setState(() { - _isLoadingPaymentMethods = true; - }); - - await asyncWhen((_) => [PaymentMethodLoaded, PaymentMethodFailed] - .contains(widget.buySellViewModel.paymentMethodState.runtimeType)); - - if (widget.buySellViewModel.paymentMethodState is PaymentMethodFailed) { - showPopUp( - context: context, - builder: (context) => AlertWithOneAction( - alertTitle: S.of(context).failed_to_load_payment_methods, - alertContent: S.of(context).please_try_again_later, - buttonText: "OK", - buttonAction: Navigator.of(context).pop)); - return; - } - - widget.buySellViewModel.selectedPaymentMethod = widget.buySellViewModel.paymentMethods - .firstWhere((item) => item.paymentMethodType == PaymentType.all); - - // unawaited because BuySellProviderPage has a nice little "loading rates..." ui thingie - unawaited(widget.buySellViewModel.calculateBestRate()); - - final page = BuySellProviderPage(buySellViewModel: widget.buySellViewModel); - Navigator.of(context).push(CupertinoPageRoute( - builder: (context) => Material(color: Colors.transparent, child: page))); - } finally { - setState(() { - _isLoadingPaymentMethods = false; - }); - } - } -} - -class BuySellCustomAmountInput extends StatelessWidget { - const BuySellCustomAmountInput( - {super.key, - required this.fiatCurrency, - required this.cryptoCurrency, - required this.cryptoAmount, - required this.controller, - required this.onContinuePressed, - required this.isLoading, - required this.onChanged, - required this.focusNode, - required this.hasCurrencySelector, - required this.onCurrencySelectorPressed}); - - final FiatCurrency fiatCurrency; - final CryptoCurrency cryptoCurrency; - final String cryptoAmount; - final TextEditingController controller; - final FocusNode focusNode; - final bool isLoading; - final bool hasCurrencySelector; - final VoidCallback onCurrencySelectorPressed; - final VoidCallback onContinuePressed; - final Function(String) onChanged; - - @override - Widget build(BuildContext context) { - return Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - SizedBox.shrink(), - Column( - spacing: 8, - children: [ - BuySellCurrencyPickerPill(curr: cryptoCurrency, onTap: onCurrencySelectorPressed), - SizedBox.shrink(), - FloatingAmountInput( - currency: fiatCurrency, - focusNode: focusNode, - controller: controller, - onChanged: onChanged, - ), - Opacity( - opacity: cryptoAmount.isEmpty ? 0 : 1, - child: Text( - "≈ ${cryptoAmount} ${cryptoCurrency.symbol}", - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w600, - color: Theme.of(context).colorScheme.onSurfaceVariant), - ), - ) - ], - ), - Padding( - padding: const EdgeInsets.all(18.0), - child: NewPrimaryButton( - onPressed: onContinuePressed, - isLoading: isLoading, - text: S.of(context).continue_text, - color: Theme.of(context).colorScheme.primary, - textColor: Theme.of(context).colorScheme.onPrimary), - ) - ], - ); - } -} - -class BuySellDefaultAmountSelector extends StatelessWidget { - const BuySellDefaultAmountSelector( - {super.key, - required this.defaultAmounts, - required this.fiatCurrency, - required this.mode, - required this.onSelected, - this.currentAmount, - required this.isLoading, - required this.hasCurrencySelector, - required this.onCurrencySelectorPressed, - required this.cryptoCurrency}); - - final List defaultAmounts; - final String? currentAmount; - final bool isLoading; - final bool hasCurrencySelector; - final VoidCallback onCurrencySelectorPressed; - final CryptoCurrency cryptoCurrency; - final FiatCurrency fiatCurrency; - final BuySellPageMode mode; - final Function(String?) onSelected; - - @override - Widget build(BuildContext context) { - return Column( - spacing: 24, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - BuySellCurrencyPickerPill(curr: cryptoCurrency, onTap: onCurrencySelectorPressed), - Text( - mode == BuySellPageMode.sell - ? S.of(context).choose_amount_to_sell - : S.of(context).choose_amount_to_buy, - style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 18.0), - child: GridView.builder( - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - // +1 for "custom" option - itemCount: defaultAmounts.length + 1, - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, crossAxisSpacing: 8, mainAxisSpacing: 16, mainAxisExtent: 105), - itemBuilder: (context, index) { - final String? item = index == defaultAmounts.length ? null : defaultAmounts[index]; - - return BuySellAmountPill( - isLoading: isLoading && item == currentAmount, - amount: item == null ? null : Money.parse(item, fiatCurrency), - onTap: () => onSelected(item), - ); - }), - ), - ], - ); - } -} - -class BuySellAmountPill extends StatelessWidget { - const BuySellAmountPill({super.key, this.amount, required this.onTap, required this.isLoading}); - - final Money? amount; - final VoidCallback onTap; - final bool isLoading; - - @override - Widget build(BuildContext context) { - return Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(9999999999), - border: Border.all( - width: 1, - color: Theme.of(context).colorScheme.surfaceContainerHigh, - ), - gradient: LinearGradient( - colors: [ - context.customColors.cardGradientColorPrimary, - context.customColors.cardGradientColorSecondary - ], - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - ), - ), - child: Material( - color: Colors.transparent, - borderRadius: BorderRadius.circular(9999999999), - child: InkWell( - borderRadius: BorderRadius.circular(9999999999), - onTap: onTap, - child: isLoading - ? CupertinoActivityIndicator() - : Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Row( - spacing: 4, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (amount != null) - Text( - amount!.toStringWithPrecision(fractionalDigits: 0), - style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600), - ), - Text( - amount?.currency.symbol ?? S.of(context).custom, - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w600, - color: amount == null - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.onSurfaceVariant), - ) - ], - ), - if (amount == null) - Text( - S.of(context).enter_amount, - style: TextStyle( - fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant), - ) - ], - ), - ), - ), - ); - } -} - -class BuySellCurrencyPickerPill extends StatelessWidget { - const BuySellCurrencyPickerPill({super.key, required this.curr, required this.onTap}); - - final CryptoCurrency curr; - final VoidCallback onTap; - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: onTap, - child: Container( + @override + Widget build(BuildContext context) { + return Container( decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surfaceContainerHigh, - borderRadius: BorderRadius.circular(999999999), + borderRadius: BorderRadius.vertical(top: Radius.circular(18)), + gradient: LinearGradient( + colors: [ + Theme.of(context).colorScheme.surface, + Theme.of(context).colorScheme.surfaceDim, + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), ), - child: Padding( - padding: EdgeInsets.only(top: 5, bottom: 5, left: 5, right: 10), - child: Row( - mainAxisSize: MainAxisSize.min, - spacing: 10, + child: SafeArea( + child: Column( children: [ - CakeImageWidget( - imageUrl: curr.iconPath, - width: 30, - height: 30, - ), - Text( - curr.fullName ?? curr.title, - style: TextStyle(fontSize: 16), - ), - RotatedBox( - quarterTurns: 2, - child: CakeImageWidget( - imageUrl: "assets/new-ui/dropdown_arrow.svg", - width: 8, - height: 8, - colorFilter: - ColorFilter.mode(Theme.of(context).colorScheme.primary, BlendMode.srcIn), + Observer( + builder: (_) => ModalTopBar( + title: _pageTitle, + bottomText: !_customAmountMode || widget.buySellViewModel.maxFiatAmount == null + ? null + : "${S.of(context).up_to} ~${widget.buySellViewModel.maxFiatAmount} ${widget.buySellViewModel.fiatCurrency.title}", + leadingIcon: Icon(Icons.close), + onLeadingPressed: Navigator.of(context, rootNavigator: true).pop, ), ), + Expanded( + child: Observer( + builder: (_) => AnimatedSwitcher( + duration: Duration(milliseconds: 300), + child: _customAmountMode + ? BuySellCustomAmountInput( + fiatCurrency: widget.buySellViewModel.fiatCurrency, + cryptoCurrency: widget.buySellViewModel.cryptoCurrency, + cryptoAmount: widget.buySellViewModel.cryptoAmount, + isLoading: _isLoadingPaymentMethods, + hasCurrencySelector: widget.buySellViewModel.hasMultipleCurrencies, + onCurrencySelectorPressed: () => selectCryptoCurrency(context), + controller: customInputController, + focusNode: customInputFocusNode, + onContinuePressed: () { + navigateToProviders(context); + }, + onChanged: (amount) => + widget.buySellViewModel.changeFiatAmount(amount: amount), + ) + : BuySellDefaultAmountSelector( + key: ValueKey(0), + defaultAmounts: widget.buySellViewModel.defaultAmounts, + fiatCurrency: widget.buySellViewModel.fiatCurrency, + currentAmount: widget.buySellViewModel.fiatAmount, + hasCurrencySelector: widget.buySellViewModel.hasMultipleCurrencies, + onCurrencySelectorPressed: () => selectCryptoCurrency(context), + cryptoCurrency: widget.buySellViewModel.cryptoCurrency, + isLoading: _isLoadingPaymentMethods, + mode: widget.buySellViewModel.mode, + onSelected: (amount) async { + if (amount == null) { + // this resets the rate and prevents showing 0 usd = 0.something btc + await widget.buySellViewModel.changeFiatAmount(amount: ""); + setState(() { + _customAmountMode = true; + }); + customInputFocusNode.requestFocus(); + } else { + await widget.buySellViewModel.changeFiatAmount(amount: amount); + navigateToProviders(context); + } + }, + ), + ), + )) ], ), ), - ), - ); + ); + } + + void selectCryptoCurrency(BuildContext context) => CurrencyPickerSheet.show( + context: context, + args: CurrencyPickerArgs( + items: widget.buySellViewModel.activeWalletCurrencies.toList(), + onSelected: (item) => widget.buySellViewModel.changeCryptoCurrency(currency: item), + symbolResolver: widget.buySellViewModel.amountParsingProxy.getCryptoSymbol, + )); + + String get _pageTitle => widget.buySellViewModel.mode == BuySellPageMode.buy + ? S.current.buy + : S.current.sell + + ((widget.buySellViewModel.cryptoCurrencies.length == 1) + ? " ${widget.buySellViewModel.cryptoCurrencies.first.fullName}" + : ""); + + Future navigateToProviders(BuildContext context) async { + if (_isLoadingPaymentMethods) { + return; + } + + try { + setState(() { + _isLoadingPaymentMethods = true; + }); + + await asyncWhen((_) => [PaymentMethodLoaded, PaymentMethodFailed] + .contains(widget.buySellViewModel.paymentMethodState.runtimeType)); + + if (widget.buySellViewModel.paymentMethodState is PaymentMethodFailed) { + showPopUp( + context: context, + builder: (context) => AlertWithOneAction( + alertTitle: S.of(context).failed_to_load_payment_methods, + alertContent: S.of(context).please_try_again_later, + buttonText: "OK", + buttonAction: Navigator.of(context).pop)); + return; + } + + widget.buySellViewModel.selectedPaymentMethod = widget.buySellViewModel.paymentMethods + .firstWhere((item) => item.paymentMethodType == PaymentType.all); + + // unawaited because BuySellProviderPage has a nice little "loading rates..." ui thingie + unawaited(widget.buySellViewModel.calculateBestRate()); + + final page = BuySellProviderPage(buySellViewModel: widget.buySellViewModel); + Navigator.of(context).push(CupertinoPageRoute( + builder: (context) => Material(color: Colors.transparent, child: page))); + } finally { + setState(() { + _isLoadingPaymentMethods = false; + }); + } + } + } + + class BuySellCustomAmountInput extends StatelessWidget { + const BuySellCustomAmountInput( + {super.key, + required this.fiatCurrency, + required this.cryptoCurrency, + required this.cryptoAmount, + required this.controller, + required this.onContinuePressed, + required this.isLoading, + required this.onChanged, + required this.focusNode, + required this.hasCurrencySelector, + required this.onCurrencySelectorPressed}); + + final FiatCurrency fiatCurrency; + final CryptoCurrency cryptoCurrency; + final String cryptoAmount; + final TextEditingController controller; + final FocusNode focusNode; + final bool isLoading; + final bool hasCurrencySelector; + final VoidCallback onCurrencySelectorPressed; + final VoidCallback onContinuePressed; + final Function(String) onChanged; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SizedBox.shrink(), + Column( + spacing: 8, + children: [ + BuySellCurrencyPickerPill(curr: cryptoCurrency, onTap: onCurrencySelectorPressed), + SizedBox.shrink(), + FloatingAmountInput( + currency: fiatCurrency, + focusNode: focusNode, + controller: controller, + onChanged: onChanged, + ), + Opacity( + opacity: cryptoAmount.isEmpty ? 0 : 1, + child: Text( + "≈ ${cryptoAmount} ${cryptoCurrency.symbol}", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: Theme.of(context).colorScheme.onSurfaceVariant), + ), + ) + ], + ), + Padding( + padding: const EdgeInsets.all(18.0), + child: NewPrimaryButton( + onPressed: onContinuePressed, + isLoading: isLoading, + text: S.of(context).continue_text, + color: Theme.of(context).colorScheme.primary, + textColor: Theme.of(context).colorScheme.onPrimary), + ) + ], + ); + } + } + + class BuySellDefaultAmountSelector extends StatelessWidget { + const BuySellDefaultAmountSelector( + {super.key, + required this.defaultAmounts, + required this.fiatCurrency, + required this.mode, + required this.onSelected, + this.currentAmount, + required this.isLoading, + required this.hasCurrencySelector, + required this.onCurrencySelectorPressed, + required this.cryptoCurrency}); + + final List defaultAmounts; + final String? currentAmount; + final bool isLoading; + final bool hasCurrencySelector; + final VoidCallback onCurrencySelectorPressed; + final CryptoCurrency cryptoCurrency; + final FiatCurrency fiatCurrency; + final BuySellPageMode mode; + final Function(String?) onSelected; + + @override + Widget build(BuildContext context) { + return Column( + spacing: 24, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + BuySellCurrencyPickerPill(curr: cryptoCurrency, onTap: onCurrencySelectorPressed), + Text( + mode == BuySellPageMode.sell + ? S.of(context).choose_amount_to_sell + : S.of(context).choose_amount_to_buy, + style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 18.0), + child: GridView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + // +1 for "custom" option + itemCount: defaultAmounts.length + 1, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, crossAxisSpacing: 8, mainAxisSpacing: 16, mainAxisExtent: 105), + itemBuilder: (context, index) { + final String? item = index == defaultAmounts.length ? null : defaultAmounts[index]; + + return BuySellAmountPill( + isLoading: isLoading && item == currentAmount, + amount: item == null ? null : Money.parse(item, fiatCurrency), + onTap: () => onSelected(item), + ); + }), + ), + ], + ); + } + } + + class BuySellAmountPill extends StatelessWidget { + const BuySellAmountPill({super.key, this.amount, required this.onTap, required this.isLoading}); + + final Money? amount; + final VoidCallback onTap; + final bool isLoading; + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(9999999999), + border: Border.all( + width: 1, + color: Theme.of(context).colorScheme.surfaceContainerHigh, + ), + gradient: LinearGradient( + colors: [ + context.customColors.cardGradientColorPrimary, + context.customColors.cardGradientColorSecondary + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + child: Material( + color: Colors.transparent, + borderRadius: BorderRadius.circular(9999999999), + child: InkWell( + borderRadius: BorderRadius.circular(9999999999), + onTap: onTap, + child: isLoading + ? CupertinoActivityIndicator() + : Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + spacing: 4, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (amount != null) + Text( + amount!.toStringWithPrecision(fractionalDigits: 0), + style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600), + ), + Text( + amount?.currency.symbol ?? S.of(context).custom, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: amount == null + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.onSurfaceVariant), + ) + ], + ), + if (amount == null) + Text( + S.of(context).enter_amount, + style: TextStyle( + fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant), + ) + ], + ), + ), + ), + ); + } + } + + class BuySellCurrencyPickerPill extends StatelessWidget { + const BuySellCurrencyPickerPill({super.key, required this.curr, required this.onTap}); + + final CryptoCurrency curr; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surfaceContainerHigh, + borderRadius: BorderRadius.circular(999999999), + ), + child: Padding( + padding: EdgeInsets.only(top: 5, bottom: 5, left: 5, right: 10), + child: Row( + mainAxisSize: MainAxisSize.min, + spacing: 10, + children: [ + CakeImageWidget( + imageUrl: curr.iconPath, + width: 30, + height: 30, + ), + Text( + curr.fullName ?? curr.title, + style: TextStyle(fontSize: 16), + ), + RotatedBox( + quarterTurns: 2, + child: CakeImageWidget( + imageUrl: "assets/new-ui/dropdown_arrow.svg", + width: 8, + height: 8, + colorFilter: + ColorFilter.mode(Theme.of(context).colorScheme.primary, BlendMode.srcIn), + ), + ), + ], + ), + ), + ), + ); + } } -} diff --git a/lib/new-ui/pages/buy_sell/buy_sell_provider_page.dart b/lib/new-ui/pages/buy_sell/buy_sell_provider_page.dart index f96545951..4f1629227 100644 --- a/lib/new-ui/pages/buy_sell/buy_sell_provider_page.dart +++ b/lib/new-ui/pages/buy_sell/buy_sell_provider_page.dart @@ -48,10 +48,9 @@ class _BuySellProviderPageState extends State { leadingIcon: Icon(Icons.arrow_back_ios_new), onLeadingPressed: Navigator.of(context).pop, ), - Expanded( - child: Observer( + Expanded(child: Observer( builder: (_) { - if(widget.buySellViewModel.buySellQuotState is BuySellQuotFailed) { + if (widget.buySellViewModel.buySellQuotState is BuySellQuotFailed) { return Column( mainAxisAlignment: MainAxisAlignment.center, spacing: 24, @@ -64,7 +63,8 @@ class _BuySellProviderPageState extends State { S.of(context).could_not_load_quotes, style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500), ), - Text((widget.buySellViewModel.buySellQuotState as BuySellQuotFailed).errorMessage ?? + Text((widget.buySellViewModel.buySellQuotState as BuySellQuotFailed) + .errorMessage ?? S.of(context).please_try_again_later) ], ) @@ -72,43 +72,56 @@ class _BuySellProviderPageState extends State { ); } - if(widget.buySellViewModel.buySellQuotState is BuySellQuotLoading) { - return Center(child: Row(mainAxisAlignment:MainAxisAlignment.center, spacing:8, children: [ - CupertinoActivityIndicator(), - Text(S.of(context).loading_rates, style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),) - ],),); + if (widget.buySellViewModel.buySellQuotState is BuySellQuotLoading) { + return Center( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + spacing: 8, + children: [ + CupertinoActivityIndicator(), + Text( + S.of(context).loading_rates, + style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant), + ) + ], + ), + ); } return Padding( - padding: EdgeInsets.symmetric(horizontal: 18), - child: NewListSections(showHeader: true, sections: { - "": [ - ListItemRegularRow( - keyValue: "payment method", - label: S.of(context).payment_method, - showArrow: true, - onTap: (){ - - final page =BuySellPaymentMethodPage(buySellViewModel: widget.buySellViewModel); - Navigator.of(context).push(CupertinoPageRoute(builder: (context)=>Material(color: Colors.transparent,child: page,))); - }, - trailingText: widget.buySellViewModel.selectedPaymentMethod?.title) - ], - S.of(context).available_providers: [ - ...widget.buySellViewModel.sortedRecommendedQuotes.map(quoteListItem), - ListItemDropdown( - keyValue: "more options", - label: S.of(context).more_options, - onTap: () { - setState(() { - _allProvidersExpanded = !_allProvidersExpanded; - }); - }), - if (_allProvidersExpanded) - ...widget.buySellViewModel.sortedQuotes.map(quoteListItem) - ] - }), - ); + padding: EdgeInsets.symmetric(horizontal: 18), + child: NewListSections(showHeader: true, sections: { + "": [ + ListItemRegularRow( + keyValue: "payment method", + label: S.of(context).payment_method, + showArrow: true, + onTap: () { + final page = + BuySellPaymentMethodPage(buySellViewModel: widget.buySellViewModel); + Navigator.of(context).push(CupertinoPageRoute( + builder: (context) => Material( + color: Colors.transparent, + child: page, + ))); + }, + trailingText: widget.buySellViewModel.selectedPaymentMethod?.title) + ], + S.of(context).available_providers: [ + ...widget.buySellViewModel.sortedRecommendedQuotes.map(quoteListItem), + ListItemDropdown( + keyValue: "more options", + label: S.of(context).more_options, + onTap: () { + setState(() { + _allProvidersExpanded = !_allProvidersExpanded; + }); + }), + if (_allProvidersExpanded) + ...widget.buySellViewModel.sortedQuotes.map(quoteListItem) + ] + }), + ); }, )) ], @@ -116,18 +129,19 @@ class _BuySellProviderPageState extends State { ); } - String get _pageTitle => (widget.buySellViewModel.mode == BuySellPageMode.buy - ? S.current.buy - : S.current.sell) + " " + (widget.buySellViewModel.cryptoCurrency.fullName ?? ""); + String get _pageTitle => + (widget.buySellViewModel.mode == BuySellPageMode.buy ? S.current.buy : S.current.sell) + + " " + + (widget.buySellViewModel.cryptoCurrency.fullName ?? ""); ListItem quoteListItem(Quote quote) => ListItemRegularRow( keyValue: quote.provider.title, label: quote.rampName ?? quote.provider.title, - secondaryLabel: quote.rampName != null? quote.provider.title : null, + secondaryLabel: quote.rampName != null ? quote.provider.title : null, subtitle: quote.badges.isEmpty ? null : quote.badges.join(" - "), subtitleColor: Theme.of(context).colorScheme.primary, iconPath: quote.darkIconPath, - onTap: (){ + onTap: () { widget.buySellViewModel.changeOption(quote); navigateToConfirmation(context); }, @@ -137,14 +151,21 @@ class _BuySellProviderPageState extends State { mainAxisAlignment: MainAxisAlignment.center, spacing: 4, children: [ - Text(widget.buySellViewModel.amountForQuote(quote).toStringWithSymbol(fractionalDigits: 8)), - Text("= ${widget.buySellViewModel.fiatAmountForQuote(quote).toStringWithSymbol(fractionalDigits: 2, trimZeros: false)}", style: TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant)) - - ],)); + Text(widget.buySellViewModel + .amountForQuote(quote) + .toStringWithSymbol(fractionalDigits: 8)), + Text( + "= ${widget.buySellViewModel.fiatAmountForQuote(quote).toStringWithSymbol(fractionalDigits: 2, trimZeros: false)}", + style: TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant)) + ], + )); void navigateToConfirmation(BuildContext context) { - final page = BuySellConfirmationPage(buySellViewModel: widget.buySellViewModel); - Navigator.of(context).push(CupertinoPageRoute(builder: (context)=>Material(color: Colors.transparent,child: page,))); + Navigator.of(context).push(CupertinoPageRoute( + builder: (context) => Material( + color: Colors.transparent, + child: page, + ))); } } diff --git a/lib/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart b/lib/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart index 6d5343ccc..0c1013443 100644 --- a/lib/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart +++ b/lib/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart @@ -14,34 +14,51 @@ class BuySellSelectorModal extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.vertical(top: Radius.circular(18)), - gradient: LinearGradient( - colors: [ - Theme.of(context).colorScheme.surface, - Theme.of(context).colorScheme.surfaceDim, - ], - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - ), - ), - child: SafeArea( - top: false, - child: Column(spacing:24, mainAxisSize: MainAxisSize.min, children: [ - SizedBox.shrink(), - Text(S.of(context).buy_or_sell, style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500),), - Text(S.of(context).buy_or_sell_desc, style: TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant),), - Padding( - padding: EdgeInsets.symmetric(horizontal: 18.0), - child: Column(spacing: 12,children: [ - BuySellSelectorModalButton(title: S.of(context).buy_crypto, description: S.of(context).buy_crypto_desc, iconPath: "assets/new-ui/plus.svg", onTap: ()=>openBuySellPage(context, BuySellPageMode.buy),), - BuySellSelectorModalButton(title: S.of(context).sell_crypto, description: S.of(context).sell_crypto_desc, iconPath: "assets/new-ui/sell.svg", onTap: ()=>openBuySellPage(context, BuySellPageMode.sell)) - ],), + decoration: BoxDecoration( + borderRadius: BorderRadius.vertical(top: Radius.circular(18)), + gradient: LinearGradient( + colors: [ + Theme.of(context).colorScheme.surface, + Theme.of(context).colorScheme.surfaceDim, + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, ), - SizedBox.shrink() - ]) - ) - ); + ), + child: SafeArea( + top: false, + child: Column(spacing: 24, mainAxisSize: MainAxisSize.min, children: [ + SizedBox.shrink(), + Text( + S.of(context).buy_or_sell, + style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500), + ), + Text( + S.of(context).buy_or_sell_desc, + style: + TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant), + ), + Padding( + padding: EdgeInsets.symmetric(horizontal: 18.0), + child: Column( + spacing: 12, + children: [ + BuySellSelectorModalButton( + title: S.of(context).buy_crypto, + description: S.of(context).buy_crypto_desc, + iconPath: "assets/new-ui/plus.svg", + onTap: () => openBuySellPage(context, BuySellPageMode.buy), + ), + BuySellSelectorModalButton( + title: S.of(context).sell_crypto, + description: S.of(context).sell_crypto_desc, + iconPath: "assets/new-ui/sell.svg", + onTap: () => openBuySellPage(context, BuySellPageMode.sell)) + ], + ), + ), + SizedBox.shrink() + ]))); } void openBuySellPage(BuildContext context, BuySellPageMode mode) { @@ -58,9 +75,13 @@ class BuySellSelectorModal extends StatelessWidget { } } - class BuySellSelectorModalButton extends StatelessWidget { - const BuySellSelectorModalButton({super.key, required this.title, required this.description, required this.iconPath, required this.onTap}); + const BuySellSelectorModalButton( + {super.key, + required this.title, + required this.description, + required this.iconPath, + required this.onTap}); final String title; final String description; @@ -75,8 +96,8 @@ class BuySellSelectorModalButton extends StatelessWidget { decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), border: Border.all( - width: 1, color: Theme.of(context).colorScheme.surfaceContainerHigh, - + width: 1, + color: Theme.of(context).colorScheme.surfaceContainerHigh, ), gradient: LinearGradient( colors: [ @@ -87,16 +108,36 @@ class BuySellSelectorModalButton extends StatelessWidget { end: Alignment.bottomCenter, ), ), - child: Padding(padding: EdgeInsets.all(24), child: Row(spacing: 20, children: [ - - CakeImageWidget(imageUrl: iconPath, width: 55, height: 55, colorFilter: ColorFilter.mode(Theme.of(context).colorScheme.onSurfaceVariant,BlendMode.srcIn),), - Column(spacing: 8, crossAxisAlignment: CrossAxisAlignment.start,children: [ - Text(title, style: TextStyle(fontWeight: FontWeight.w500, fontSize: 16),), - Text(description, style: TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant),) - ],) - - ],),), - + child: Padding( + padding: EdgeInsets.all(24), + child: Row( + spacing: 20, + children: [ + CakeImageWidget( + imageUrl: iconPath, + width: 55, + height: 55, + colorFilter: ColorFilter.mode( + Theme.of(context).colorScheme.onSurfaceVariant, BlendMode.srcIn), + ), + Column( + spacing: 8, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle(fontWeight: FontWeight.w500, fontSize: 16), + ), + Text( + description, + style: TextStyle( + fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant), + ) + ], + ) + ], + ), + ), ), ); } diff --git a/lib/new-ui/widgets/coins_page/cards/cards_view.dart b/lib/new-ui/widgets/coins_page/cards/cards_view.dart index ca6cb6bb8..85812c80c 100644 --- a/lib/new-ui/widgets/coins_page/cards/cards_view.dart +++ b/lib/new-ui/widgets/coins_page/cards/cards_view.dart @@ -179,7 +179,8 @@ class _CardsViewState extends State { icon: Icons.arrow_forward_ios_rounded, iconSize: 12, onTap: () { - showModalBottomSheet(context: context, builder: (context)=>BuySellSelectorModal()); + showModalBottomSheet( + context: context, builder: (context) => BuySellSelectorModal()); }, ) ] diff --git a/lib/new-ui/widgets/floating_amount_input.dart b/lib/new-ui/widgets/floating_amount_input.dart index 3367d0a3a..7fb230452 100644 --- a/lib/new-ui/widgets/floating_amount_input.dart +++ b/lib/new-ui/widgets/floating_amount_input.dart @@ -3,7 +3,14 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class FloatingAmountInput extends StatefulWidget { - const FloatingAmountInput({super.key, required this.currency, required this.controller, this.focusNode, this.inputFormatters, this.onChanged, this.validator}); + const FloatingAmountInput( + {super.key, + required this.currency, + required this.controller, + this.focusNode, + this.inputFormatters, + this.onChanged, + this.validator}); final Currency currency; final TextEditingController controller; @@ -28,7 +35,7 @@ class _FloatingAmountInputState extends State { @override Widget build(BuildContext context) { - return Center( + return Center( child: Row( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.baseline, @@ -59,19 +66,17 @@ class _FloatingAmountInputState extends State { hoverColor: Colors.transparent, focusedBorder: InputBorder.none, enabledBorder: InputBorder.none, - hintText: _amountFocused || widget.controller.text.isNotEmpty - ? null - : "0.00", + hintText: _amountFocused || widget.controller.text.isNotEmpty ? null : "0.00", hintStyle: Theme.of(context).textTheme.displayMedium?.copyWith( - fontWeight: FontWeight.w400, - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), + fontWeight: FontWeight.w400, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), ), style: Theme.of(context).textTheme.displayMedium?.copyWith( - fontWeight: FontWeight.w400, - fontSize: 45, - color: Theme.of(context).colorScheme.onSurface, - ), + fontWeight: FontWeight.w400, + fontSize: 45, + color: Theme.of(context).colorScheme.onSurface, + ), ), ), const SizedBox(width: 8), @@ -80,10 +85,10 @@ class _FloatingAmountInputState extends State { maxLines: 1, overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.displayMedium?.copyWith( - fontWeight: FontWeight.w400, - fontSize: 45, - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), + fontWeight: FontWeight.w400, + fontSize: 45, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), ), ], ), diff --git a/lib/utils/exception_handler.dart b/lib/utils/exception_handler.dart index e46650517..fe9c0844a 100644 --- a/lib/utils/exception_handler.dart +++ b/lib/utils/exception_handler.dart @@ -118,7 +118,7 @@ class ExceptionHandler { if (kDebugMode || kProfileMode) { if (_ignoreError(errorDetails.exception.toString()) || - _ignoreError(errorDetails.stack.toString())) { + _ignoreError(errorDetails.stack.toString()) || _flutterErrorIgnore(errorDetails)) { printV("(BELOW ERROR IS IGNORED AND WILL NOT TRIGGER POPUP IN PROD)"); } FlutterError.presentError(errorDetails); diff --git a/lib/view_model/buy/buy_sell_view_model.dart b/lib/view_model/buy/buy_sell_view_model.dart index 25e8ef862..4e6e2bf51 100644 --- a/lib/view_model/buy/buy_sell_view_model.dart +++ b/lib/view_model/buy/buy_sell_view_model.dart @@ -174,10 +174,15 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S return maxAmount.toStringAsFixed(2); } - Money amountForQuote(Quote quote) => Money.parse(double.parse(fiatAmount)/quote.rate, cryptoCurrency); + Money amountForQuote(Quote quote) => + Money.parse(double.parse(fiatAmount) / quote.rate, cryptoCurrency); Money fiatAmountForQuote(Quote quote) { - return Money.parse((fiatConversionStore.prices[cryptoCurrency]! * double.parse(amountForQuote(quote).toString())).toStringAsFixed(2), fiatCurrency); + return Money.parse( + (fiatConversionStore.prices[cryptoCurrency]! * + double.parse(amountForQuote(quote).toString())) + .toStringAsFixed(2), + fiatCurrency); } // based on usd values, should have roughly equal worth (was done with ai though so it's subject to correction) From 72b0844f2950d2781ef4cea9f5c1ef1fac10e429 Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Fri, 3 Jul 2026 14:47:25 +0200 Subject: [PATCH 07/58] fix decimals exception --- lib/view_model/buy/buy_sell_view_model.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/view_model/buy/buy_sell_view_model.dart b/lib/view_model/buy/buy_sell_view_model.dart index 4e6e2bf51..4228057cb 100644 --- a/lib/view_model/buy/buy_sell_view_model.dart +++ b/lib/view_model/buy/buy_sell_view_model.dart @@ -175,7 +175,7 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S } Money amountForQuote(Quote quote) => - Money.parse(double.parse(fiatAmount) / quote.rate, cryptoCurrency); + Money.parse((double.parse(fiatAmount) / quote.rate).toStringAsFixed(cryptoCurrency.decimals), cryptoCurrency); Money fiatAmountForQuote(Quote quote) { return Money.parse( From 8405aa932c02c1a0d8d993bb33f97b3bee826158 Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Fri, 3 Jul 2026 17:00:21 +0200 Subject: [PATCH 08/58] hide currency picker for single-currency wallets --- lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart b/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart index 772cfc77a..b9a44673f 100644 --- a/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart +++ b/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart @@ -222,9 +222,11 @@ Column( spacing: 8, children: [ + if (hasCurrencySelector) ...[ BuySellCurrencyPickerPill(curr: cryptoCurrency, onTap: onCurrencySelectorPressed), SizedBox.shrink(), - FloatingAmountInput( + ], + FloatingAmountInput( currency: fiatCurrency, focusNode: focusNode, controller: controller, @@ -285,6 +287,7 @@ spacing: 24, mainAxisAlignment: MainAxisAlignment.center, children: [ + if(hasCurrencySelector) BuySellCurrencyPickerPill(curr: cryptoCurrency, onTap: onCurrencySelectorPressed), Text( mode == BuySellPageMode.sell From 2c3ef1cf636ba2fd3417e456f8728f1fa177916c Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Fri, 3 Jul 2026 17:00:34 +0200 Subject: [PATCH 09/58] hide "more options" if no more options --- lib/new-ui/pages/buy_sell/buy_sell_provider_page.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/new-ui/pages/buy_sell/buy_sell_provider_page.dart b/lib/new-ui/pages/buy_sell/buy_sell_provider_page.dart index 4f1629227..1963f9a92 100644 --- a/lib/new-ui/pages/buy_sell/buy_sell_provider_page.dart +++ b/lib/new-ui/pages/buy_sell/buy_sell_provider_page.dart @@ -109,6 +109,7 @@ class _BuySellProviderPageState extends State { ], S.of(context).available_providers: [ ...widget.buySellViewModel.sortedRecommendedQuotes.map(quoteListItem), + if(widget.buySellViewModel.sortedQuotes.isNotEmpty) ListItemDropdown( keyValue: "more options", label: S.of(context).more_options, From c3545b13e316b96740d8659b263da95dbf2f0d19 Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Fri, 3 Jul 2026 17:02:13 +0200 Subject: [PATCH 10/58] better error msg --- lib/view_model/buy/buy_sell_view_model.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/view_model/buy/buy_sell_view_model.dart b/lib/view_model/buy/buy_sell_view_model.dart index 4228057cb..0116b943b 100644 --- a/lib/view_model/buy/buy_sell_view_model.dart +++ b/lib/view_model/buy/buy_sell_view_model.dart @@ -544,7 +544,7 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S .toList(); if (validQuotes.isEmpty) { - buySellQuotState = BuySellQuotFailed(); + buySellQuotState = BuySellQuotFailed(errorMessage: "No provider could create a quote for ${cryptoCurrency.fullName}"); return; } From 4b40365efb549e7fd4b9d2fe26313411c527886b Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Fri, 3 Jul 2026 17:29:54 +0200 Subject: [PATCH 11/58] add viewpadding for virtual keyboard --- .../pages/buy_sell/buy_sell_amount_page.dart | 79 ++++++++++--------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart b/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart index b9a44673f..a0d250fac 100644 --- a/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart +++ b/lib/new-ui/pages/buy_sell/buy_sell_amount_page.dart @@ -215,45 +215,48 @@ @override Widget build(BuildContext context) { - return Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - SizedBox.shrink(), - Column( - spacing: 8, - children: [ - if (hasCurrencySelector) ...[ - BuySellCurrencyPickerPill(curr: cryptoCurrency, onTap: onCurrencySelectorPressed), - SizedBox.shrink(), - ], - FloatingAmountInput( - currency: fiatCurrency, - focusNode: focusNode, - controller: controller, - onChanged: onChanged, - ), - Opacity( - opacity: cryptoAmount.isEmpty ? 0 : 1, - child: Text( - "≈ ${cryptoAmount} ${cryptoCurrency.symbol}", - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w600, - color: Theme.of(context).colorScheme.onSurfaceVariant), + return Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SizedBox.shrink(), + Column( + spacing: 8, + children: [ + if (hasCurrencySelector) ...[ + BuySellCurrencyPickerPill(curr: cryptoCurrency, onTap: onCurrencySelectorPressed), + SizedBox.shrink(), + ], + FloatingAmountInput( + currency: fiatCurrency, + focusNode: focusNode, + controller: controller, + onChanged: onChanged, ), - ) - ], - ), - Padding( - padding: const EdgeInsets.all(18.0), - child: NewPrimaryButton( - onPressed: onContinuePressed, - isLoading: isLoading, - text: S.of(context).continue_text, - color: Theme.of(context).colorScheme.primary, - textColor: Theme.of(context).colorScheme.onPrimary), - ) - ], + Opacity( + opacity: cryptoAmount.isEmpty ? 0 : 1, + child: Text( + "≈ ${cryptoAmount} ${cryptoCurrency.symbol}", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: Theme.of(context).colorScheme.onSurfaceVariant), + ), + ) + ], + ), + Padding( + padding: const EdgeInsets.all(18.0), + child: NewPrimaryButton( + onPressed: onContinuePressed, + isLoading: isLoading, + text: S.of(context).continue_text, + color: Theme.of(context).colorScheme.primary, + textColor: Theme.of(context).colorScheme.onPrimary), + ) + ], + ), ); } } From 4427a05f6b58f6d21498aa8991b07566999d3db3 Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Tue, 14 Jul 2026 21:10:44 +0200 Subject: [PATCH 12/58] auto-reformat --- cw_bitcoin/lib/address_from_output.dart | 15 +- cw_bitcoin/lib/bitcoin_address_record.dart | 12 +- cw_bitcoin/lib/bitcoin_amount_format.dart | 4 +- .../bitcoin_commit_transaction_exception.dart | 1 - .../lib/bitcoin_transaction_priority.dart | 18 +- cw_bitcoin/lib/bitcoin_wallet.dart | 70 +- cw_bitcoin/lib/bitcoin_wallet_addresses.dart | 8 +- .../bitcoin_wallet_creation_credentials.dart | 32 +- cw_bitcoin/lib/bitcoin_wallet_keys.dart | 13 +- cw_bitcoin/lib/bitcoin_wallet_service.dart | 24 +- cw_bitcoin/lib/electrum.dart | 39 +- .../lib/electrum_transaction_history.dart | 1 - cw_bitcoin/lib/electrum_transaction_info.dart | 8 +- cw_bitcoin/lib/electrum_wallet.dart | 167 +- cw_bitcoin/lib/electrum_wallet_addresses.dart | 232 +- cw_bitcoin/lib/exceptions.dart | 2 +- cw_bitcoin/lib/hardware/bitbox_service.dart | 3 +- .../lib/hardware/litecoin_ledger_service.dart | 6 +- .../lib/lightning/lightning_wallet.dart | 32 +- .../pending_lightning_transaction.dart | 3 +- cw_bitcoin/lib/litecoin_wallet.dart | 56 +- cw_bitcoin/lib/litecoin_wallet_addresses.dart | 20 +- cw_bitcoin/lib/litecoin_wallet_service.dart | 17 +- cw_bitcoin/lib/payjoin/manager.dart | 31 +- .../lib/payjoin/payjoin_receive_worker.dart | 31 +- .../lib/payjoin/payjoin_send_worker.dart | 8 +- cw_bitcoin/lib/payjoin/storage.dart | 34 +- cw_bitcoin/lib/psbt/signer.dart | 57 +- cw_bitcoin/lib/psbt/transaction_builder.dart | 31 +- cw_bitcoin/lib/psbt/utils.dart | 4 +- cw_bitcoin/lib/psbt/v0_deserialize.dart | 7 +- cw_bitcoin/lib/psbt/v0_finalizer.dart | 7 +- cw_bitcoin/lib/utils.dart | 2 +- cw_bitcoin/pubspec.lock | 10 +- .../lib/src/bitcoin_cash_wallet_service.dart | 8 +- .../lib/src/exceptions/exceptions.dart | 2 +- cw_core/lib/account.dart | 2 +- cw_core/lib/account_list.dart | 1 - cw_core/lib/address_info.part.dart | 4 +- cw_core/lib/amount/amount_sanitizer.dart | 1 - cw_core/lib/amount_converter.dart | 9 +- cw_core/lib/balance_card_style_settings.dart | 30 +- cw_core/lib/card_design.dart | 233 +- cw_core/lib/crypto_amount_format.dart | 1 - cw_core/lib/crypto_currency.dart | 985 ++++- cw_core/lib/currency_for_wallet_type.dart | 3 +- cw_core/lib/db/sqlite_debug.dart | 2 +- cw_core/lib/encryption_file_utils.dart | 55 +- cw_core/lib/erc20_token.part.dart | 6 +- cw_core/lib/exceptions.dart | 2 +- cw_core/lib/format_amount.dart | 6 +- cw_core/lib/format_fixed.dart | 9 +- cw_core/lib/get_height_by_date.dart | 3 +- cw_core/lib/get_height_by_date_xmr.dart | 1 - cw_core/lib/hive_type_ids.dart | 48 +- cw_core/lib/key.dart | 3 +- cw_core/lib/keyable.dart | 2 +- cw_core/lib/lnurl.dart | 15 +- cw_core/lib/monero_wallet_keys.dart | 12 +- cw_core/lib/mweb_utxo.part.dart | 4 +- cw_core/lib/nano_account.part.dart | 4 +- cw_core/lib/node.dart | 64 +- cw_core/lib/node_legacy.dart | 46 +- cw_core/lib/node_legacy.part.dart | 7 +- cw_core/lib/node_list.dart | 21 +- cw_core/lib/parseBoolFromString.dart | 2 +- cw_core/lib/parse_fixed.dart | 3 +- cw_core/lib/pathForWallet.dart | 3 +- cw_core/lib/payjoin_session.dart | 1 - cw_core/lib/payjoin_session.part.dart | 4 +- cw_core/lib/payment_uris.dart | 7 +- cw_core/lib/root_dir.dart | 28 +- cw_core/lib/sec_random_native.dart | 3 +- cw_core/lib/solana_rpc_http_service.dart | 6 +- cw_core/lib/spl_token.part.dart | 4 +- cw_core/lib/transaction_direction.dart | 8 +- cw_core/lib/transaction_history.dart | 3 +- cw_core/lib/transaction_priority.dart | 6 +- cw_core/lib/tron_token.part.dart | 4 +- cw_core/lib/utils/file.dart | 4 +- cw_core/lib/utils/proxy_logger/abstract.dart | 6 +- .../proxy_logger/memory_proxy_logger.dart | 22 +- .../lib/utils/proxy_logger/silent_logger.dart | 4 +- cw_core/lib/utils/proxy_socket/abstract.dart | 17 +- cw_core/lib/utils/proxy_socket/insecure.dart | 21 +- cw_core/lib/utils/proxy_socket/secure.dart | 18 +- cw_core/lib/utils/proxy_socket/socks.dart | 12 +- cw_core/lib/utils/proxy_wrapper.dart | 50 +- cw_core/lib/utils/tor/abstract.dart | 2 +- cw_core/lib/utils/tor/disabled.dart | 2 +- cw_core/lib/utils/tor/socks.dart | 2 +- cw_core/lib/utils/tor/torch.dart | 2 +- cw_core/lib/utils/zpub.dart | 22 +- cw_core/lib/wallet_info.dart | 240 +- cw_core/lib/wallet_info_legacy.dart | 2 +- cw_core/lib/wallet_info_legacy.part.dart | 16 +- cw_core/lib/wallet_keys_file.dart | 9 +- cw_core/lib/wallet_type.dart | 25 +- cw_core/lib/wallet_type.part.dart | 4 +- cw_core/lib/wownero_amount_format.dart | 2 +- cw_core/lib/zano_asset.part.dart | 4 +- cw_core/pubspec.lock | 10 +- .../test/amount/amount_sanitizer_test.dart | 10 +- cw_core/test/amount/money_test.dart | 4 +- cw_core/test/crypto_amount_format.dart | 6 +- cw_core/test/format_fixed_test.dart | 12 +- cw_core/test/lnurl_test.dart | 27 +- cw_decred/lib/wallet.dart | 14 +- cw_decred/lib/wallet_service.dart | 12 +- cw_decred/pubspec.lock | 8 +- cw_dogecoin/lib/cw_dogecoin.dart | 1 - .../src/dogecoin_transaction_priority.dart | 7 +- cw_dogecoin/lib/src/dogecoin_wallet.dart | 3 +- .../lib/src/dogecoin_wallet_addresses.dart | 9 +- cw_dogecoin/test/cw_dogecoin_test.dart | 3 +- cw_evm/lib/clients/arbitrum_client.dart | 1 - cw_evm/lib/clients/base_client.dart | 1 - cw_evm/lib/clients/bsc_client.dart | 1 - cw_evm/lib/clients/ethereum_client.dart | 1 - cw_evm/lib/clients/evm_chain_client.dart | 15 +- cw_evm/lib/clients/polygon_client.dart | 1 - cw_evm/lib/contract/erc20.dart | 47 +- cw_evm/lib/deuro/deuro_savings.dart | 17 +- cw_evm/lib/deuro/deuro_savings_contract.dart | 6 +- .../deuro/deuro_savings_gateway_contract.dart | 110 +- cw_evm/lib/evm_chain_exceptions.dart | 13 +- cw_evm/lib/evm_chain_registry.dart | 3 +- cw_evm/lib/evm_chain_transaction_model.dart | 3 +- cw_evm/lib/evm_chain_wallet.dart | 15 +- cw_evm/lib/evm_chain_wallet_addresses.dart | 1 - cw_evm/lib/evm_erc20_balance.dart | 2 +- .../hardware/evm_chain_bitbox_service.dart | 5 +- .../evm_chain_ledger_credentials.dart | 28 +- .../hardware/evm_chain_ledger_service.dart | 6 +- cw_evm/lib/tokens/base_tokens.dart | 4 +- cw_evm/lib/tokens/polygon_tokens.dart | 4 +- cw_evm/lib/usdt0/usdt0_config.dart | 20 +- cw_evm/lib/usdt0/usdt0_service.dart | 2 +- cw_evm/lib/utils/network_chain_utils.dart | 1 - cw_monero/lib/api/account_list.dart | 2 +- .../creation_transaction_exception.dart | 4 +- .../exceptions/setup_wallet_exception.dart | 4 +- .../exceptions/wallet_creation_exception.dart | 2 +- .../exceptions/wallet_opening_exception.dart | 2 +- .../wallet_restore_from_keys_exception.dart | 4 +- .../wallet_restore_from_seed_exception.dart | 4 +- cw_monero/lib/api/monero_output.dart | 2 +- .../lib/api/structs/pending_transaction.dart | 15 +- cw_monero/lib/api/subaddress_list.dart | 32 +- cw_monero/lib/api/transaction_history.dart | 120 +- cw_monero/lib/api/wallet.dart | 59 +- cw_monero/lib/api/wallet_manager.dart | 58 +- cw_monero/lib/bip39_seed.dart | 15 +- ...monero_transaction_creation_exception.dart | 2 +- ...onero_transaction_no_inputs_exception.dart | 3 +- cw_monero/lib/ledger.dart | 13 +- .../lib/mnemonics/chinese_simplified.dart | 2 +- cw_monero/lib/mnemonics/dutch.dart | 2 +- cw_monero/lib/mnemonics/french.dart | 3258 ++++++++--------- cw_monero/lib/mnemonics/german.dart | 2 +- cw_monero/lib/mnemonics/italian.dart | 3258 ++++++++--------- cw_monero/lib/mnemonics/japanese.dart | 2 +- cw_monero/lib/mnemonics/portuguese.dart | 2 +- cw_monero/lib/mnemonics/russian.dart | 2 +- cw_monero/lib/mnemonics/spanish.dart | 2 +- cw_monero/lib/monero_account_list.dart | 25 +- cw_monero/lib/monero_subaddress_list.dart | 24 +- cw_monero/lib/monero_transaction_history.dart | 11 +- cw_monero/lib/monero_wallet.dart | 131 +- cw_monero/lib/monero_wallet_addresses.dart | 34 +- cw_monero/lib/monero_wallet_service.dart | 67 +- cw_monero/lib/pending_monero_transaction.dart | 6 +- cw_monero/lib/trezor.dart | 14 +- cw_monero/pubspec.lock | 10 +- cw_monero/test/bip39_seed_test.dart | 19 +- .../test/monero_wallet_service_test.dart | 12 +- cw_monero/test/utils/setup_monero_c.dart | 13 +- cw_mweb/lib/cw_mweb.dart | 14 +- cw_mweb/lib/mweb_ffi.dart | 3 +- cw_mweb/lib/mwebd.pb.dart | 1398 ++++--- cw_mweb/lib/mwebd.pbgrpc.dart | 215 +- cw_nano/lib/nano_client.dart | 12 +- cw_nano/lib/nano_transaction_model.dart | 4 +- cw_nano/lib/nano_wallet.dart | 6 +- cw_nano/lib/nano_wallet_service.dart | 3 +- cw_nano/lib/pending_nano_transaction.dart | 2 +- cw_nano/pubspec.lock | 8 +- cw_solana/lib/pending_solana_transaction.dart | 2 +- cw_solana/lib/solana_client.dart | 12 +- cw_solana/lib/solana_wallet.dart | 24 +- cw_solana/lib/solana_wallet_service.dart | 10 +- cw_tron/lib/pending_tron_transaction.dart | 2 +- cw_tron/lib/tron_balance.dart | 2 +- cw_tron/lib/tron_client.dart | 11 +- cw_tron/lib/tron_exception.dart | 3 +- cw_tron/lib/tron_http_provider.dart | 16 +- cw_tron/lib/tron_wallet.dart | 25 +- cw_wownero/pubspec.lock | 8 +- cw_zano/lib/api/consts.dart | 2 +- cw_zano/lib/api/model/asset_id_params.dart | 6 +- cw_zano/lib/api/model/balance.dart | 5 +- .../lib/api/model/create_wallet_result.dart | 7 +- cw_zano/lib/api/model/destination.dart | 13 +- cw_zano/lib/api/model/employed_entries.dart | 19 +- .../model/get_recent_txs_and_info_params.dart | 15 +- .../model/get_recent_txs_and_info_result.dart | 11 +- .../api/model/get_wallet_status_result.dart | 3 +- cw_zano/lib/api/model/recent_history.dart | 12 +- cw_zano/lib/api/model/store_result.dart | 2 +- cw_zano/lib/api/model/subtransfer.dart | 3 +- cw_zano/lib/api/model/transfer.dart | 44 +- cw_zano/lib/api/model/transfer_params.dart | 21 +- cw_zano/lib/api/model/wi_extended.dart | 18 +- cw_zano/lib/mnemonics/english.dart | 3252 ++++++++-------- .../lib/model/pending_zano_transaction.dart | 2 +- cw_zano/lib/model/zano_asset.dart | 1 + .../zano_transaction_creation_exception.dart | 2 +- .../model/zano_transaction_credentials.dart | 3 +- cw_zano/lib/model/zano_transaction_info.dart | 15 +- cw_zano/lib/model/zano_wallet_keys.dart | 8 +- cw_zano/lib/zano_formatter.dart | 54 +- cw_zano/lib/zano_transaction_history.dart | 10 +- cw_zano/lib/zano_wallet.dart | 19 +- cw_zano/lib/zano_wallet_api.dart | 61 +- cw_zano/lib/zano_wallet_exceptions.dart | 6 +- cw_zano/lib/zano_wallet_service.dart | 35 +- cw_zano/pubspec.lock | 8 +- lib/anonpay/anonpay_api.dart | 4 +- lib/anonpay/anonpay_donation_link_info.dart | 6 +- lib/anonpay/anonpay_info_base.dart | 2 +- lib/anypay/any_pay_chain.dart | 8 +- lib/anypay/any_pay_payment.dart | 108 +- .../any_pay_payment_committed_info.dart | 24 +- lib/anypay/any_pay_payment_instruction.dart | 49 +- .../any_pay_payment_instruction_output.dart | 14 +- lib/anypay/any_pay_trasnaction.dart | 10 +- lib/anypay/anypay_api.dart | 162 +- lib/bitcoin/cw_bitcoin.dart | 38 +- lib/bitcoin_cash/cw_bitcoin_cash.dart | 8 +- lib/buy/buy_amount.dart | 13 +- lib/buy/buy_exception.dart | 3 +- lib/buy/buy_provider.dart | 20 +- lib/buy/dfx/dfx_buy_provider.dart | 17 +- lib/buy/get_buy_provider_icon.dart | 11 +- lib/buy/kryptonim/kryptonim.dart | 19 +- lib/buy/meld/meld_buy_provider.dart | 32 +- lib/buy/moonpay/moonpay_provider.dart | 23 +- lib/buy/onramper/onramper_buy_provider.dart | 72 +- lib/buy/payment_method.dart | 8 +- lib/buy/robinhood/robinhood_buy_provider.dart | 5 +- lib/buy/sell_buy_states.dart | 3 +- lib/buy/wyre/wyre_buy_provider.dart | 16 +- .../src/auth/cake_pay_account_page.dart | 15 +- .../src/auth/cake_pay_verify_otp_page.dart | 4 +- lib/cake_pay/src/cake_pay_states.dart | 1 - .../src/cards/cake_pay_buy_card_page.dart | 17 +- .../src/cards/cake_pay_cards_page.dart | 4 +- lib/cake_pay/src/models/cake_pay_card.dart | 7 +- lib/cake_pay/src/models/cake_pay_order.dart | 31 +- .../src/models/cake_pay_user_credentials.dart | 8 +- lib/cake_pay/src/widgets/cake_pay_tile.dart | 12 +- .../widgets/denominations_amount_widget.dart | 14 +- .../src/widgets/enter_amount_widget.dart | 19 +- .../src/widgets/flip_card_widget.dart | 17 +- lib/cake_pay/src/widgets/link_extractor.dart | 2 +- .../widgets/rounded_overlay_cards_widget.dart | 15 +- .../three_checkbox_alert_content_widget.dart | 18 +- lib/cake_pay/src/widgets/user_card_item.dart | 9 +- .../address_lookup_provider.dart | 1 - .../address_resolver_service.dart | 3 +- .../address_resolver_utils.dart | 3 +- .../bip_353/bip_353_address_provider.dart | 16 +- .../bip_353/bip_353_record.dart | 3 +- lib/core/address_resolver/ens/ens_record.dart | 24 +- .../fio/fio_address_provider.dart | 28 +- .../lnurl_pay/lnurl_pay_address_provider.dart | 5 +- .../lnurl_pay/lnurlpay_record.dart | 2 +- .../mastodon/mastodon_api.dart | 2 - .../mastodon/mastodon_user.dart | 11 +- .../address_resolver/nostr/nostr_api.dart | 8 +- .../address_resolver/nostr/nostr_user.dart | 3 +- .../openalias/openalias_record.dart | 2 +- lib/core/address_resolver/parsed_address.dart | 2 +- .../twitter/twitter_address_provider.dart | 1 - .../address_resolver/twitter/twitter_api.dart | 4 +- .../unstoppable_address_provider.dart | 6 +- .../wellknown/wellknown_record.dart | 8 +- lib/core/address_resolver/yat/yat_record.dart | 2 +- .../address_resolver/yat/yat_service.dart | 15 +- lib/core/address_resolver/yat/yat_store.dart | 64 +- .../zano/zano_alias_address_provider.dart | 1 - .../zcash/zcash_names_record.dart | 3 +- lib/core/address_validator.dart | 57 +- lib/core/amount_validator.dart | 6 +- lib/core/auth_state.dart | 1 - lib/core/backup_service.dart | 47 +- lib/core/backup_service_v3.dart | 63 +- lib/core/csv_export_service.dart | 11 +- lib/core/email_validator.dart | 3 +- lib/core/execution_state.dart | 2 +- lib/core/fiat_conversion_service.dart | 18 +- lib/core/mnemonic_length.dart | 2 +- lib/core/monero_account_label_validator.dart | 8 +- .../open_cryptopay_service.dart | 22 +- lib/core/seed_validator.dart | 5 +- lib/core/selectable_option.dart | 3 - .../socks_proxy_node_address_validator.dart | 5 +- lib/core/template_validator.dart | 11 +- lib/core/universal_address_detector.dart | 2 +- lib/core/utilities.dart | 3 +- lib/core/validator.dart | 10 +- lib/core/wallet_creation_service.dart | 3 +- lib/core/wallet_creation_state.dart | 2 +- lib/core/wallet_loading_service.dart | 81 +- lib/di.dart | 474 ++- lib/dogecoin/cw_dogecoin.dart | 5 +- .../auto_generate_subaddress_status.dart | 12 +- lib/entities/balance_display_mode.dart | 9 +- lib/entities/biometric_auth.dart | 2 +- lib/entities/bitcoin_amount_display_mode.dart | 3 +- lib/entities/calculate_fiat_amount.dart | 4 +- lib/entities/calculate_fiat_amount_raw.dart | 2 +- lib/entities/contact.dart | 7 +- lib/entities/contact.part.dart | 4 +- lib/entities/contact_base.dart | 2 +- lib/entities/country.dart | 7 +- lib/entities/default_settings_migration.dart | 33 +- lib/entities/emoji_string_extension.dart | 8 +- .../evm_transaction_error_fees_handler.dart | 18 +- lib/entities/exchange_api_mode.dart | 2 +- lib/entities/fiat_currency.dart | 30 +- lib/entities/format_amount.dart | 6 +- .../require_hardware_wallet_connection.dart | 7 +- lib/entities/haven_seed_store.part.dart | 4 +- lib/entities/ios_legacy_helper.dart | 18 +- .../new_ui_entities/list_item/list_item.dart | 6 +- .../list_item/list_item_selector.dart | 2 +- .../list_item/list_item_text_field.dart | 19 +- lib/entities/node_check.dart | 23 +- lib/entities/node_list.dart | 1 + lib/entities/pin_code_required_duration.dart | 2 +- lib/entities/preferences_key.dart | 3 +- lib/entities/provider_types.dart | 26 +- lib/entities/qr_scanner.dart | 3 +- lib/entities/qr_view_data.dart | 2 +- lib/entities/seed_phrase_length.dart | 3 +- lib/entities/seed_type.dart | 3 +- lib/entities/service_status.dart | 3 +- lib/entities/sort_balance_types.dart | 2 +- lib/entities/template.part.dart | 4 +- .../transaction_creation_credentials.dart | 2 +- lib/entities/transaction_description.dart | 13 +- .../transaction_description.part.dart | 3 +- lib/entities/transaction_history.dart | 3 +- lib/entities/wallet_description.dart | 4 +- lib/entities/wallet_manager.dart | 8 +- lib/entities/wallet_nft_response.dart | 4 +- .../exchange_provider_description.dart | 118 +- .../provider/chainflip_exchange_provider.dart | 75 +- .../provider/exolix_exchange_provider.dart | 46 +- .../provider/jupiter_exchange_provider.dart | 3 +- .../near_Intents_exchange_provider.dart | 75 +- .../provider/sideshift_exchange_provider.dart | 22 +- .../simpleswap_exchange_provider.dart | 25 +- .../provider/swapsxyz_exchange_provider.dart | 103 +- .../provider/swaptrade_exchange_provider.dart | 33 +- .../provider/thorchain_exchange.provider.dart | 78 +- .../provider/xoswap_exchange_provider.dart | 56 +- lib/exchange/trade_legacy.part.dart | 10 +- lib/haven/cw_haven.dart | 1 - lib/locales/hausa_intl.dart | 9 +- lib/locales/yoruba_intl.dart | 7 +- lib/main.dart | 7 +- lib/monero/cw_monero.dart | 33 +- lib/new-ui/modal_navigator.dart | 39 +- lib/new-ui/pages/about_page.dart | 2 +- lib/new-ui/pages/account_customizer.dart | 12 +- lib/new-ui/pages/addresses_page.dart | 3 +- .../pages/bridge/bridge_confirm_sheet.dart | 6 +- .../pages/bridge/bridge_network_page.dart | 6 +- .../bridge_receive_address_input_page.dart | 8 +- .../pages/buy_sell/buy_sell_amount_page.dart | 790 ++-- .../buy_sell/buy_sell_provider_page.dart | 18 +- lib/new-ui/pages/card_customizer.dart | 3 +- lib/new-ui/pages/coin_control_page.dart | 22 +- lib/new-ui/pages/home_page.dart | 173 +- lib/new-ui/pages/lightning_username_page.dart | 16 +- lib/new-ui/pages/receive_page.dart | 57 +- lib/new-ui/pages/scan_page.dart | 10 +- lib/new-ui/pages/send_page.dart | 63 +- lib/new-ui/pages/settings_page.dart | 65 +- lib/new-ui/pages/swap_page.dart | 47 +- .../card_customizer/card_customizer_bloc.dart | 18 +- .../card_customizer_event.dart | 2 - .../card_customizer_state.dart | 32 +- .../lightning_username_bloc.dart | 6 +- .../lightning_username_event.dart | 2 +- .../widgets/addresses_page/address_info.dart | 3 +- .../addresses_page/address_label_input.dart | 3 +- lib/new-ui/widgets/animated_dropdown.dart | 33 +- lib/new-ui/widgets/apps_widget.dart | 14 +- .../widgets/bridge/confirm_details_card.dart | 5 +- .../widgets/bridge/transfer_history_row.dart | 6 +- lib/new-ui/widgets/changelog_modal.dart | 2 +- .../coin_control_list_item.dart | 136 +- .../action_row/coin_action_row.dart | 137 +- .../coins_page/assets_history/asset_tile.dart | 22 +- .../assets_history_section.dart | 44 +- .../assets_history/assets_section.dart | 102 +- .../assets_history/assets_top_bar.dart | 107 +- .../assets_history/history_filters_page.dart | 9 +- .../assets_history/history_modal.dart | 6 +- .../assets_history/history_order_tile.dart | 26 +- .../assets_history/history_section.dart | 328 +- .../history_swap_providers_page.dart | 6 +- .../assets_history/history_tile.dart | 60 +- .../assets_history/history_tile_base.dart | 49 +- .../assets_history/history_top_bar.dart | 20 +- .../assets_history/history_trade_tile.dart | 21 +- .../transaction_details_modal.dart | 70 +- .../coins_page/cards/balance_card.dart | 82 +- .../widgets/coins_page/cards/cards_view.dart | 84 +- lib/new-ui/widgets/coins_page/mweb_ad.dart | 4 +- .../coins_page/top_bar_widget/chain_icon.dart | 6 +- .../top_bar_widget/lightning_switcher.dart | 8 +- .../top_bar_widget/pulsing_dot.dart | 12 +- .../coins_page/top_bar_widget/sync_bar.dart | 25 +- .../coins_page/top_bar_widget/top_bar.dart | 6 +- .../unconfirmed_balance_widget.dart | 127 +- .../widgets/coins_page/wallet_info.dart | 14 +- lib/new-ui/widgets/confirm_swiper.dart | 16 +- lib/new-ui/widgets/copy_wrapper.dart | 2 +- .../currency_picker/currency_picker_args.dart | 8 +- .../picker_recents_loader.dart | 4 +- .../single_network_currency_picker.dart | 6 +- lib/new-ui/widgets/dropdown_row.dart | 3 +- lib/new-ui/widgets/keyboard_hide_overlay.dart | 3 +- lib/new-ui/widgets/line_tab_switcher.dart | 80 +- lib/new-ui/widgets/long_press_menu.dart | 3 +- lib/new-ui/widgets/modal_header.dart | 12 +- lib/new-ui/widgets/modal_page_wrapper.dart | 16 +- lib/new-ui/widgets/modern_button.dart | 2 +- lib/new-ui/widgets/new_primary_button.dart | 33 +- lib/new-ui/widgets/picker.dart | 147 +- .../receive_page/payjoin_copy_modal.dart | 2 +- .../receive_page/receive_address_type.dart | 3 +- .../receive_address_type_selector.dart | 11 +- .../receive_page/receive_amount_display.dart | 108 +- .../receive_page/receive_amount_modal.dart | 16 +- .../receive_page/receive_bottom_buttons.dart | 49 +- .../receive_page/receive_info_box.dart | 45 +- .../receive_page/receive_label_modal.dart | 12 +- .../receive_page/receive_label_widget.dart | 3 +- .../receive_large_amount_preview.dart | 3 +- .../widgets/receive_page/receive_qr_code.dart | 14 +- .../receive_page/receive_token_display.dart | 11 +- .../widgets/receive_page/receive_top_bar.dart | 65 +- .../widgets/send_page/fiat_amount_bar.dart | 16 +- .../send_page/floating_icon_button.dart | 16 +- .../send_page/l2_send_external_modal.dart | 39 +- .../widgets/send_page/recipient_dot_row.dart | 26 +- .../widgets/send_page/send_address_input.dart | 2 +- .../widgets/send_page/send_amount_input.dart | 30 +- .../send_page/send_confirm_bottom_widget.dart | 4 +- .../widgets/send_page/send_confirm_sheet.dart | 131 +- .../widgets/send_page/send_memo_input.dart | 3 +- .../send_page/send_syncing_indicator.dart | 35 +- .../swap_page/provider_options_page.dart | 14 +- .../swap_page/refund_address_modal.dart | 8 +- .../swap_address_selection_modal.dart | 81 +- .../widgets/swap_page/swap_options_page.dart | 17 +- ...wap_provider_initial_preference_modal.dart | 2 +- .../swap_page/swap_send_external_modal.dart | 10 +- .../trocador_providers_settings.dart | 3 +- lib/order/order.part.dart | 4 +- lib/order/order_provider_description.dart | 4 +- lib/order/order_source_description.dart | 2 +- lib/reactions/bootstrap.dart | 2 +- lib/reactions/fiat_rate_update.dart | 3 +- .../on_authentication_state_change.dart | 3 +- .../on_current_fiat_api_mode_change.dart | 17 +- lib/reactions/on_current_fiat_change.dart | 17 +- lib/reactions/on_current_wallet_change.dart | 3 +- lib/router.dart | 34 +- lib/solana/cw_solana.dart | 4 +- lib/src/screens/Info_page.dart | 2 +- lib/src/screens/auth/auth_page.dart | 8 +- lib/src/screens/backup/backup_page.dart | 11 +- .../backup/edit_backup_password_page.dart | 5 +- lib/src/screens/buy/buy_sell_page.dart | 10 +- lib/src/screens/buy/webview_page.dart | 5 +- .../connect_device/connect_device_page.dart | 3 +- .../monero_hardware_wallet_options_page.dart | 2 +- .../connect_device/widgets/device_tile.dart | 6 +- .../screens/contact/contact_list_page.dart | 60 +- lib/src/screens/contact/contact_page.dart | 33 +- lib/src/screens/dashboard/dashboard_page.dart | 7 +- .../dashboard/desktop_dashboard_page.dart | 2 +- .../desktop_action_button.dart | 14 +- .../desktop_dashboard_actions.dart | 114 +- .../desktop_dashboard_navbar.dart | 3 +- .../desktop_sidebar/side_menu.dart | 2 +- .../desktop_wallet_selection_dropdown.dart | 35 +- .../dashboard/favorite_token_modal.dart | 5 +- .../dashboard/pages/balance/balance_page.dart | 3 +- .../pages/balance/crypto_balance_widget.dart | 10 +- .../dashboard/pages/cake_features_page.dart | 66 +- .../dashboard/pages/navigation_dock.dart | 27 +- .../dashboard/pages/nft_listing_page.dart | 6 +- .../dashboard/pages/transactions_page.dart | 9 +- lib/src/screens/dashboard/sign_page.dart | 14 +- .../dashboard/widgets/action_button.dart | 3 +- .../dashboard/widgets/date_section_raw.dart | 4 +- .../dashboard/widgets/filter_tile.dart | 2 +- .../dashboard/widgets/filter_widget.dart | 19 +- .../screens/dashboard/widgets/header_row.dart | 8 +- .../widgets/new_main_navbar_widget.dart | 193 +- .../screens/dashboard/widgets/order_row.dart | 20 +- .../dashboard/widgets/page_indicator.dart | 64 +- .../screens/dashboard/widgets/sign_form.dart | 10 +- .../widgets/solana_nft_tile_widget.dart | 14 +- .../dashboard/widgets/sync_indicator.dart | 3 +- .../dashboard/widgets/transaction_raw.dart | 17 +- .../dashboard/widgets/verify_form.dart | 2 +- lib/src/screens/dev/moneroc_cache_debug.dart | 45 +- .../screens/dev/moneroc_call_profiler.dart | 25 +- lib/src/screens/dev/network_requests.dart | 58 +- lib/src/screens/dev/qr_tools_page.dart | 17 +- .../screens/dev/secure_preferences_page.dart | 22 +- .../screens/dev/shared_preferences_page.dart | 73 +- .../screens/disclaimer/disclaimer_page.dart | 2 +- lib/src/screens/exchange/exchange_page.dart | 10 +- .../widgets/currency_picker_widget.dart | 2 +- .../mobile_exchange_cards_section.dart | 4 +- .../screens/exchange/widgets/picker_item.dart | 5 +- .../exchange_trade_external_send_page.dart | 6 +- .../exchange_trade/exchange_trade_item.dart | 2 +- .../exchange_trade/exchange_trade_page.dart | 19 +- .../exchange_trade/widgets/timer_widget.dart | 6 +- .../integrations/deuro/savings_page.dart | 6 +- .../integrations/deuro/widgets/numpad.dart | 14 +- .../monero_accounts/widgets/account_tile.dart | 2 +- .../wallet_group_description_page.dart | 2 +- ..._group_existing_seed_description_page.dart | 17 +- .../new_wallet/widgets/select_button.dart | 20 +- .../nodes/node_create_or_edit_page.dart | 102 +- .../nodes/pow_node_create_or_edit_page.dart | 43 +- lib/src/screens/nodes/widgets/node_form.dart | 189 +- .../screens/nodes/widgets/node_list_row.dart | 53 +- lib/src/screens/pin_code/pin_code.dart | 8 +- lib/src/screens/pin_code/pin_code_widget.dart | 6 +- .../widgets/anonpay_status_section.dart | 24 +- .../receive/widgets/copy_link_item.dart | 4 +- .../release_notes/release_notes_screen.dart | 20 +- lib/src/screens/rescan/rescan_page.dart | 1 - .../restore/restore_from_backup_page.dart | 16 +- .../screens/restore/restore_options_page.dart | 10 +- .../wallet_restore_choose_derivation.dart | 7 +- .../wallet_restore_from_keys_form.dart | 38 +- .../wallet_restore_from_seed_form.dart | 2 +- .../screens/restore/wallet_restore_page.dart | 5 +- lib/src/screens/root/root.dart | 2 - .../seed_verification_success_view.dart | 16 +- lib/src/screens/seed/wallet_seed_page.dart | 19 +- lib/src/screens/send/send_page.dart | 667 ++-- .../widgets/choose_yat_address_alert.dart | 11 +- lib/src/screens/send/widgets/send_card.dart | 17 +- lib/src/screens/settings/attributes.dart | 2 +- .../settings/connection_sync_page.dart | 311 +- .../desktop_settings_page.dart | 12 +- .../settings/display_settings_page.dart | 350 +- .../screens/settings/domain_lookups_page.dart | 12 +- .../screens/settings/items/item_headers.dart | 2 +- .../screens/settings/manage_nodes_page.dart | 21 +- lib/src/screens/settings/mweb_logs_page.dart | 12 +- lib/src/screens/settings/mweb_node_page.dart | 41 +- lib/src/screens/settings/mweb_settings.dart | 2 +- .../screens/settings/other_settings_page.dart | 137 +- lib/src/screens/settings/privacy_page.dart | 92 +- .../settings/security_backup_page.dart | 21 +- .../settings/silent_payments_logs_page.dart | 9 +- .../settings/silent_payments_settings.dart | 25 +- .../widgets/settings_choices_cell.dart | 7 +- .../settings/widgets/settings_picker_row.dart | 2 +- .../widgets/settings_theme_choice.dart | 52 +- .../widgets/wallet_connect_button.dart | 4 +- .../setup_2fa/setup_2fa_enter_code_page.dart | 12 +- .../widgets/popup_cancellable_alert.dart | 11 +- .../setup_pin_code/setup_pin_code.dart | 13 +- lib/src/screens/splash/splash_page.dart | 6 +- lib/src/screens/start_tor/start_tor_page.dart | 4 +- .../support_chat/support_chat_page.dart | 24 +- .../support_chat/widgets/chatwoot_widget.dart | 7 +- .../trade_details/track_trade_list_item.dart | 5 +- .../trade_details_list_card.dart | 12 +- .../trade_details/trade_details_page.dart | 8 +- .../trade_details_status_item.dart | 3 +- .../address_list_item.dart | 2 +- .../confirmations_list_item.dart | 6 +- .../transaction_details/rbf_details_page.dart | 3 +- .../textfield_list_item.dart | 2 +- .../transaction_details_page.dart | 32 +- .../transaction_expandable_list_item.dart | 2 +- .../unspent_coins_list_page.dart | 82 +- .../widgets/unspent_coins_list_item.dart | 48 +- lib/src/screens/ur/animated_ur_page.dart | 12 +- .../widgets/qr_format_info_bottom_sheet.dart | 5 +- .../ur/widgets/qr_selection_dialog.dart | 9 +- lib/src/screens/ur/widgets/urqr.dart | 14 +- .../eth/evm_supported_methods.dart | 2 +- .../services/walletkit_service.dart | 39 +- .../wallet_connect/utils/method_utils.dart | 13 +- .../utils/wc_permissions_mapper.dart | 3 +- .../wc_connections_listing_view.dart | 2 +- .../enter_wallet_connect_uri_widget.dart | 4 +- .../wallet_connect/widgets/wc_hero_card.dart | 1 - .../screens/wallet_keys/wallet_keys_page.dart | 2 +- .../screens/wallet_list/wallet_list_page.dart | 86 +- .../wallet_unlock_arguments.dart | 5 +- .../welcome/create_pin_welcome_page.dart | 3 +- lib/src/screens/welcome/welcome_page.dart | 10 +- .../yat/widgets/first_introduction.dart | 95 +- .../yat/widgets/second_introduction.dart | 64 +- .../yat/widgets/third_introduction.dart | 22 +- lib/src/screens/yat/widgets/yat_bar.dart | 20 +- .../yat/widgets/yat_page_indicator.dart | 10 +- lib/src/widgets/adaptable_page_view.dart | 3 +- lib/src/widgets/alert_with_picker_option.dart | 1 - lib/src/widgets/base_alert_dialog.dart | 97 +- lib/src/widgets/base_text_form_field.dart | 3 +- lib/src/widgets/blockchain_height_widget.dart | 2 +- ...ake_pay_transaction_sent_bottom_sheet.dart | 21 +- .../confirm_sending_bottom_sheet_widget.dart | 3 +- .../info_bottom_sheet_widget.dart | 4 +- .../info_steps_bottom_sheet_widget.dart | 22 +- .../payment_confirmation_bottom_sheet.dart | 3 +- .../swap_confirmation_bottom_sheet.dart | 3 +- .../swap_details_bottom_sheet.dart | 11 +- .../token_selection_bottom_sheet.dart | 13 +- lib/src/widgets/cake_image_widget.dart | 12 +- lib/src/widgets/check_box_picker.dart | 11 +- lib/src/widgets/checkbox_widget.dart | 1 + lib/src/widgets/evm_switcher.dart | 3 +- .../widgets/haven_wallet_removal_popup.dart | 20 +- lib/src/widgets/index.dart | 1 + .../new_list_row/list_Item_style_wrapper.dart | 40 +- .../list_item_checkbox_widget.dart | 64 +- .../list_item_regular_row_widget.dart | 20 +- .../list_item_selector_widget.dart | 13 +- .../list_item_text_field_widget.dart | 5 +- .../new_list_row/list_item_toggle_widget.dart | 3 +- .../new_list_row/new_list_section.dart | 17 +- lib/src/widgets/number_text_fild_widget.dart | 10 +- lib/src/widgets/picker.dart | 3 +- .../widgets/picker_inner_wrapper_widget.dart | 10 +- lib/src/widgets/provider_optoin_tile.dart | 4 +- lib/src/widgets/rounded_checkbox.dart | 28 +- lib/src/widgets/rounded_icon_button.dart | 2 +- .../scrollable_with_bottom_section.dart | 2 +- lib/src/widgets/seed_widget.dart | 13 +- lib/src/widgets/seedphrase_grid_widget.dart | 8 +- lib/src/widgets/simple_checkbox.dart | 6 +- lib/src/widgets/standard_checkbox.dart | 11 +- lib/src/widgets/standard_list.dart | 19 +- lib/src/widgets/standard_list_status_row.dart | 6 +- .../widgets/standard_slide_button_widget.dart | 4 +- .../validable_annotated_editable_text.dart | 19 +- lib/src/widgets/vulnerable_seeds_popup.dart | 21 +- lib/store/app_store.dart | 1 - lib/store/dashboard/order_filter_store.dart | 5 +- .../dashboard/payjoin_transactions_store.dart | 3 +- lib/store/dashboard/trade_filter_store.dart | 52 +- lib/store/node_list_store.dart | 1 + lib/store/seed_settings_store.dart | 1 - lib/store/settings_store.dart | 160 +- lib/store/templates/send_template_store.dart | 6 +- lib/store/wallet_list_store.dart | 2 +- lib/themes/core/theme_store.dart | 4 +- .../light_theme_custom_colors.dart | 12 +- lib/tron/cw_tron.dart | 3 +- lib/utils/address_formatter.dart | 15 +- lib/utils/brightness_util.dart | 2 +- lib/utils/clipboard_util.dart | 3 +- lib/utils/date_picker.dart | 26 +- lib/utils/debounce.dart | 6 +- lib/utils/device_info.dart | 4 +- lib/utils/exception_handler.dart | 3 +- lib/utils/feature_flag.dart | 3 +- lib/utils/item_cell.dart | 4 +- lib/utils/list_item.dart | 2 +- lib/utils/list_section.dart | 2 +- lib/utils/mobx.dart | 12 +- lib/utils/package_info.dart | 77 +- lib/utils/tor.dart | 16 +- lib/utils/totp_utils.dart | 2 +- lib/view_model/animated_ur_model.dart | 5 +- lib/view_model/auth_state.dart | 1 - lib/view_model/auth_view_model.dart | 2 +- lib/view_model/backup_view_model.dart | 6 +- .../bridge/bridge_details_view_model.dart | 4 +- lib/view_model/bridge/bridge_view_model.dart | 20 +- lib/view_model/buy/buy_amount_view_model.dart | 4 +- lib/view_model/buy/buy_item.dart | 5 +- lib/view_model/buy/buy_sell_view_model.dart | 60 +- .../cake_pay_buy_card_view_model.dart | 7 +- .../cake_pay_cards_list_view_model.dart | 1 - .../contact_list/contact_view_model.dart | 26 +- .../dashboard/action_list_item.dart | 2 +- .../dashboard/dashboard_view_model.dart | 33 +- lib/view_model/dashboard/filter_item.dart | 19 +- .../dashboard/home_settings_view_model.dart | 24 +- lib/view_model/dashboard/order_list_item.dart | 2 +- .../dashboard/receive_option_view_model.dart | 1 - .../dashboard/transaction_list_item.dart | 4 +- lib/view_model/dashboard/wallet_balance.dart | 2 +- .../dev/background_sync_logs_view_model.dart | 8 +- .../exchange_provider_logs_view_model.dart | 5 +- .../dev/network_requests_view_model.dart | 2 +- lib/view_model/dev/qr_tools_view_model.dart | 26 +- lib/view_model/dev/secure_preferences.dart | 19 +- .../dev/send_network_requests_view_model.dart | 5 +- lib/view_model/dev/shared_preferences.dart | 9 +- .../dev/socket_health_logs_view_model.dart | 1 - .../edit_backup_password_view_model.dart | 9 +- .../exchange/exchange_trade_view_model.dart | 38 +- .../exchange/exchange_view_model.dart | 93 +- .../hardware_wallet/bitbox_view_model.dart | 5 +- .../hardware_wallet_view_model.dart | 1 - .../hardware_wallet/ledger_view_model.dart | 20 +- .../trezor_connect_view_model.dart | 17 +- .../integrations/deuro_view_model.dart | 5 +- .../account_list_item.dart | 3 +- ...ero_account_edit_or_create_view_model.dart | 23 +- .../monero_account_list_view_model.dart | 43 +- .../node_create_or_edit_view_model.dart | 31 +- .../node_list/node_list_view_model.dart | 34 +- .../node_list/pow_node_list_view_model.dart | 1 + .../payjoin_details_view_model.dart | 6 +- lib/view_model/rescan_view_model.dart | 2 +- lib/view_model/restore/restore_mode.dart | 2 +- .../restore_from_backup_view_model.dart | 5 +- lib/view_model/seed_settings_view_model.dart | 3 +- lib/view_model/send/output.dart | 15 +- .../send/send_template_view_model.dart | 4 +- lib/view_model/send/send_view_model.dart | 142 +- .../send/send_view_model_state.dart | 3 + .../settings/connection_sync_view_model.dart | 6 +- .../settings/display_settings_view_model.dart | 5 +- lib/view_model/settings/link_list_item.dart | 12 +- .../settings/other_settings_view_model.dart | 18 +- .../settings/privacy_settings_view_model.dart | 6 +- .../settings/regular_list_item.dart | 2 +- .../security_settings_view_model.dart | 7 +- .../settings/switcher_list_item.dart | 5 +- .../trocador_providers_view_model.dart | 5 +- .../settings/version_list_item.dart | 2 +- lib/view_model/setup_pin_code_view_model.dart | 5 +- lib/view_model/start_tor_view_model.dart | 13 +- lib/view_model/trade_details_view_model.dart | 24 +- .../transaction_details_view_model.dart | 52 +- .../unspent_coins_details_view_model.dart | 3 +- .../unspent_coins/unspent_coins_item.dart | 28 +- .../unspent_coins_list_view_model.dart | 10 +- .../unspent_coins_switch_item.dart | 13 +- .../wallet_account_list_header.dart | 2 +- .../wallet_address_hidden_list_header.dart | 2 +- .../wallet_address_list_item.dart | 30 +- .../wallet_address_list_view_model.dart | 27 +- .../wallet_address_util.dart | 10 +- lib/view_model/wallet_creation_vm.dart | 13 +- .../wallet_groups_display_view_model.dart | 23 +- .../wallet_hardware_restore_view_model.dart | 17 +- .../wallet_list/wallet_list_view_model.dart | 7 +- lib/view_model/wallet_restore_view_model.dart | 26 +- lib/view_model/wallet_seed_view_model.dart | 1 - lib/wallet_type_utils.dart | 18 +- lib/wownero/cw_wownero.dart | 12 +- lib/zano/cw_zano.dart | 34 +- lib/zcash/cw_zcash.dart | 39 +- 779 files changed, 15370 insertions(+), 14559 deletions(-) diff --git a/cw_bitcoin/lib/address_from_output.dart b/cw_bitcoin/lib/address_from_output.dart index 0d985b237..072a92dc2 100644 --- a/cw_bitcoin/lib/address_from_output.dart +++ b/cw_bitcoin/lib/address_from_output.dart @@ -17,21 +17,16 @@ BitcoinBaseAddress addressFromScript(Script script, switch (addressType) { case P2pkhAddressType.p2pkh: - return P2pkhAddress.fromScriptPubkey( - script: script, network: BitcoinNetwork.mainnet); + return P2pkhAddress.fromScriptPubkey(script: script, network: BitcoinNetwork.mainnet); case P2shAddressType.p2pkhInP2sh: case P2shAddressType.p2pkInP2sh: - return P2shAddress.fromScriptPubkey( - script: script, network: BitcoinNetwork.mainnet); + return P2shAddress.fromScriptPubkey(script: script, network: BitcoinNetwork.mainnet); case SegwitAddresType.p2wpkh: - return P2wpkhAddress.fromScriptPubkey( - script: script, network: BitcoinNetwork.mainnet); + return P2wpkhAddress.fromScriptPubkey(script: script, network: BitcoinNetwork.mainnet); case SegwitAddresType.p2wsh: - return P2wshAddress.fromScriptPubkey( - script: script, network: BitcoinNetwork.mainnet); + return P2wshAddress.fromScriptPubkey(script: script, network: BitcoinNetwork.mainnet); case SegwitAddresType.p2tr: - return P2trAddress.fromScriptPubkey( - script: script, network: BitcoinNetwork.mainnet); + return P2trAddress.fromScriptPubkey(script: script, network: BitcoinNetwork.mainnet); } throw ArgumentError("Invalid script"); diff --git a/cw_bitcoin/lib/bitcoin_address_record.dart b/cw_bitcoin/lib/bitcoin_address_record.dart index d6de05051..65b728904 100644 --- a/cw_bitcoin/lib/bitcoin_address_record.dart +++ b/cw_bitcoin/lib/bitcoin_address_record.dart @@ -72,17 +72,16 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord { required super.type, String? scriptHash, required super.network, - }) { + }) { try { this.scriptHash = scriptHash ?? - (network != null ? BitcoinAddressUtils.scriptHash(address, network: network!) : null); + (network != null ? BitcoinAddressUtils.scriptHash(address, network: network!) : null); } catch (e) { printV(e); } -} + } static bool _legacyDefaultForType(BitcoinAddressType type) { - // Some address types (p2wpkh, p2wsh) were historically derived from the same account/path as our new standard // but using legacy formats. For these, we default to legacy = false. if (type == SegwitAddresType.p2wpkh || type == SegwitAddresType.p2wsh) return false; @@ -154,6 +153,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord { } return scriptHash!; } + @override String get derivationPath { if (type == SegwitAddresType.mweb) { @@ -162,9 +162,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord { final coinType = _coinTypeForNetwork(); final purpose = _purposeForType(type); - final accountPath = isLegacyDerivation - ? electrum_path - : "m/$purpose'/$coinType'/0'"; + final accountPath = isLegacyDerivation ? electrum_path : "m/$purpose'/$coinType'/0'"; final chain = isHidden ? 1 : 0; return "$accountPath/$chain/$index"; diff --git a/cw_bitcoin/lib/bitcoin_amount_format.dart b/cw_bitcoin/lib/bitcoin_amount_format.dart index d5a42d984..dd7363569 100644 --- a/cw_bitcoin/lib/bitcoin_amount_format.dart +++ b/cw_bitcoin/lib/bitcoin_amount_format.dart @@ -7,8 +7,8 @@ final bitcoinAmountFormat = NumberFormat() ..maximumFractionDigits = bitcoinAmountLength ..minimumFractionDigits = 1; -String bitcoinAmountToString({required int amount}) => bitcoinAmountFormat.format( - cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider)); +String bitcoinAmountToString({required int amount}) => + bitcoinAmountFormat.format(cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider)); double bitcoinAmountToDouble({required int amount}) => cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider); diff --git a/cw_bitcoin/lib/bitcoin_commit_transaction_exception.dart b/cw_bitcoin/lib/bitcoin_commit_transaction_exception.dart index 7bf488f3f..ffadb65ae 100644 --- a/cw_bitcoin/lib/bitcoin_commit_transaction_exception.dart +++ b/cw_bitcoin/lib/bitcoin_commit_transaction_exception.dart @@ -5,4 +5,3 @@ class BitcoinCommitTransactionException implements Exception { @override String toString() => errorMessage; } - diff --git a/cw_bitcoin/lib/bitcoin_transaction_priority.dart b/cw_bitcoin/lib/bitcoin_transaction_priority.dart index 46e4ca8e4..e38cbce30 100644 --- a/cw_bitcoin/lib/bitcoin_transaction_priority.dart +++ b/cw_bitcoin/lib/bitcoin_transaction_priority.dart @@ -2,7 +2,8 @@ import 'package:cw_core/transaction_priority.dart'; import 'package:flutter/foundation.dart'; class BitcoinTransactionPriority extends TransactionPriority { - const BitcoinTransactionPriority({required String title, required int raw, String? description, String? hint}) + const BitcoinTransactionPriority( + {required String title, required int raw, String? description, String? hint}) : super(title: title, raw: raw, description: description, hint: hint); static const List all = [fast, medium, slow, custom]; @@ -10,10 +11,10 @@ class BitcoinTransactionPriority extends TransactionPriority { BitcoinTransactionPriority(title: 'Slow', description: "2 sat/byte", hint: "~ 24 h", raw: 0); static const BitcoinTransactionPriority medium = BitcoinTransactionPriority(title: 'Medium', description: "3 sat/byte", hint: "~ 1 h", raw: 1); - static const BitcoinTransactionPriority fast = - BitcoinTransactionPriority(title: 'Fast', description: "4 sat/byte", hint: "~ 30 min", raw: 2); + static const BitcoinTransactionPriority fast = BitcoinTransactionPriority( + title: 'Fast', description: "4 sat/byte", hint: "~ 30 min", raw: 2); static const BitcoinTransactionPriority custom = - BitcoinTransactionPriority(title: 'Custom', raw: 3); + BitcoinTransactionPriority(title: 'Custom', raw: 3); static BitcoinTransactionPriority deserialize({required int raw}) { switch (raw) { @@ -116,19 +117,19 @@ class LitecoinTransactionPriority extends BitcoinTransactionPriority { return label; } - } + class BitcoinCashTransactionPriority extends BitcoinTransactionPriority { const BitcoinCashTransactionPriority({required String title, required int raw}) : super(title: title, raw: raw); static const List all = [fast, medium, slow]; static const BitcoinCashTransactionPriority slow = - BitcoinCashTransactionPriority(title: 'Slow', raw: 0); + BitcoinCashTransactionPriority(title: 'Slow', raw: 0); static const BitcoinCashTransactionPriority medium = - BitcoinCashTransactionPriority(title: 'Medium', raw: 1); + BitcoinCashTransactionPriority(title: 'Medium', raw: 1); static const BitcoinCashTransactionPriority fast = - BitcoinCashTransactionPriority(title: 'Fast', raw: 2); + BitcoinCashTransactionPriority(title: 'Fast', raw: 2); static BitcoinCashTransactionPriority deserialize({required int raw}) { switch (raw) { @@ -170,4 +171,3 @@ class BitcoinCashTransactionPriority extends BitcoinTransactionPriority { return label; } } - diff --git a/cw_bitcoin/lib/bitcoin_wallet.dart b/cw_bitcoin/lib/bitcoin_wallet.dart index a0d88ce9a..7e76132a8 100644 --- a/cw_bitcoin/lib/bitcoin_wallet.dart +++ b/cw_bitcoin/lib/bitcoin_wallet.dart @@ -140,7 +140,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { autorun((_) { this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress; }); - + reaction((_) => this.useLightning, (bool useLightning) { if (useLightning && LightningWallet.isAvailable) { if (mnemonic != null) { @@ -306,28 +306,28 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { } return BitcoinWallet( - mnemonic: mnemonic, - xpub: keysData.xPub != null ? convertZpubToXpub(keysData.xPub!) : null, - password: password, - passphrase: passphrase, - walletInfo: walletInfo, - derivationInfo: derivationInfo, - unspentCoinsInfo: unspentCoinsInfo, - initialAddresses: snp?.addresses, - initialSilentAddresses: snp?.silentAddresses, - initialSilentAddressIndex: snp?.silentAddressIndex ?? 0, - initialBalance: snp?.balance, - initialLightningBalance: snp?.lightningBalance, - encryptionFileUtils: encryptionFileUtils, - seedBytes: seedBytes, - initialRegularAddressIndex: snp?.regularAddressIndex, - initialChangeAddressIndex: snp?.changeAddressIndex, - addressPageType: snp?.addressPageType, - networkParam: network, - alwaysScan: snp?.alwaysScan, - useLightning: snp?.useLightning, - cachedLightningAddress: snp?.cachedLightningAddress, - payjoinBox: payjoinBox, + mnemonic: mnemonic, + xpub: keysData.xPub != null ? convertZpubToXpub(keysData.xPub!) : null, + password: password, + passphrase: passphrase, + walletInfo: walletInfo, + derivationInfo: derivationInfo, + unspentCoinsInfo: unspentCoinsInfo, + initialAddresses: snp?.addresses, + initialSilentAddresses: snp?.silentAddresses, + initialSilentAddressIndex: snp?.silentAddressIndex ?? 0, + initialBalance: snp?.balance, + initialLightningBalance: snp?.lightningBalance, + encryptionFileUtils: encryptionFileUtils, + seedBytes: seedBytes, + initialRegularAddressIndex: snp?.regularAddressIndex, + initialChangeAddressIndex: snp?.changeAddressIndex, + addressPageType: snp?.addressPageType, + networkParam: network, + alwaysScan: snp?.alwaysScan, + useLightning: snp?.useLightning, + cachedLightningAddress: snp?.cachedLightningAddress, + payjoinBox: payjoinBox, ); } @@ -346,12 +346,12 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { } try { - final lBalance = await lightningWallet!.getBalance(); + final lBalance = await lightningWallet!.getBalance(); - this.balance[CryptoCurrency.btcln] = ElectrumBalance( - confirmed: lBalance, - unconfirmed: Money.zero(CryptoCurrency.btcln), - frozen: Money.zero(CryptoCurrency.btcln)); + this.balance[CryptoCurrency.btcln] = ElectrumBalance( + confirmed: lBalance, + unconfirmed: Money.zero(CryptoCurrency.btcln), + frozen: Money.zero(CryptoCurrency.btcln)); } catch (e) { printV("Error fetching lightning balance: $e"); } @@ -494,7 +494,9 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { @override Future createTransaction(Object credentials) async { credentials = credentials as BitcoinTransactionCredentials; - final lnAddr = credentials.outputs.first.isParsedAddress ? credentials.outputs.first.extractedAddress! : credentials.outputs.first.address; + final lnAddr = credentials.outputs.first.isParsedAddress + ? credentials.outputs.first.extractedAddress! + : credentials.outputs.first.address; final isLNCompatible = await lightningWallet?.isCompatible(lnAddr); if ((credentials.coinTypeToSpendFrom == UnspentCoinType.lightning && lightningWallet != null) || @@ -506,12 +508,12 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { amount = credentials.outputs.first.cryptoAmount; } - return lightningWallet!.createTransaction( - lnAddr, - amount.amount > BigInt.zero ? amount.amount : null, - credentials.priority, - credentials.outputs.first.sendAll,); + lnAddr, + amount.amount > BigInt.zero ? amount.amount : null, + credentials.priority, + credentials.outputs.first.sendAll, + ); } final tx = (await super.createTransaction(credentials)) as PendingBitcoinTransaction; diff --git a/cw_bitcoin/lib/bitcoin_wallet_addresses.dart b/cw_bitcoin/lib/bitcoin_wallet_addresses.dart index 6abce9689..0bc7f8c9c 100644 --- a/cw_bitcoin/lib/bitcoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/bitcoin_wallet_addresses.dart @@ -137,7 +137,8 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S } @override - bool containsAddress(String address) => super.containsAddress(address) || address == lightningAddress; + bool containsAddress(String address) => + super.containsAddress(address) || address == lightningAddress; @override String get addressForBuy { @@ -153,12 +154,9 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S @override String get addressForExchange { - final current = getFreshAddress(); final availableReceiveAddresses = receiveAddresses.where((element) => - !element.isUsed && - !element.isHidden && - !hiddenAddresses.contains(element.address)); + !element.isUsed && !element.isHidden && !hiddenAddresses.contains(element.address)); final bool isSilentPaymentsPage = addressPageType == SilentPaymentsAddresType.p2sp; final bool isLightningPage = addressPageType == LightningAddressType.p2l; diff --git a/cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart b/cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart index 479cf6416..658969c3e 100644 --- a/cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart +++ b/cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart @@ -59,27 +59,27 @@ class BitcoinRestoreWalletFromWIFCredentials extends WalletCredentials { } class BitcoinWalletFromKeysCredentials extends WalletCredentials { - BitcoinWalletFromKeysCredentials({ - required String name, - required String password, - required this.xpub, - WalletInfo? walletInfo, - super.hardwareWalletType - }) : super(name: name, password: password, walletInfo: walletInfo); + BitcoinWalletFromKeysCredentials( + {required String name, + required String password, + required this.xpub, + WalletInfo? walletInfo, + super.hardwareWalletType}) + : super(name: name, password: password, walletInfo: walletInfo); final String xpub; } class LitecoinWalletFromKeysCredentials extends WalletCredentials { - LitecoinWalletFromKeysCredentials({ - required String name, - required String password, - required this.xpub, - required this.scanSecret, - required this.spendPubkey, - WalletInfo? walletInfo, - super.hardwareWalletType - }) : super(name: name, password: password, walletInfo: walletInfo); + LitecoinWalletFromKeysCredentials( + {required String name, + required String password, + required this.xpub, + required this.scanSecret, + required this.spendPubkey, + WalletInfo? walletInfo, + super.hardwareWalletType}) + : super(name: name, password: password, walletInfo: walletInfo); final String xpub; final String scanSecret; diff --git a/cw_bitcoin/lib/bitcoin_wallet_keys.dart b/cw_bitcoin/lib/bitcoin_wallet_keys.dart index 4ed0da49c..9a9bdcba4 100644 --- a/cw_bitcoin/lib/bitcoin_wallet_keys.dart +++ b/cw_bitcoin/lib/bitcoin_wallet_keys.dart @@ -1,15 +1,12 @@ class BitcoinWalletKeys { - const BitcoinWalletKeys({required this.wif, required this.privateKey, required this.publicKey, required this.xpub}); + const BitcoinWalletKeys( + {required this.wif, required this.privateKey, required this.publicKey, required this.xpub}); final String wif; final String privateKey; final String publicKey; final String xpub; - Map toJson() => { - 'wif': wif, - 'privateKey': privateKey, - 'publicKey': publicKey, - 'xpub': xpub - }; -} \ No newline at end of file + Map toJson() => + {'wif': wif, 'privateKey': privateKey, 'publicKey': publicKey, 'xpub': xpub}; +} diff --git a/cw_bitcoin/lib/bitcoin_wallet_service.dart b/cw_bitcoin/lib/bitcoin_wallet_service.dart index 8a034d8cf..836b006d2 100644 --- a/cw_bitcoin/lib/bitcoin_wallet_service.dart +++ b/cw_bitcoin/lib/bitcoin_wallet_service.dart @@ -21,8 +21,7 @@ class BitcoinWalletService extends WalletService< BitcoinRestoreWalletFromSeedCredentials, BitcoinWalletFromKeysCredentials, BitcoinRestoreWalletFromHardware> { - BitcoinWalletService(this.unspentCoinsInfoSource, - this.payjoinSessionSource, this.isDirect); + BitcoinWalletService(this.unspentCoinsInfoSource, this.payjoinSessionSource, this.isDirect); final Box unspentCoinsInfoSource; final Box payjoinSessionSource; @@ -38,9 +37,12 @@ class BitcoinWalletService extends WalletService< final String mnemonic; final derivationInfo = await credentials.walletInfo!.getDerivationInfo(); - derivationInfo.derivationType = credentials.derivationInfo?.derivationType ?? derivationInfo.derivationType; - derivationInfo.derivationPath = credentials.derivationInfo?.derivationPath ?? derivationInfo.derivationPath; - derivationInfo.description = credentials.derivationInfo?.description ?? derivationInfo.description; + derivationInfo.derivationType = + credentials.derivationInfo?.derivationType ?? derivationInfo.derivationType; + derivationInfo.derivationPath = + credentials.derivationInfo?.derivationPath ?? derivationInfo.derivationPath; + derivationInfo.description = + credentials.derivationInfo?.description ?? derivationInfo.description; derivationInfo.scriptType = credentials.derivationInfo?.scriptType ?? derivationInfo.scriptType; await derivationInfo.save(); switch (derivationInfo.derivationType) { @@ -119,8 +121,9 @@ class BitcoinWalletService extends WalletService< } await WalletInfo.delete(walletInfo); - final unspentCoinsToDelete = unspentCoinsInfoSource.values.where( - (unspentCoin) => unspentCoin.walletId == walletInfo.id).toList(); + final unspentCoinsToDelete = unspentCoinsInfoSource.values + .where((unspentCoin) => unspentCoin.walletId == walletInfo.id) + .toList(); final keysToDelete = unspentCoinsToDelete.map((unspentCoin) => unspentCoin.key).toList(); @@ -135,11 +138,10 @@ class BitcoinWalletService extends WalletService< final network = isTestnet == true ? BitcoinNetwork.testnet : BitcoinNetwork.mainnet; credentials.walletInfo?.network = network.value; final derivationInfo = await credentials.walletInfo!.getDerivationInfo(); - derivationInfo.derivationPath = - credentials.hwAccountData.derivationPath; - + derivationInfo.derivationPath = credentials.hwAccountData.derivationPath; + final xpub = convertAnyToXpub(credentials.hwAccountData.xpub!); - + await credentials.walletInfo!.save(); final wallet = await BitcoinWallet( password: credentials.password!, diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart index 052d18ef0..44aa096c4 100644 --- a/cw_bitcoin/lib/electrum.dart +++ b/cw_bitcoin/lib/electrum.dart @@ -304,9 +304,9 @@ class ElectrumClient { }); Future>>> getBatchHistory( - List scriptHashes, { - int timeout = 10000, - }) async { + List scriptHashes, { + int timeout = 10000, + }) async { final paramsList = scriptHashes.map((h) => [h]).toList(growable: false); final batchResults = await callBatchWithTimeout( @@ -342,9 +342,9 @@ class ElectrumClient { } Future>>> getBatchUnspent( - List scriptHashes, { - int timeout = 10000, - }) async { + List scriptHashes, { + int timeout = 10000, + }) async { final paramsList = scriptHashes.map((h) => [h]).toList(growable: false); final batchResults = await callBatchWithTimeout( @@ -380,9 +380,9 @@ class ElectrumClient { } Future>> getBatchBalance( - List scriptHashes, { - int timeout = 10000, - }) async { + List scriptHashes, { + int timeout = 10000, + }) async { final paramsList = scriptHashes.map((h) => [h]).toList(growable: false); final batchResults = await callBatchWithTimeout( @@ -416,9 +416,9 @@ class ElectrumClient { } Future>> getBatchTransactionVerbose( - List hashes, { - int timeout = 10000, - }) async { + List hashes, { + int timeout = 10000, + }) async { final result = >{}; if (hashes.isEmpty) return result; @@ -443,9 +443,9 @@ class ElectrumClient { } Future> getBatchTransactionHex( - List hashes, { - int timeout = 10000, - }) async { + List hashes, { + int timeout = 10000, + }) async { final result = {}; if (hashes.isEmpty) return result; @@ -483,12 +483,8 @@ class ElectrumClient { // Build the Batch Array final List> batchPayload = []; for (int i = 0; i < paramsList.length; i++) { - batchPayload.add({ - "jsonrpc": "2.0", - "method": method, - "params": paramsList[i], - "id": "$batchBaseId-$i" - }); + batchPayload.add( + {"jsonrpc": "2.0", "method": method, "params": paramsList[i], "id": "$batchBaseId-$i"}); } // Register the task @@ -775,7 +771,6 @@ class ElectrumClient { } void _handleResponse(dynamic response) { - // Handle batch response if (response is List) { if (response.isEmpty) return; diff --git a/cw_bitcoin/lib/electrum_transaction_history.dart b/cw_bitcoin/lib/electrum_transaction_history.dart index 6457832ec..5de131c77 100644 --- a/cw_bitcoin/lib/electrum_transaction_history.dart +++ b/cw_bitcoin/lib/electrum_transaction_history.dart @@ -1,6 +1,5 @@ import 'dart:convert'; - import 'package:cw_bitcoin/electrum_transaction_info.dart'; import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/pathForWallet.dart'; diff --git a/cw_bitcoin/lib/electrum_transaction_info.dart b/cw_bitcoin/lib/electrum_transaction_info.dart index a58829e00..48c006ae2 100644 --- a/cw_bitcoin/lib/electrum_transaction_info.dart +++ b/cw_bitcoin/lib/electrum_transaction_info.dart @@ -184,15 +184,13 @@ class ElectrumTransactionInfo extends TransactionInfo { // MWEB HogEx final isHogExTx = (BtcTransaction tx) { - if (tx.inputs.isEmpty || tx.inputs.first.txIndex > 0 || tx.outputs.isEmpty) - return false; + if (tx.inputs.isEmpty || tx.inputs.first.txIndex > 0 || tx.outputs.isEmpty) return false; final b = tx.outputs.first.scriptPubKey.toBytes(); return b.length == 34 && b[0] == 88 && b[1] == 32; }; final firstInput = bundle.ins.isNotEmpty ? bundle.ins.first : null; - final isHogEx = firstInput != null && - isHogExTx(bundle.originalTransaction) && - isHogExTx(firstInput); + final isHogEx = + firstInput != null && isHogExTx(bundle.originalTransaction) && isHogExTx(firstInput); final fee = hasMissingInputTx ? null : inputAmount - totalOutAmount; final walletCurrency = walletTypeToCryptoCurrency(type); diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index ccd41d010..bc7d45ec4 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -156,7 +156,6 @@ abstract class ElectrumWalletBase sideHdByType[type] = sideHd; } } - } int _purposeForType(BitcoinAddressType type) { @@ -195,7 +194,6 @@ abstract class ElectrumWalletBase /// For LEGACY addresses, returns the wallet's legacy derivation base (derivationInfo.derivationPath) /// which is already the account path used historically (e.g. m/0' or m/84'/0'/0'). String _accountDerivationPathForRecord(BaseBitcoinAddressRecord record) { - if (derivationInfo.derivationType == DerivationType.electrum) { return derivationInfo.derivationPath ?? electrum_path; // m/0' } @@ -807,7 +805,7 @@ abstract class ElectrumWalletBase node!.isElectrs = true; // TODO figure out why condition was needed // if (node!.isInBox) { - node!.save(); + node!.save(); // } return node!.isElectrs!; } @@ -873,7 +871,8 @@ abstract class ElectrumWalletBase BigInt get networkDustAmount => BigInt.from(546); - bool _isBelowDust(BigInt amount) => amount <= networkDustAmount && network != BitcoinNetwork.testnet; + bool _isBelowDust(BigInt amount) => + amount <= networkDustAmount && network != BitcoinNetwork.testnet; UtxoDetails _createUTXOS({ required bool sendAll, @@ -961,8 +960,7 @@ abstract class ElectrumWalletBase final baseDerivationPath = _accountDerivationPathForRecord(utx.bitcoinAddressRecord); - final derivationPath = - "${_hardenedDerivationPath(baseDerivationPath)}" + final derivationPath = "${_hardenedDerivationPath(baseDerivationPath)}" "/${utx.bitcoinAddressRecord.isHidden ? "1" : "0"}" "/${utx.bitcoinAddressRecord.index}"; publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath(pubKeyHex, derivationPath); @@ -1139,7 +1137,8 @@ abstract class ElectrumWalletBase utxoDetails.utxos.length == utxoDetails.availableInputs.length - utxoDetails.unconfirmedCoins.length; - final amountLeftForChangeAndFee = utxoDetails.allInputsAmount - credentialsAmount.amount.toInt(); + final amountLeftForChangeAndFee = + utxoDetails.allInputsAmount - credentialsAmount.amount.toInt(); if (amountLeftForChangeAndFee <= 0) { if (!spendingAllCoins) { @@ -1175,11 +1174,9 @@ abstract class ElectrumWalletBase isChange: true, )); - // Must match the address' account root (purpose/coinType) and legacy derivation when applicable. final changeBaseDerivationPath = _accountDerivationPathForRecord(changeAddress); - final changeDerivationPath = - "${_hardenedDerivationPath(changeBaseDerivationPath)}" + final changeDerivationPath = "${_hardenedDerivationPath(changeBaseDerivationPath)}" "/${changeAddress.isHidden ? "1" : "0"}" "/${changeAddress.index}"; utxoDetails.publicKeys[address.pubKeyHash()] = @@ -1242,7 +1239,8 @@ abstract class ElectrumWalletBase inputPrivKeyInfos: utxoDetails.inputPrivKeyInfos, vinOutpoints: utxoDetails.vinOutpoints, ); - final leftover = utxoDetails.allInputsAmount - credentialsAmount.amount.toInt() - feeNoChange; + final leftover = + utxoDetails.allInputsAmount - credentialsAmount.amount.toInt() - feeNoChange; if (leftover >= 0) { final finalFee = feeNoChange + leftover; // absorb tiny remainder @@ -1827,16 +1825,15 @@ abstract class ElectrumWalletBase } Future?>> _fetchUnspentsRegular( - List addresses, - ) async { + List addresses, + ) async { final addressFutures = addresses.map((address) => fetchUnspent(address)).toList(); return Future.wait(addressFutures); } - Future?>> _fetchUnspentsBatch( - List addresses, - ) async { + List addresses, + ) async { final byScriptHash = { for (final address in addresses) address.getScriptHash(network): address, }; @@ -1845,7 +1842,7 @@ abstract class ElectrumWalletBase try { final unspentByScriptHash = - await _processChunksToMap>>( + await _processChunksToMap>>( items: scriptHashes, chunkSize: addressHistoryChunkSize, processChunk: _getListUnspentBatch, @@ -2131,10 +2128,7 @@ abstract class ElectrumWalletBase final hd = _hdFor(record: addressRecord); - final privkey = generateECPrivate( - hd: hd, - index: addressRecord.index, - network: network); + final privkey = generateECPrivate(hd: hd, index: addressRecord.index, network: network); privateKeys.add(privkey); @@ -2197,8 +2191,7 @@ abstract class ElectrumWalletBase final deduction = (outputAmount - networkDustAmount >= remainingFee) ? remainingFee : outputAmount - networkDustAmount; - outputs[i] = BitcoinOutput( - address: output.address, value: outputAmount - deduction); + outputs[i] = BitcoinOutput(address: output.address, value: outputAmount - deduction); remainingFee -= deduction; if (remainingFee <= BigInt.zero) break; @@ -2464,7 +2457,8 @@ abstract class ElectrumWalletBase @override Future> fetchTransactions() async { try { - final Map historiesWithDetails = {};; + final Map historiesWithDetails = {}; + ; printV('[BATCH_TEST] Fetching transactions with batch: $shouldUseBatchFetching'); @@ -2473,19 +2467,16 @@ abstract class ElectrumWalletBase ? fetchTransactionsForAddressTypeBatch(historiesWithDetails, type) : fetchTransactionsForAddressType(historiesWithDetails, type))); } else if (type == WalletType.bitcoinCash) { - await Future.wait(BITCOIN_CASH_ADDRESS_TYPES - .map((type) => shouldUseBatchFetching + await Future.wait(BITCOIN_CASH_ADDRESS_TYPES.map((type) => shouldUseBatchFetching ? fetchTransactionsForAddressTypeBatch(historiesWithDetails, type) : fetchTransactionsForAddressType(historiesWithDetails, type))); } else if (type == WalletType.litecoin) { - await Future.wait(LITECOIN_ADDRESS_TYPES - .where((type) => type != SegwitAddresType.mweb) - .map((type) => shouldUseBatchFetching - ? fetchTransactionsForAddressTypeBatch(historiesWithDetails, type) - : fetchTransactionsForAddressType(historiesWithDetails, type))); + await Future.wait(LITECOIN_ADDRESS_TYPES.where((type) => type != SegwitAddresType.mweb).map( + (type) => shouldUseBatchFetching + ? fetchTransactionsForAddressTypeBatch(historiesWithDetails, type) + : fetchTransactionsForAddressType(historiesWithDetails, type))); } else if (type == WalletType.dogecoin) { - await Future.wait(DOGECOIN_ADDRESS_TYPES - .map((type) => shouldUseBatchFetching + await Future.wait(DOGECOIN_ADDRESS_TYPES.map((type) => shouldUseBatchFetching ? fetchTransactionsForAddressTypeBatch(historiesWithDetails, type) : fetchTransactionsForAddressType(historiesWithDetails, type))); } @@ -2633,8 +2624,7 @@ abstract class ElectrumWalletBase } Future fetchTransactionsForAddressTypeBatch( - Map historiesWithDetails, - BitcoinAddressType type) async { + Map historiesWithDetails, BitcoinAddressType type) async { final addressesByType = walletAddresses.allAddresses.where((addr) => addr.type == type).toList(); final receiveAddresses = addressesByType.where((addr) => !addr.isHidden).toList(); @@ -2676,14 +2666,13 @@ abstract class ElectrumWalletBase ); } - Future fetchTransactionsForAddressesBranchBatch( - Map historiesWithDetails, - BitcoinAddressType type, - List branchAddresses, { - required bool isHidden, - required bool isLegacyDerivation, - }) async { + Map historiesWithDetails, + BitcoinAddressType type, + List branchAddresses, { + required bool isHidden, + required bool isLegacyDerivation, + }) async { if (branchAddresses.isEmpty) return; final tip = await getCurrentChainTip(); @@ -2714,7 +2703,6 @@ abstract class ElectrumWalletBase if (!shouldDiscover) return; - final newAddresses = await walletAddresses.discoverAddressesBatch( currentBranch, isHidden, @@ -2756,9 +2744,7 @@ abstract class ElectrumWalletBase } Future> _fetchBatchAddressHistory( - List addressRecords, - int? currentHeight, - int historyChunkSize) async { + List addressRecords, int? currentHeight, int historyChunkSize) async { String lastTxId = ''; bool didUpdateHistory = false; @@ -2769,11 +2755,8 @@ abstract class ElectrumWalletBase final scriptHashes = addressRecords.map((a) => a.getScriptHash(network)).toList(); final historyByScriptHash = - await _processChunksToMap>>( - items: scriptHashes, - chunkSize: historyChunkSize, - processChunk: _getHistoryBatch - ); + await _processChunksToMap>>( + items: scriptHashes, chunkSize: historyChunkSize, processChunk: _getHistoryBatch); // Map scriptHash -> addressRecord final byScriptHash = {}; @@ -2869,8 +2852,7 @@ abstract class ElectrumWalletBase .toList(growable: false); final heightsByHash = { - for (final e in chunkHistory) - (e['tx_hash'] as String): (e['height'] as int?), + for (final e in chunkHistory) (e['tx_hash'] as String): (e['height'] as int?), }; final infosByHash = await fetchTransactionInfoBatch( @@ -2909,40 +2891,35 @@ abstract class ElectrumWalletBase } } - Future>> _getTransactionVerboseBatch( - List hashes) { + Future>> _getTransactionVerboseBatch(List hashes) { return electrumClient.getBatchTransactionVerbose( hashes, timeout: transactionBatchTimeoutMs, ); } - Future> _getTransactionHexBatch( - List hashes) { + Future> _getTransactionHexBatch(List hashes) { return electrumClient.getBatchTransactionHex( hashes, timeout: transactionBatchTimeoutMs, ); } - Future>>> _getHistoryBatch( - List scriptHashes) { + Future>>> _getHistoryBatch(List scriptHashes) { return electrumClient.getBatchHistory( scriptHashes, timeout: transactionBatchTimeoutMs, ); } - Future>>> _getListUnspentBatch( - List scriptHashes) { + Future>>> _getListUnspentBatch(List scriptHashes) { return electrumClient.getBatchUnspent( scriptHashes, timeout: transactionBatchTimeoutMs, ); } - Future>> _getBalanceBatch( - List scriptHashes) { + Future>> _getBalanceBatch(List scriptHashes) { return electrumClient.getBatchBalance( scriptHashes, timeout: transactionBatchTimeoutMs, @@ -2956,8 +2933,7 @@ abstract class ElectrumWalletBase Duration retryDelay = const Duration(seconds: 2), }) async { final result = {}; - final uniqueHashes = - hashes.map((h) => h.trim()).where((h) => h.isNotEmpty).toSet().toList(); + final uniqueHashes = hashes.map((h) => h.trim()).where((h) => h.isNotEmpty).toSet().toList(); if (uniqueHashes.isEmpty) return result; @@ -2990,9 +2966,8 @@ abstract class ElectrumWalletBase required Map? heightsByHash, }) async { for (var i = 0; i < txIds.length; i += transactionChunkSize) { - final end = (i + transactionChunkSize < txIds.length) - ? i + transactionChunkSize - : txIds.length; + final end = + (i + transactionChunkSize < txIds.length) ? i + transactionChunkSize : txIds.length; final chunk = txIds.sublist(i, end); final bundlesByHash = await getTransactionExpandedBatch( @@ -3024,9 +2999,8 @@ abstract class ElectrumWalletBase } } - Future> getTransactionExpandedBatch({ - required List hashes, - Map? heightsByHash}) async { + Future> getTransactionExpandedBatch( + {required List hashes, Map? heightsByHash}) async { final bundles = {}; if (hashes.isEmpty) return bundles; @@ -3062,8 +3036,8 @@ abstract class ElectrumWalletBase Future>> _fetchTransactionVerboseBatch( List txIds) async { - - final verboseTransactionByHash = await _processChunksToMap>( + final verboseTransactionByHash = + await _processChunksToMap>( items: txIds, chunkSize: transactionChunkSize, processChunk: _getTransactionVerboseBatch, @@ -3100,8 +3074,8 @@ abstract class ElectrumWalletBase } Map _parseTransactions( - Map> verboseByHash, - ) { + Map> verboseByHash, + ) { final result = {}; for (final entry in verboseByHash.entries) { @@ -3116,10 +3090,9 @@ abstract class ElectrumWalletBase return result; } - Map> _collectInputTxIdsByHash( - Map originalByHash, - ) { + Map originalByHash, + ) { final inputTxIdsByHash = >{}; for (final entry in originalByHash.entries) { @@ -3201,8 +3174,8 @@ abstract class ElectrumWalletBase } Future> _fetchBlockTimestampsFromMempoolByHeights( - Set heights, - ) async { + Set heights, + ) async { final out = {}; if (heights.isEmpty) return out; if (!(await checkIfMempoolAPIIsEnabled())) return out; @@ -3212,10 +3185,10 @@ abstract class ElectrumWalletBase try { final blockHashResp = await ProxyWrapper() .get( - clearnetUri: Uri.parse( - 'https://mempool.cakewallet.com/api/v1/block-height/$h', - ), - ) + clearnetUri: Uri.parse( + 'https://mempool.cakewallet.com/api/v1/block-height/$h', + ), + ) .timeout(const Duration(seconds: 15)); if (blockHashResp.statusCode != 200 || blockHashResp.body.isEmpty) return; @@ -3225,10 +3198,10 @@ abstract class ElectrumWalletBase final blockResp = await ProxyWrapper() .get( - clearnetUri: Uri.parse( - 'https://mempool.cakewallet.com/api/v1/block/$blockHash', - ), - ) + clearnetUri: Uri.parse( + 'https://mempool.cakewallet.com/api/v1/block/$blockHash', + ), + ) .timeout(const Duration(seconds: 15)); if (blockResp.statusCode != 200 || blockResp.body.isEmpty) return; @@ -3276,7 +3249,6 @@ abstract class ElectrumWalletBase return result; } - Future updateTransactions() async { printV("updateTransactions() called!"); try { @@ -3368,8 +3340,7 @@ abstract class ElectrumWalletBase } try { - final balancesByScriptHash = - await _processChunksToMap>( + final balancesByScriptHash = await _processChunksToMap>( items: scriptHashes, chunkSize: addressHistoryChunkSize, processChunk: _getBalanceBatch, @@ -3415,7 +3386,8 @@ abstract class ElectrumWalletBase ? await fetchBalancesBatch(addresses) : await fetchBalancesRegular(addresses); - printV('Fetched balances for ${addresses.length} addresses. Batch fetching: $shouldUseBatchFetching'); + printV( + 'Fetched balances for ${addresses.length} addresses. Batch fetching: $shouldUseBatchFetching'); var totalFrozen = 0; var totalConfirmed = 0; @@ -3544,17 +3516,14 @@ abstract class ElectrumWalletBase // that matches this received transaction, mark it as being from a peg out: for (final tx2 in transactionHistory.transactions.values) { final heightDiff = ((tx2.height ?? 0) - (tx.height ?? 0)).abs(); - // this isn't a perfect matching algorithm since we don't have the right input/output information from these transaction models (the addresses are in different formats), but this should be more than good enough for now as it's extremely unlikely a user receives the EXACT same amount from 2 different sources and one of them is a peg out and the other isn't WITHIN 5 blocks of each other - if (tx2.additionalInfo["isPegOut"] == true && - tx2.amount == tx.amount && - heightDiff <= 5) { + // this isn't a perfect matching algorithm since we don't have the right input/output information from these transaction models (the addresses are in different formats), but this should be more than good enough for now as it's extremely unlikely a user receives the EXACT same amount from 2 different sources and one of them is a peg out and the other isn't WITHIN 5 blocks of each other + if (tx2.additionalInfo["isPegOut"] == true && tx2.amount == tx.amount && heightDiff <= 5) { tx.additionalInfo["fromPegOut"] = true; } } } Future checkIfBatchSupported() async { - if (_isBatchSupported != null) { printV('[BATCH_TEST] Already checked: $_isBatchSupported'); return; @@ -3580,9 +3549,7 @@ abstract class ElectrumWalletBase ); final hasError = result.any((item) => - item is Map && - item.containsKey('error') && - item['error'] != null); + item is Map && item.containsKey('error') && item['error'] != null); if (hasError) { _isBatchSupported = false; diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index 239fa491d..239977ecb 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -195,13 +195,12 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { } if (addressPageType == LightningAddressType.p2l) { - return lightningAddress ?? - "Error: Unable to fetch your Lightning address, please check your network connection."; + return lightningAddress ?? + "Error: Unable to fetch your Lightning address, please check your network connection."; } - final typeMatchingAddressesAll = _addresses - .where((addr) => !addr.isHidden && _isAddressPageTypeMatch(addr)) - .toList(); + final typeMatchingAddressesAll = + _addresses.where((addr) => !addr.isHidden && _isAddressPageTypeMatch(addr)).toList(); // Prefer standard derivation addresses for the current/active address, // but keep legacy addresses present in the overall address lists. @@ -210,8 +209,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { ...typeMatchingAddressesAll.where((a) => a.isLegacyDerivation), ]; - final typeMatchingReceiveAddressesAll = - typeMatchingAddressesAll.where((addr) => !addr.isUsed && !hiddenAddresses.contains(addr.address)).toList(); + final typeMatchingReceiveAddressesAll = typeMatchingAddressesAll + .where((addr) => !addr.isUsed && !hiddenAddresses.contains(addr.address)) + .toList(); final typeMatchingReceiveAddresses = [ ...typeMatchingReceiveAddressesAll.where((a) => !a.isLegacyDerivation), ...typeMatchingReceiveAddressesAll.where((a) => a.isLegacyDerivation), @@ -354,34 +354,35 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { return acc; }); - @override - Future init() async { - if (walletInfo.type == WalletType.bitcoinCash) { - await _generateInitialAddresses(type: P2pkhAddressType.p2pkh); - } else if (walletInfo.type == WalletType.litecoin) { - await _generateInitialAddresses(type: SegwitAddresType.p2wpkh); - if ((Platform.isAndroid || Platform.isIOS) && !isHardwareWallet) { - await _generateInitialAddresses(type: SegwitAddresType.mweb); - } - } else if (walletInfo.type == WalletType.dogecoin) { - await _generateInitialAddresses(type: P2pkhAddressType.p2pkh); - } else if (walletInfo.type == WalletType.bitcoin) { - await _generateInitialAddresses(isLegacyDerivation: true); - await _generateInitialAddresses(); - if (!isHardwareWallet) { - await _generateInitialAddresses(type: P2pkhAddressType.p2pkh, isLegacyDerivation: true); - await _generateInitialAddresses(type: P2pkhAddressType.p2pkh); - - await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh, isLegacyDerivation: true); - await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh); - - await _generateInitialAddresses(type: SegwitAddresType.p2tr, isLegacyDerivation: true); - await _generateInitialAddresses(type: SegwitAddresType.p2tr); - - await _generateInitialAddresses(type: SegwitAddresType.p2wsh, isLegacyDerivation: true); - await _generateInitialAddresses(type: SegwitAddresType.p2wsh); - } + @override + Future init() async { + if (walletInfo.type == WalletType.bitcoinCash) { + await _generateInitialAddresses(type: P2pkhAddressType.p2pkh); + } else if (walletInfo.type == WalletType.litecoin) { + await _generateInitialAddresses(type: SegwitAddresType.p2wpkh); + if ((Platform.isAndroid || Platform.isIOS) && !isHardwareWallet) { + await _generateInitialAddresses(type: SegwitAddresType.mweb); } + } else if (walletInfo.type == WalletType.dogecoin) { + await _generateInitialAddresses(type: P2pkhAddressType.p2pkh); + } else if (walletInfo.type == WalletType.bitcoin) { + await _generateInitialAddresses(isLegacyDerivation: true); + await _generateInitialAddresses(); + if (!isHardwareWallet) { + await _generateInitialAddresses(type: P2pkhAddressType.p2pkh, isLegacyDerivation: true); + await _generateInitialAddresses(type: P2pkhAddressType.p2pkh); + + await _generateInitialAddresses( + type: P2shAddressType.p2wpkhInP2sh, isLegacyDerivation: true); + await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh); + + await _generateInitialAddresses(type: SegwitAddresType.p2tr, isLegacyDerivation: true); + await _generateInitialAddresses(type: SegwitAddresType.p2tr); + + await _generateInitialAddresses(type: SegwitAddresType.p2wsh, isLegacyDerivation: true); + await _generateInitialAddresses(type: SegwitAddresType.p2wsh); + } + } updateAddressesByMatch(); updateReceiveAddresses(); @@ -680,9 +681,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { @action void updateReceiveAddresses() { receiveAddresses.removeRange(0, receiveAddresses.length); - final newAddresses = _addresses.where((addressRecord) => - !addressRecord.isHidden && - !addressRecord.isUsed); + final newAddresses = + _addresses.where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed); receiveAddresses.addAll(newAddresses); } @@ -699,12 +699,12 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { @action Future discoverAddresses( - List addressList, - bool isHidden, - Future Function(BitcoinAddressRecord) getAddressHistory, { - BitcoinAddressType type = SegwitAddresType.p2wpkh, - required bool isLegacyDerivation, - }) async { + List addressList, + bool isHidden, + Future Function(BitcoinAddressRecord) getAddressHistory, { + BitcoinAddressType type = SegwitAddresType.p2wpkh, + required bool isLegacyDerivation, + }) async { final newAddresses = await _createNewAddresses( gap, startIndex: addressList.length, @@ -716,8 +716,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { addAddresses(newAddresses); addressList.addAll(newAddresses); - final addressesWithHistory = - await Future.wait(newAddresses.map(getAddressHistory)); + final addressesWithHistory = await Future.wait(newAddresses.map(getAddressHistory)); final isLastAddressUsed = addressesWithHistory.last != null; if (isLastAddressUsed) { @@ -733,12 +732,12 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { @action Future> discoverAddressesBatch( - List addressList, - bool isHidden, - Future> Function(List) getUsedAddresses, { - BitcoinAddressType type = SegwitAddresType.p2wpkh, - required bool isLegacyDerivation, - }) async { + List addressList, + bool isHidden, + Future> Function(List) getUsedAddresses, { + BitcoinAddressType type = SegwitAddresType.p2wpkh, + required bool isLegacyDerivation, + }) async { final newAddresses = await _createNewAddresses( gap, startIndex: addressList.length, @@ -750,8 +749,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { final usedAddresses = await getUsedAddresses(newAddresses); - final hasUsedAddressInGap = newAddresses.any( - (addressRecord) => usedAddresses.contains(addressRecord.address)); + final hasUsedAddressInGap = + newAddresses.any((addressRecord) => usedAddresses.contains(addressRecord.address)); if (!hasUsedAddressInGap) { return newAddresses; @@ -770,64 +769,69 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { return [...newAddresses, ...moreNewAddresses]; } - Future _generateInitialAddresses( - {BitcoinAddressType type = SegwitAddresType.p2wpkh, - bool isLegacyDerivation = false }) async { - - // Legacy derivation produces the same addresses as standard for these types. - // Don't generate a legacy set to avoid duplicates. - if (isLegacyDerivation && (type == SegwitAddresType.p2wpkh || type == SegwitAddresType.p2wsh)) { - return; - } - - var countOfReceiveAddresses = 0; - var countOfHiddenAddresses = 0; - - _addresses.forEach((addr) { - if (addr.type == type && addr.isLegacyDerivation == isLegacyDerivation) { - if (addr.isHidden) { - countOfHiddenAddresses += 1; - } else { - countOfReceiveAddresses += 1; - } - } - }); - - if (countOfReceiveAddresses < defaultReceiveAddressesCount) { - final addressesCount = defaultReceiveAddressesCount - countOfReceiveAddresses; - final newAddresses = await _createNewAddresses(addressesCount, - startIndex: countOfReceiveAddresses, isHidden: false, type: type, isLegacyDerivation: isLegacyDerivation); - addAddresses(newAddresses); - } - - if (countOfHiddenAddresses < defaultChangeAddressesCount) { - final addressesCount = defaultChangeAddressesCount - countOfHiddenAddresses; - final newAddresses = await _createNewAddresses(addressesCount, - startIndex: countOfHiddenAddresses, isHidden: true, type: type, isLegacyDerivation: isLegacyDerivation); - addAddresses(newAddresses); - } + {BitcoinAddressType type = SegwitAddresType.p2wpkh, bool isLegacyDerivation = false}) async { + // Legacy derivation produces the same addresses as standard for these types. + // Don't generate a legacy set to avoid duplicates. + if (isLegacyDerivation && (type == SegwitAddresType.p2wpkh || type == SegwitAddresType.p2wsh)) { + return; } - Future> _createNewAddresses(int count, - {int startIndex = 0, bool isHidden = false, BitcoinAddressType? type, bool isLegacyDerivation = false}) async { - final list = []; + var countOfReceiveAddresses = 0; + var countOfHiddenAddresses = 0; - for (var i = startIndex; i < count + startIndex; i++) { - - final addrType = type ?? addressPageType; - final hd = _hdFor(isHidden: isHidden, type: addrType, isLegacyDerivation: isLegacyDerivation); - - final address = BitcoinAddressRecord( - await getAddressAsync(index: i, hd: hd, addressType: addrType), - index: i, - isHidden: isHidden, - isLegacyDerivation: isLegacyDerivation, - type: addrType, - network: network, - ); - list.add(address); + _addresses.forEach((addr) { + if (addr.type == type && addr.isLegacyDerivation == isLegacyDerivation) { + if (addr.isHidden) { + countOfHiddenAddresses += 1; + } else { + countOfReceiveAddresses += 1; + } } + }); + + if (countOfReceiveAddresses < defaultReceiveAddressesCount) { + final addressesCount = defaultReceiveAddressesCount - countOfReceiveAddresses; + final newAddresses = await _createNewAddresses(addressesCount, + startIndex: countOfReceiveAddresses, + isHidden: false, + type: type, + isLegacyDerivation: isLegacyDerivation); + addAddresses(newAddresses); + } + + if (countOfHiddenAddresses < defaultChangeAddressesCount) { + final addressesCount = defaultChangeAddressesCount - countOfHiddenAddresses; + final newAddresses = await _createNewAddresses(addressesCount, + startIndex: countOfHiddenAddresses, + isHidden: true, + type: type, + isLegacyDerivation: isLegacyDerivation); + addAddresses(newAddresses); + } + } + + Future> _createNewAddresses(int count, + {int startIndex = 0, + bool isHidden = false, + BitcoinAddressType? type, + bool isLegacyDerivation = false}) async { + final list = []; + + for (var i = startIndex; i < count + startIndex; i++) { + final addrType = type ?? addressPageType; + final hd = _hdFor(isHidden: isHidden, type: addrType, isLegacyDerivation: isLegacyDerivation); + + final address = BitcoinAddressRecord( + await getAddressAsync(index: i, hd: hd, addressType: addrType), + index: i, + isHidden: isHidden, + isLegacyDerivation: isLegacyDerivation, + type: addrType, + network: network, + ); + list.add(address); + } return list; } @@ -868,8 +872,10 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { return; } - final mainHd = _hdFor(isHidden: false, type: element.type, isLegacyDerivation: element.isLegacyDerivation); - final sideHd = _hdFor(isHidden: true, type: element.type, isLegacyDerivation: element.isLegacyDerivation); + final mainHd = _hdFor( + isHidden: false, type: element.type, isLegacyDerivation: element.isLegacyDerivation); + final sideHd = _hdFor( + isHidden: true, type: element.type, isLegacyDerivation: element.isLegacyDerivation); if (!element.isHidden && element.address != await getAddressAsync(index: element.index, hd: mainHd, addressType: element.type)) { @@ -897,7 +903,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { return _isAddressByType(addressRecord, addressPageType); } - bool _isAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) => addr.type == type; + bool _isAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) => addr.type == type; bool _isUnusedReceiveAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) => !addr.isHidden && !addr.isUsed && addr.type == type; @@ -907,9 +913,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { final addressRecord = silentAddresses.firstWhere((addressRecord) => addressRecord.type == SilentPaymentsAddresType.p2sp && addressRecord.address == address); - silentAddresses.remove(addressRecord); - updateAddressesByMatch(); - } + silentAddresses.remove(addressRecord); + updateAddressesByMatch(); + } Bip32Slip10Secp256k1 _hdFor({ required bool isHidden, @@ -923,7 +929,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { if (hd == null) throw Exception("HD not found for type $type"); return hd; } - + @action Future setLightningAddress(String walletName, {String newAddress = ""}) async { if (lightningWallet == null) return; diff --git a/cw_bitcoin/lib/exceptions.dart b/cw_bitcoin/lib/exceptions.dart index 9bdb66eef..d43ce823a 100644 --- a/cw_bitcoin/lib/exceptions.dart +++ b/cw_bitcoin/lib/exceptions.dart @@ -30,7 +30,7 @@ class BitcoinTransactionCommitFailed extends TransactionCommitFailed { @override String toString() { - return errorMessage??"unknown error"; + return errorMessage ?? "unknown error"; } } diff --git a/cw_bitcoin/lib/hardware/bitbox_service.dart b/cw_bitcoin/lib/hardware/bitbox_service.dart index 3f47c5c62..a71dcacbc 100644 --- a/cw_bitcoin/lib/hardware/bitbox_service.dart +++ b/cw_bitcoin/lib/hardware/bitbox_service.dart @@ -67,7 +67,8 @@ class BitcoinBitboxService extends HardwareWalletService with BitcoinHardwareWal Future getMasterFingerprint() => manager.getMasterFingerprint(); } -class LitecoinBitboxService extends HardwareWalletService with BitcoinHardwareWalletService, LitecoinHardwareWalletService { +class LitecoinBitboxService extends HardwareWalletService + with BitcoinHardwareWalletService, LitecoinHardwareWalletService { LitecoinBitboxService(this.manager); final BitboxManager manager; diff --git a/cw_bitcoin/lib/hardware/litecoin_ledger_service.dart b/cw_bitcoin/lib/hardware/litecoin_ledger_service.dart index 9ede5714e..aa73fc106 100644 --- a/cw_bitcoin/lib/hardware/litecoin_ledger_service.dart +++ b/cw_bitcoin/lib/hardware/litecoin_ledger_service.dart @@ -12,7 +12,8 @@ import 'package:cw_core/hardware/hardware_wallet_service.dart'; import 'package:ledger_flutter_plus/ledger_flutter_plus.dart'; import 'package:ledger_litecoin/ledger_litecoin.dart'; -class LitecoinLedgerService extends HardwareWalletService with BitcoinHardwareWalletService, LitecoinHardwareWalletService { +class LitecoinLedgerService extends HardwareWalletService + with BitcoinHardwareWalletService, LitecoinHardwareWalletService { LitecoinLedgerService(this.ledgerConnection) : litecoinLedgerApp = LitecoinLedgerApp(ledgerConnection); @@ -53,7 +54,6 @@ class LitecoinLedgerService extends HardwareWalletService with BitcoinHardwareWa required List inputs, required Map publicKeys, }) { - final readyInputs = []; for (final utxo in inputs) { final publicKeyAndDerivationPath = publicKeys[utxo.ownerDetails.address.pubKeyHash()]!; @@ -77,7 +77,7 @@ class LitecoinLedgerService extends HardwareWalletService with BitcoinHardwareWa inputs: readyInputs, outputs: outputs .map((e) => TransactionOutput.fromBigInt((e as BitcoinOutput).value, - Uint8List.fromList(e.address.toScriptPubKey().toBytes()))) + Uint8List.fromList(e.address.toScriptPubKey().toBytes()))) .toList(), changePath: changePath, sigHashType: 0x01, diff --git a/cw_bitcoin/lib/lightning/lightning_wallet.dart b/cw_bitcoin/lib/lightning/lightning_wallet.dart index 42dda7cb2..ea9399c6a 100644 --- a/cw_bitcoin/lib/lightning/lightning_wallet.dart +++ b/cw_bitcoin/lib/lightning/lightning_wallet.dart @@ -90,8 +90,7 @@ class LightningWallet { lnurlDomain: lnurlDomain, apiKey: apiKey, privateEnabledDefault: true, - maxDepositClaimFee: MaxFee.rate(satPerVbyte: BigInt.from(5)) - ); + maxDepositClaimFee: MaxFee.rate(satPerVbyte: BigInt.from(5))); final connectRequest = ConnectRequest( config: config, @@ -105,8 +104,7 @@ class LightningWallet { _logStream ??= initLogging().asBroadcastStream(); try { - final logFile = File("$appPath/lightning.log") - ..createSync(); + final logFile = File("$appPath/lightning.log")..createSync(); _subscribeToLogStream(logFile); } catch (e) { printV(e); @@ -198,15 +196,17 @@ class LightningWallet { } } - Future createTransaction( - String address, BigInt? amountSats, BitcoinTransactionPriority? priority, bool feesIncluded) async { + Future createTransaction(String address, BigInt? amountSats, + BitcoinTransactionPriority? priority, bool feesIncluded) async { final inputType = await sdk.parse(input: address); final feePolicy = feesIncluded ? FeePolicy.feesIncluded : FeePolicy.feesExcluded; if (inputType is InputType_Bolt11Invoice) { final request = PrepareSendPaymentRequest( - paymentRequest: inputType.field0.invoice.bolt11, amount: amountSats, feePolicy: feePolicy); + paymentRequest: inputType.field0.invoice.bolt11, + amount: amountSats, + feePolicy: feePolicy); final prepareResponse = await sdk.prepareSendPayment(request: request); final paymentMethod = prepareResponse.paymentMethod; @@ -392,14 +392,16 @@ class LightningWallet { return _getElectrumTransactionInfoFromPayment(response.payment); } - Future refundDeposit(String txId, int vout, String destinationAddress, - BigInt feeRate) async { - final response = await sdk.refundDeposit(request: RefundDepositRequest( - txid: txId, - vout: vout, - destinationAddress: destinationAddress, - fee: Fee.rate(satPerVbyte: feeRate), - ),); + Future refundDeposit( + String txId, int vout, String destinationAddress, BigInt feeRate) async { + final response = await sdk.refundDeposit( + request: RefundDepositRequest( + txid: txId, + vout: vout, + destinationAddress: destinationAddress, + fee: Fee.rate(satPerVbyte: feeRate), + ), + ); return response.txHex; } diff --git a/cw_bitcoin/lib/lightning/pending_lightning_transaction.dart b/cw_bitcoin/lib/lightning/pending_lightning_transaction.dart index d2bcabb1f..237a6b824 100644 --- a/cw_bitcoin/lib/lightning/pending_lightning_transaction.dart +++ b/cw_bitcoin/lib/lightning/pending_lightning_transaction.dart @@ -10,10 +10,9 @@ class PendingLightningTransaction with PendingTransaction { required this.commitOverride, }); - final bool isSendAll; Future Function() commitOverride; - final List _listeners =[]; + final List _listeners = []; @override String id; diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 2f9ae0b3c..23959374c 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -175,7 +175,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { @override bool get hasRescan => true; - final String? scanSecretOverride; final String? spendPubkeyOverride; List get scanSecret => (scanSecretOverride != null && scanSecretOverride?.isNotEmpty == true) @@ -232,8 +231,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } @override - WalletKeysData get walletKeysData => - WalletKeysData(mnemonic: seed, xPub: xpub, passphrase: passphrase, scanSecret: scanSecretOverride, spendPubkey: spendPubkeyOverride); + WalletKeysData get walletKeysData => WalletKeysData( + mnemonic: seed, + xPub: xpub, + passphrase: passphrase, + scanSecret: scanSecretOverride, + spendPubkey: spendPubkeyOverride); static Future open({ required String name, @@ -1106,15 +1109,18 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { for (final utxo in transaction.utxos) { if (utxo.utxo.scriptType != SegwitAddresType.mweb) { inputs.add(utxo.utxo.toInput()); - txouts.add(TxOut(value: Int64(utxo.utxo.value.toInt()), - pkScript: utxo.ownerDetails.address.toScriptPubKey().toBytes())); + txouts.add(TxOut( + value: Int64(utxo.utxo.value.toInt()), + pkScript: utxo.ownerDetails.address.toScriptPubKey().toBytes())); } } var resp = await CwMweb.psbtCreate(PsbtCreateRequest( - rawTx: inputs.isEmpty ? null : BtcTransaction( - inputs: inputs, - outputs: isMweb ? [] : transaction.outputs, - ).toBytes(), + rawTx: inputs.isEmpty + ? null + : BtcTransaction( + inputs: inputs, + outputs: isMweb ? [] : transaction.outputs, + ).toBytes(), witnessUtxo: txouts, )); for (final utxo in transaction.utxos) { @@ -1127,17 +1133,18 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { )); } } - if (isMweb) for (final output in transaction.outputs) { - var address = addressFromOutputScript(output.scriptPubKey, LitecoinNetwork.mainnet); - if (output.scriptPubKey.getAddressType() == SegwitAddresType.mweb) { - address = SegwitBech32Encoder.encode("ltcmweb", 0, output.scriptPubKey.toBytes()); + if (isMweb) + for (final output in transaction.outputs) { + var address = addressFromOutputScript(output.scriptPubKey, LitecoinNetwork.mainnet); + if (output.scriptPubKey.getAddressType() == SegwitAddresType.mweb) { + address = SegwitBech32Encoder.encode("ltcmweb", 0, output.scriptPubKey.toBytes()); + } + resp = await CwMweb.psbtAddRecipient(PsbtAddRecipientRequest( + psbtB64: resp.psbtB64, + recipient: PsbtRecipient(address: address, value: Int64(output.amount.toInt())), + feeRatePerKb: Int64.parseInt(transaction.feeRate) * 1000, + )); } - resp = await CwMweb.psbtAddRecipient(PsbtAddRecipientRequest( - psbtB64: resp.psbtB64, - recipient: PsbtRecipient(address: address, value: Int64(output.amount.toInt())), - feeRatePerKb: Int64.parseInt(transaction.feeRate) * 1000, - )); - } return base64.decode(resp.psbtB64); } @@ -1203,7 +1210,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { for (final utxo in tx.utxos) { if (utxo.utxo.scriptType == SegwitAddresType.mweb) { hasMwebInput = true; - } else { // check if any of the inputs of this transaction are hog-ex: // this list is only non-mweb inputs: @@ -1256,9 +1262,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { final utxo = unspentCoins .firstWhere((utxo) => utxo.hash == e.value.txId && utxo.vout == e.value.txIndex); final key = generateECPrivate( - hd: utxo.bitcoinAddressRecord.isHidden - ? sideHd - : mainHd, + hd: utxo.bitcoinAddressRecord.isHidden ? sideHd : mainHd, index: utxo.bitcoinAddressRecord.index, network: network); final digest = tx2.getTransactionSegwitDigit( @@ -1284,8 +1288,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } } - void addTransactionListener(PendingBitcoinTransaction tx, - List inputAddresses, bool isPegIn, bool isPegOut) { + void addTransactionListener( + PendingBitcoinTransaction tx, List inputAddresses, bool isPegIn, bool isPegOut) { tx.addListener((transaction) async { final addresses = {}; transaction.inputAddresses?.addAll(inputAddresses); @@ -1575,7 +1579,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { rawTx: rawTx, ownerDetails: utxo.ownerDetails, ownerDerivationPath: publicKeyAndDerivationPath.derivationPath, - ownerMasterFingerprint:masterFingerprint, + ownerMasterFingerprint: masterFingerprint, ownerPublicKey: publicKeyAndDerivationPath.publicKey, )); } diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index a4a4b7590..fe85cd782 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -59,9 +59,11 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with ? hex.decode(scanSecretOverride!) : mwebHd?.childKey(Bip32KeyIndex(0x80000000)).privateKey.privKey.raw ?? List.filled(32, 0); - List get spendPubkey => (spendPubkeyOverride != null && spendPubkeyOverride?.isNotEmpty == true) - ? hex.decode(spendPubkeyOverride!) - : mwebHd?.childKey(Bip32KeyIndex(0x80000001)).publicKey.pubKey.compressed ?? List.filled(32, 0); + List get spendPubkey => + (spendPubkeyOverride != null && spendPubkeyOverride?.isNotEmpty == true) + ? hex.decode(spendPubkeyOverride!) + : mwebHd?.childKey(Bip32KeyIndex(0x80000001)).publicKey.pubKey.compressed ?? + List.filled(32, 0); @override Future init() async { @@ -81,7 +83,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with return null; } if ((scanSecret.length < 1 || scanSecret.reduce((a, b) => a + b) == 0) && - (spendPubkey.length < 1 || spendPubkey.reduce((a, b) => a + b) == 0)) { + (spendPubkey.length < 1 || spendPubkey.reduce((a, b) => a + b) == 0)) { return null; } @@ -230,12 +232,16 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with // don't use mweb addresses for exchange refund address: final current = getFreshAddress(); - final bool isMweb = receiveAddresses - .any((e) => e.address == current && e.type == SegwitAddresType.mweb); + final bool isMweb = + receiveAddresses.any((e) => e.address == current && e.type == SegwitAddresType.mweb); if (isMweb) { final segwit = receiveAddresses - .where((e) => e.type == SegwitAddresType.p2wpkh && !e.isUsed && !e.isHidden && !hiddenAddresses.contains(e.address)) + .where((e) => + e.type == SegwitAddresType.p2wpkh && + !e.isUsed && + !e.isHidden && + !hiddenAddresses.contains(e.address)) .map((e) => e.address) .toList(); diff --git a/cw_bitcoin/lib/litecoin_wallet_service.dart b/cw_bitcoin/lib/litecoin_wallet_service.dart index 0156ee672..09ab924d1 100644 --- a/cw_bitcoin/lib/litecoin_wallet_service.dart +++ b/cw_bitcoin/lib/litecoin_wallet_service.dart @@ -48,7 +48,8 @@ class LitecoinWalletService extends WalletService< password: credentials.password!, passphrase: credentials.passphrase, walletInfo: credentials.walletInfo!, - derivationInfo: credentials.derivationInfo ?? (await credentials.walletInfo!.getDerivationInfo()), + derivationInfo: + credentials.derivationInfo ?? (await credentials.walletInfo!.getDerivationInfo()), unspentCoinsInfo: unspentCoinsInfoSource, encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); @@ -64,7 +65,6 @@ class LitecoinWalletService extends WalletService< @override Future openWallet(String name, String password) async { - final walletInfo = await WalletInfo.get(name, getType()); if (walletInfo == null) { throw Exception('Wallet not found'); @@ -125,8 +125,9 @@ class LitecoinWalletService extends WalletService< } } - final unspentCoinsToDelete = unspentCoinsInfoSource.values.where( - (unspentCoin) => unspentCoin.walletId == walletInfo.id).toList(); + final unspentCoinsToDelete = unspentCoinsInfoSource.values + .where((unspentCoin) => unspentCoin.walletId == walletInfo.id) + .toList(); final keysToDelete = unspentCoinsToDelete.map((unspentCoin) => unspentCoin.key).toList(); @@ -150,8 +151,7 @@ class LitecoinWalletService extends WalletService< final network = isTestnet == true ? LitecoinNetwork.testnet : LitecoinNetwork.mainnet; credentials.walletInfo?.network = network.value; final derivationInfo = await credentials.walletInfo!.getDerivationInfo(); - derivationInfo.derivationPath = - credentials.hwAccountData.derivationPath; + derivationInfo.derivationPath = credentials.hwAccountData.derivationPath; await derivationInfo.save(); credentials.walletInfo!.save(); @@ -170,7 +170,7 @@ class LitecoinWalletService extends WalletService< @override Future restoreFromKeys(LitecoinWalletFromKeysCredentials credentials, - {bool? isTestnet}) async { + {bool? isTestnet}) async { final network = isTestnet == true ? LitecoinNetwork.testnet : LitecoinNetwork.mainnet; credentials.walletInfo?.network = network.value; @@ -202,7 +202,8 @@ class LitecoinWalletService extends WalletService< passphrase: credentials.passphrase, mnemonic: credentials.mnemonic, walletInfo: credentials.walletInfo!, - derivationInfo: credentials.derivationInfo ?? (await credentials.walletInfo!.getDerivationInfo()), + derivationInfo: + credentials.derivationInfo ?? (await credentials.walletInfo!.getDerivationInfo()), unspentCoinsInfo: unspentCoinsInfoSource, encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); diff --git a/cw_bitcoin/lib/payjoin/manager.dart b/cw_bitcoin/lib/payjoin/manager.dart index 4fa2e2a63..efa120f99 100644 --- a/cw_bitcoin/lib/payjoin/manager.dart +++ b/cw_bitcoin/lib/payjoin/manager.dart @@ -108,16 +108,14 @@ class PayjoinManager { Future initSender( String pjUriString, String originalPsbt, int networkFeesSatPerVb) async { try { - final pjUri = - (await PayjoinUri.Uri.fromStr(pjUriString)).checkPjSupported(); + final pjUri = (await PayjoinUri.Uri.fromStr(pjUriString)).checkPjSupported(); final minFeeRateSatPerKwu = BigInt.from(networkFeesSatPerVb * 250); final senderBuilder = await SenderBuilder.fromPsbtAndUri( psbtBase64: originalPsbt, pjUri: pjUri, ); final persister = PayjoinSenderPersister.impl(); - final newSender = - await senderBuilder.buildRecommended(minFeeRate: minFeeRateSatPerKwu); + final newSender = await senderBuilder.buildRecommended(minFeeRate: minFeeRateSatPerKwu); final senderToken = await newSender.persist(persister: persister); return Sender.load(token: senderToken, persister: persister); @@ -133,8 +131,7 @@ class PayjoinManager { bool isTestnet = false, }) async { final pjUri = Uri.parse(pjUrl).queryParameters['pj']!; - await _payjoinStorage.insertSenderSession( - sender, pjUri, _wallet.id, amount); + await _payjoinStorage.insertSenderSession(sender, pjUri, _wallet.id, amount); return _spawnSender(isTestnet: isTestnet, sender: sender, pjUri: pjUri); } @@ -207,8 +204,7 @@ class PayjoinManager { return completer.future; } - Future getUnusedReceiver(String address, - [bool isTestnet = false]) async { + Future getUnusedReceiver(String address, [bool isTestnet = false]) async { final session = _payjoinStorage.getUnusedActiveReceiverSession(_wallet.id); if (session != null) { @@ -220,7 +216,8 @@ class PayjoinManager { return initReceiver(address); } - Future initReceiver(String address, [bool isTestnet = false, int retryCount = 0]) async { + Future initReceiver(String address, + [bool isTestnet = false, int retryCount = 0]) async { if (retryCount > 0) writePayjoinLog("Retrying initReceiver ${retryCount + 1} attempt"); try { @@ -245,7 +242,6 @@ class PayjoinManager { } catch (e) { writePayjoinLog(e.toString()); if (e.toString().contains("error sending request for url") && retryCount < 5) { - return initReceiver(address, isTestnet, ++retryCount); } else { rethrow; @@ -273,13 +269,11 @@ class PayjoinManager { rawAmount = getOutputAmountFromTx(tx, _wallet); break; case PayjoinReceiverRequestTypes.checkIsOwned: - (_wallet.walletAddresses as BitcoinWalletAddresses) - .newPayjoinReceiver(); + (_wallet.walletAddresses as BitcoinWalletAddresses).newPayjoinReceiver(); _payjoinStorage.markReceiverSessionInProgress(receiver.id()); final inputScript = message['input_script'] as Uint8List; - final isOwned = - _wallet.isMine(Script.fromRaw(byteData: inputScript)); + final isOwned = _wallet.isMine(Script.fromRaw(byteData: inputScript)); mainToIsolateSendPort?.send({ 'requestId': message['requestId'], 'result': isOwned, @@ -288,8 +282,7 @@ class PayjoinManager { case PayjoinReceiverRequestTypes.checkIsReceiverOutput: final outputScript = message['output_script'] as Uint8List; - final isReceiverOutput = - _wallet.isMine(Script.fromRaw(byteData: outputScript)); + final isReceiverOutput = _wallet.isMine(Script.fromRaw(byteData: outputScript)); mainToIsolateSendPort?.send({ 'requestId': message['requestId'], 'result': isReceiverOutput, @@ -310,7 +303,8 @@ class PayjoinManager { case PayjoinReceiverRequestTypes.processPsbt: final psbt = message['psbt'] as String; - writePayjoinLog("Receiver(${receiver.id()}) PayjoinReceiverRequestTypes.processPsbt: $psbt"); + writePayjoinLog( + "Receiver(${receiver.id()}) PayjoinReceiverRequestTypes.processPsbt: $psbt"); final signedPsbt = await _wallet.signPsbt(psbt, utxos); mainToIsolateSendPort?.send({ @@ -322,7 +316,8 @@ class PayjoinManager { case PayjoinReceiverRequestTypes.proposalSent: _cleanupSession(receiver.id()); final psbt = message['psbt'] as String; - writePayjoinLog("Receiver(${receiver.id()}) PayjoinReceiverRequestTypes.proposalSent: $psbt"); + writePayjoinLog( + "Receiver(${receiver.id()}) PayjoinReceiverRequestTypes.proposalSent: $psbt"); await _payjoinStorage.markReceiverSessionComplete( receiver.id(), getTxIdFromPsbtV0(psbt), rawAmount); diff --git a/cw_bitcoin/lib/payjoin/payjoin_receive_worker.dart b/cw_bitcoin/lib/payjoin/payjoin_receive_worker.dart index 641399504..f1980e39e 100644 --- a/cw_bitcoin/lib/payjoin/payjoin_receive_worker.dart +++ b/cw_bitcoin/lib/payjoin/payjoin_receive_worker.dart @@ -47,8 +47,7 @@ class PayjoinReceiverWorker { try { final receiver = Receiver.fromJson(json: receiverJson); - final uncheckedProposal = - await worker.receiveUncheckedProposal(receiver); + final uncheckedProposal = await worker.receiveUncheckedProposal(receiver); final originalTx = await uncheckedProposal.extractTxToScheduleBroadcast(); sendPort.send({ @@ -112,8 +111,7 @@ class PayjoinReceiverWorker { final httpRequest = await client.post(url, headers: {'Content-Type': request.contentType}, body: request.body); - final proposal = await session.processRes( - body: httpRequest.bodyBytes, ctx: extractReq.$2); + final proposal = await session.processRes(body: httpRequest.bodyBytes, ctx: extractReq.$2); if (proposal != null) return proposal; sleep(Duration(seconds: 2)); } @@ -140,8 +138,7 @@ class PayjoinReceiverWorker { return await finalProposal.psbt(); } - Future processPayjoinProposal( - UncheckedProposal proposal) async { + Future processPayjoinProposal(UncheckedProposal proposal) async { await proposal.extractTxToScheduleBroadcast(); // TODO Handle this. send to the main port on a timer? @@ -174,20 +171,17 @@ class PayjoinReceiverWorker { ); final pj5 = await pj4.commitOutputs(); - final listUnspent = - await _sendRequest(PayjoinReceiverRequestTypes.getCandidateInputs); + final listUnspent = await _sendRequest(PayjoinReceiverRequestTypes.getCandidateInputs); final unspent = listUnspent as List; if (unspent.isEmpty) throw RecoverableError('No unspent outputs available'); - final candidateInputs = - await Future.wait(unspent.map(_inputPairFromUtxo)); + final candidateInputs = await Future.wait(unspent.map(_inputPairFromUtxo)); // Prefer a UTXO that avoids the Unnecessary Input Heuristic (UIH2); // fall back to the first candidate if none preserves privacy. InputPair selectedUtxo = candidateInputs.first; try { - selectedUtxo = - await pj5.tryPreservingPrivacy(candidateInputs: candidateInputs); + selectedUtxo = await pj5.tryPreservingPrivacy(candidateInputs: candidateInputs); } catch (_) {} final pj6 = await pj5.contributeInputs(replacementInputs: [selectedUtxo]); @@ -196,8 +190,8 @@ class PayjoinReceiverWorker { // Finalize proposal final payjoinProposal = await pj7.finalizeProposal( processPsbt: (String psbt) async { - final result = await _sendRequest( - PayjoinReceiverRequestTypes.processPsbt, {'psbt': psbt}); + final result = + await _sendRequest(PayjoinReceiverRequestTypes.processPsbt, {'psbt': psbt}); return result as String; }, // TODO set maxFeeRateSatPerVb @@ -213,15 +207,12 @@ class PayjoinReceiverWorker { Future _inputPairFromUtxo(UtxoWithPrivateKey utxo) async { final txout = TxOut( value: utxo.utxo.value, - scriptPubkey: Uint8List.fromList( - utxo.ownerDetails.address.toScriptPubKey().toBytes()), + scriptPubkey: Uint8List.fromList(utxo.ownerDetails.address.toScriptPubKey().toBytes()), ); - final psbtin = - PsbtInput(witnessUtxo: txout, redeemScript: null, witnessScript: null); + final psbtin = PsbtInput(witnessUtxo: txout, redeemScript: null, witnessScript: null); - final previousOutput = - OutPoint(txid: utxo.utxo.txHash, vout: utxo.utxo.vout); + final previousOutput = OutPoint(txid: utxo.utxo.txHash, vout: utxo.utxo.vout); final txin = TxIn( previousOutput: previousOutput, diff --git a/cw_bitcoin/lib/payjoin/payjoin_send_worker.dart b/cw_bitcoin/lib/payjoin/payjoin_send_worker.dart index 75f58a4e3..0d46d1f62 100644 --- a/cw_bitcoin/lib/payjoin/payjoin_send_worker.dart +++ b/cw_bitcoin/lib/payjoin/payjoin_send_worker.dart @@ -46,11 +46,11 @@ class PayjoinSenderWorker { sendPort.send(e); } } + final client = ProxyWrapper().getHttpIOClient(); /// Run a payjoin sender (V2 protocol first, fallback to V1). Future runSender(Sender sender) async { - try { return await _runSenderV2(sender); } catch (e) { @@ -70,13 +70,11 @@ class PayjoinSenderWorker { Future _runSenderV2(Sender sender) async { try { final postRequest = await sender.extractV2( - ohttpProxyUrl: - await pj_uri.Url.fromStr(PayjoinManager.randomOhttpRelayUrl()), + ohttpProxyUrl: await pj_uri.Url.fromStr(PayjoinManager.randomOhttpRelayUrl()), ); final postResult = await _postRequest(postRequest.$1); - final getContext = - await postRequest.$2.processResponse(response: postResult); + final getContext = await postRequest.$2.processResponse(response: postResult); sendPort.send({'type': PayjoinSenderRequestTypes.requestPosted, "pj": pjUrl}); diff --git a/cw_bitcoin/lib/payjoin/storage.dart b/cw_bitcoin/lib/payjoin/storage.dart index 5fb9d5716..e4132ebfa 100644 --- a/cw_bitcoin/lib/payjoin/storage.dart +++ b/cw_bitcoin/lib/payjoin/storage.dart @@ -23,16 +23,14 @@ class PayjoinStorage { ), ); - PayjoinSession? getUnusedActiveReceiverSession(String walletId) => - _payjoinSessionSources.values - .where((session) => - session.walletId == walletId && - session.status == PayjoinSessionStatus.created.name && - !session.isSenderSession) - .firstOrNull; + PayjoinSession? getUnusedActiveReceiverSession(String walletId) => _payjoinSessionSources.values + .where((session) => + session.walletId == walletId && + session.status == PayjoinSessionStatus.created.name && + !session.isSenderSession) + .firstOrNull; - Future markReceiverSessionComplete( - String sessionId, String txId, String amount) async { + Future markReceiverSessionComplete(String sessionId, String txId, String amount) async { final session = _payjoinSessionSources.get("$_receiverPrefix${sessionId}")!; session.status = PayjoinSessionStatus.success.name; @@ -41,8 +39,7 @@ class PayjoinStorage { await session.save(); } - Future markReceiverSessionUnrecoverable( - String sessionId, String reason) async { + Future markReceiverSessionUnrecoverable(String sessionId, String reason) async { final session = _payjoinSessionSources.get("$_receiverPrefix${sessionId}")!; session.status = PayjoinSessionStatus.unrecoverable.name; @@ -92,13 +89,10 @@ class PayjoinStorage { await session.save(); } - List readAllOpenSessions(String walletId) => - _payjoinSessionSources.values - .where((session) => - session.walletId == walletId && - ![ - PayjoinSessionStatus.success.name, - PayjoinSessionStatus.unrecoverable.name - ].contains(session.status)) - .toList(); + List readAllOpenSessions(String walletId) => _payjoinSessionSources.values + .where((session) => + session.walletId == walletId && + ![PayjoinSessionStatus.success.name, PayjoinSessionStatus.unrecoverable.name] + .contains(session.status)) + .toList(); } diff --git a/cw_bitcoin/lib/psbt/signer.dart b/cw_bitcoin/lib/psbt/signer.dart index c46517e66..fbd00b842 100644 --- a/cw_bitcoin/lib/psbt/signer.dart +++ b/cw_bitcoin/lib/psbt/signer.dart @@ -40,8 +40,7 @@ extension PsbtSigner on PsbtV2 { return tx.buffer(); } - Future signWithUTXO( - List utxos, UTXOSignerCallBack signer, + Future signWithUTXO(List utxos, UTXOSignerCallBack signer, [UTXOGetterCallBack? getTaprootPair]) async { final raw = BytesUtils.toHexString(extractUnsignedTX(getSegwit: false)); final tx = BtcTransaction.fromRaw(raw); @@ -53,8 +52,8 @@ extension PsbtSigner on PsbtV2 { if (utxos.any((e) => e.utxo.isP2tr())) { for (final input in tx.inputs) { - final utxo = utxos.firstWhereOrNull( - (u) => u.utxo.txHash == input.txId && u.utxo.vout == input.txIndex); + final utxo = utxos + .firstWhereOrNull((u) => u.utxo.txHash == input.txId && u.utxo.vout == input.txIndex); if (utxo == null) { final trPair = await getTaprootPair!.call(input.txId, input.txIndex); @@ -81,8 +80,8 @@ extension PsbtSigner on PsbtV2 { : BitcoinOpCodeConst.SIGHASH_ALL; /// We generate transaction digest for current input - final digest = _generateTransactionDigest( - script, i, utxo.utxo, tx, taprootAmounts, taprootScripts); + final digest = + _generateTransactionDigest(script, i, utxo.utxo, tx, taprootAmounts, taprootScripts); /// now we need sign the transaction digest final sig = signer(digest, utxo, utxo.privateKey, sighash); @@ -90,21 +89,14 @@ extension PsbtSigner on PsbtV2 { if (utxo.utxo.isP2tr()) { setInputTapKeySig(i, Uint8List.fromList(BytesUtils.fromHexString(sig))); } else { - setInputPartialSig( - i, - Uint8List.fromList(BytesUtils.fromHexString(utxo.public().toHex())), + setInputPartialSig(i, Uint8List.fromList(BytesUtils.fromHexString(utxo.public().toHex())), Uint8List.fromList(BytesUtils.fromHexString(sig))); } } } - List _generateTransactionDigest( - Script scriptPubKeys, - int input, - BitcoinUtxo utxo, - BtcTransaction transaction, - List taprootAmounts, - List with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin output.fiatAmount, (String amount) { @@ -991,8 +992,8 @@ class SendCardState extends State with AutomaticKeepAliveClientMixin - NewListSections( - sections: { - "": [ - if (FeatureFlag.isInAppTorEnabled) - ListItemToggle( - keyValue: "enable_builtin_tor", - label: S.of(context).enable_builtin_tor, - value: _connectionSyncViewModel.builtinTor, - onChanged: (val) { - _connectionSyncViewModel.setBuiltinTor(val, context); - }), - ListItemToggle( - keyValue: "disable_automatic_exchange_status_updates", - label: S.of(context).disable_automatic_exchange_status_updates, - value: _connectionSyncViewModel.disableAutomaticExchangeStatusUpdates, - onChanged: (val) { - _connectionSyncViewModel.setDisableAutomaticExchangeStatusUpdates(val); - }), - if (_connectionSyncViewModel.canUseBlinkProtection) - ListItemToggle( - keyValue: "can_use_blink_protection", - label: S.of(context).use_blink_protection, - value: _connectionSyncViewModel.useBlinkProtection, - onChanged: (val) { - _connectionSyncViewModel.setUseBlinkProtection(val); - }), - if (_connectionSyncViewModel.canUseEtherscan) - ListItemToggle( - keyValue: "can_use_etherscan", - label: S.of(context).etherscan_history, - value: _connectionSyncViewModel.useEtherscan, - onChanged: (val) { - _connectionSyncViewModel.setUseEtherscan(val); - }), - if (_connectionSyncViewModel.canUsePolygonScan) - ListItemToggle( - keyValue: "can_use_polygonscan", - label: S.of(context).polygonscan_history, - value: _connectionSyncViewModel.usePolygonScan, - onChanged: (val) { - _connectionSyncViewModel.setUsePolygonScan(val); - }), - if (_connectionSyncViewModel.canUseBaseScan) - ListItemToggle( - keyValue: "can_use_basescan", - label: S.of(context).basescan_history, - value: _connectionSyncViewModel.canUseBaseScan, - onChanged: (val) { - _connectionSyncViewModel.setUseBaseScan(val); - }), - if (_connectionSyncViewModel.canUseArbiScan) - ListItemToggle( - keyValue: "can_use_arbiscan", - label: S.of(context).arbiscan_history, - value: _connectionSyncViewModel.useArbiScan, - onChanged: (val) { - _connectionSyncViewModel.setUseArbiScan(val); - }), - if (_connectionSyncViewModel.canUseBscScan) - ListItemToggle( - keyValue: "can_use_bscscan", - label: S.of(context).bscscan_history, - value: _connectionSyncViewModel.useBscScan, - onChanged: (val) { - _connectionSyncViewModel.setUseBscScan(val); - }), - if (_connectionSyncViewModel.canUseTronGrid) - ListItemToggle( - keyValue: "can_use_trongrid", - label: S.of(context).trongrid_history, - value: _connectionSyncViewModel.useTronGrid, - onChanged: (val) { - _connectionSyncViewModel.setUseTronGrid(val); - }), - if (_connectionSyncViewModel.canUseMempoolFeeAPI) - ListItemToggle( - keyValue: "enable_mempool_api", - label: S.of(context).enable_mempool_api, - value: _connectionSyncViewModel.useMempoolFeeAPI, - onChanged: (bool isEnabled) async { - if (!isEnabled) { - final bool confirmation = await showPopUp( + builder: (context) => NewListSections(sections: { + "": [ + if (FeatureFlag.isInAppTorEnabled) + ListItemToggle( + keyValue: "enable_builtin_tor", + label: S.of(context).enable_builtin_tor, + value: _connectionSyncViewModel.builtinTor, + onChanged: (val) { + _connectionSyncViewModel.setBuiltinTor(val, context); + }), + ListItemToggle( + keyValue: "disable_automatic_exchange_status_updates", + label: S.of(context).disable_automatic_exchange_status_updates, + value: _connectionSyncViewModel.disableAutomaticExchangeStatusUpdates, + onChanged: (val) { + _connectionSyncViewModel.setDisableAutomaticExchangeStatusUpdates(val); + }), + if (_connectionSyncViewModel.canUseBlinkProtection) + ListItemToggle( + keyValue: "can_use_blink_protection", + label: S.of(context).use_blink_protection, + value: _connectionSyncViewModel.useBlinkProtection, + onChanged: (val) { + _connectionSyncViewModel.setUseBlinkProtection(val); + }), + if (_connectionSyncViewModel.canUseEtherscan) + ListItemToggle( + keyValue: "can_use_etherscan", + label: S.of(context).etherscan_history, + value: _connectionSyncViewModel.useEtherscan, + onChanged: (val) { + _connectionSyncViewModel.setUseEtherscan(val); + }), + if (_connectionSyncViewModel.canUsePolygonScan) + ListItemToggle( + keyValue: "can_use_polygonscan", + label: S.of(context).polygonscan_history, + value: _connectionSyncViewModel.usePolygonScan, + onChanged: (val) { + _connectionSyncViewModel.setUsePolygonScan(val); + }), + if (_connectionSyncViewModel.canUseBaseScan) + ListItemToggle( + keyValue: "can_use_basescan", + label: S.of(context).basescan_history, + value: _connectionSyncViewModel.canUseBaseScan, + onChanged: (val) { + _connectionSyncViewModel.setUseBaseScan(val); + }), + if (_connectionSyncViewModel.canUseArbiScan) + ListItemToggle( + keyValue: "can_use_arbiscan", + label: S.of(context).arbiscan_history, + value: _connectionSyncViewModel.useArbiScan, + onChanged: (val) { + _connectionSyncViewModel.setUseArbiScan(val); + }), + if (_connectionSyncViewModel.canUseBscScan) + ListItemToggle( + keyValue: "can_use_bscscan", + label: S.of(context).bscscan_history, + value: _connectionSyncViewModel.useBscScan, + onChanged: (val) { + _connectionSyncViewModel.setUseBscScan(val); + }), + if (_connectionSyncViewModel.canUseTronGrid) + ListItemToggle( + keyValue: "can_use_trongrid", + label: S.of(context).trongrid_history, + value: _connectionSyncViewModel.useTronGrid, + onChanged: (val) { + _connectionSyncViewModel.setUseTronGrid(val); + }), + if (_connectionSyncViewModel.canUseMempoolFeeAPI) + ListItemToggle( + keyValue: "enable_mempool_api", + label: S.of(context).enable_mempool_api, + value: _connectionSyncViewModel.useMempoolFeeAPI, + onChanged: (bool isEnabled) async { + if (!isEnabled) { + final bool confirmation = await showPopUp( context: context, builder: (BuildContext context) { return AlertWithTwoActions( @@ -133,91 +131,88 @@ class ConnectionSyncPage extends BasePage { alertContent: S.of(context).disable_fee_api_warning, rightButtonText: S.of(context).confirm, leftButtonText: S.of(context).cancel, - actionRightButton: () => Navigator.of(context).pop(true), - actionLeftButton: () => Navigator.of(context).pop(false)); + actionRightButton: () => + Navigator.of(context).pop(true), + actionLeftButton: () => + Navigator.of(context).pop(false)); }) ?? - false; - if (confirmation) { - _connectionSyncViewModel.setUseMempoolFeeAPI(isEnabled); - } - return; - } - + false; + if (confirmation) { _connectionSyncViewModel.setUseMempoolFeeAPI(isEnabled); - }), - if (Platform.isAndroid && FeatureFlag.isBackgroundSyncEnabled) - ListItemRegularRow( - keyValue: "background_sync", - label: S.of(context).background_sync, - onTap: () => Navigator.of(context).pushNamed(Routes.backgroundSync) - ), - if (_connectionSyncViewModel.hasPowNodes) - ListItemRegularRow( - keyValue: "manage_pow_nodes", - label: S.of(context).manage_pow_nodes, - onTap: () => Navigator.of(context).pushNamed(Routes.managePowNodes), - ), - ListItemSelector( - keyValue: "fiat_api", - label: S.of(context).fiat_api, - options: [_connectionSyncViewModel.fiatApiMode.title], - onTap: () async { - final items = FiatApiMode.all; + } + return; + } - final selectedAtIndex = + _connectionSyncViewModel.setUseMempoolFeeAPI(isEnabled); + }), + if (Platform.isAndroid && FeatureFlag.isBackgroundSyncEnabled) + ListItemRegularRow( + keyValue: "background_sync", + label: S.of(context).background_sync, + onTap: () => Navigator.of(context).pushNamed(Routes.backgroundSync)), + if (_connectionSyncViewModel.hasPowNodes) + ListItemRegularRow( + keyValue: "manage_pow_nodes", + label: S.of(context).manage_pow_nodes, + onTap: () => Navigator.of(context).pushNamed(Routes.managePowNodes), + ), + ListItemSelector( + keyValue: "fiat_api", + label: S.of(context).fiat_api, + options: [_connectionSyncViewModel.fiatApiMode.title], + onTap: () async { + final items = FiatApiMode.all; + + final selectedAtIndex = items.indexOf(_connectionSyncViewModel.fiatApiMode); - await showPopUp( - context: context, - builder: (_) => Picker( - items: items, - selectedAtIndex: selectedAtIndex, - mainAxisAlignment: MainAxisAlignment.start, - onItemSelected: (FiatApiMode fiatApiMode) { - _connectionSyncViewModel.setFiatMode(fiatApiMode); - }, - isSeparated: false, - ), - ); - }), - ListItemSelector( - keyValue: "swap", - label: S.of(context).swap, - options: [_connectionSyncViewModel.exchangeStatus.title], - onTap: () async { - final items = ExchangeApiMode.all; + await showPopUp( + context: context, + builder: (_) => Picker( + items: items, + selectedAtIndex: selectedAtIndex, + mainAxisAlignment: MainAxisAlignment.start, + onItemSelected: (FiatApiMode fiatApiMode) { + _connectionSyncViewModel.setFiatMode(fiatApiMode); + }, + isSeparated: false, + ), + ); + }), + ListItemSelector( + keyValue: "swap", + label: S.of(context).swap, + options: [_connectionSyncViewModel.exchangeStatus.title], + onTap: () async { + final items = ExchangeApiMode.all; - final selectedAtIndex = + final selectedAtIndex = items.indexOf(_connectionSyncViewModel.exchangeStatus); - await showPopUp( - context: context, - builder: (_) => Picker( - items: items, - selectedAtIndex: selectedAtIndex, - mainAxisAlignment: MainAxisAlignment.start, - onItemSelected: (ExchangeApiMode mode) { - _connectionSyncViewModel.setExchangeApiMode(mode); - }, - isSeparated: false, - ), - ); - }), - ListItemRegularRow( - keyValue: "domain_lookups", - label: S.of(context).domain_looks_up, - onTap: () => Navigator.of(context).pushNamed(Routes.domainLookupsPage) - ), - if(_connectionSyncViewModel.hasRescan) - ListItemRegularRow( + await showPopUp( + context: context, + builder: (_) => Picker( + items: items, + selectedAtIndex: selectedAtIndex, + mainAxisAlignment: MainAxisAlignment.start, + onItemSelected: (ExchangeApiMode mode) { + _connectionSyncViewModel.setExchangeApiMode(mode); + }, + isSeparated: false, + ), + ); + }), + ListItemRegularRow( + keyValue: "domain_lookups", + label: S.of(context).domain_looks_up, + onTap: () => Navigator.of(context).pushNamed(Routes.domainLookupsPage)), + if (_connectionSyncViewModel.hasRescan) + ListItemRegularRow( keyValue: "rescan", label: S.of(context).rescan, - onTap: ()=>Navigator.of(context).pushNamed(Routes.rescan) - ) - ], - } - ) - ), + onTap: () => Navigator.of(context).pushNamed(Routes.rescan)) + ], + })), ], ), ); diff --git a/lib/src/screens/settings/desktop_settings/desktop_settings_page.dart b/lib/src/screens/settings/desktop_settings/desktop_settings_page.dart index 09c3bb0b9..a792560a1 100644 --- a/lib/src/screens/settings/desktop_settings/desktop_settings_page.dart +++ b/lib/src/screens/settings/desktop_settings/desktop_settings_page.dart @@ -61,9 +61,9 @@ class _DesktopSettingsPageState extends State { } if ((!widget.dashboardViewModel.isMoneroViewOnly && - item.name(context) == S.of(context).export_outputs) || - (!widget.dashboardViewModel.hasMweb && - item.name(context) == S.of(context).litecoin_mweb_settings)) { + item.name(context) == S.of(context).export_outputs) || + (!widget.dashboardViewModel.hasMweb && + item.name(context) == S.of(context).litecoin_mweb_settings)) { return Container(); } @@ -103,11 +103,9 @@ class _DesktopSettingsPageState extends State { key: _settingsNavigatorKey, initialRoute: Routes.empty_no_route, onGenerateRoute: (settings) => Router.createRoute(settings), - onGenerateInitialRoutes: - (NavigatorState navigator, String initialRouteName) { + onGenerateInitialRoutes: (NavigatorState navigator, String initialRouteName) { return [ - navigator - .widget.onGenerateRoute!(RouteSettings(name: initialRouteName))! + navigator.widget.onGenerateRoute!(RouteSettings(name: initialRouteName))! ]; }, ), diff --git a/lib/src/screens/settings/display_settings_page.dart b/lib/src/screens/settings/display_settings_page.dart index be46befc3..5a04b86e2 100644 --- a/lib/src/screens/settings/display_settings_page.dart +++ b/lib/src/screens/settings/display_settings_page.dart @@ -27,7 +27,6 @@ import 'package:image_picker/image_picker.dart'; class DisplaySettingsPage extends StatelessWidget { DisplaySettingsPage(this._displaySettingsViewModel); - final DisplaySettingsViewModel _displaySettingsViewModel; @override @@ -38,198 +37,197 @@ class DisplaySettingsPage extends StatelessWidget { leadingIcon: Icon(Icons.arrow_back_ios_new), onLeadingPressed: () => Navigator.of(context).pop(), ), - content: Column( - spacing: 16, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (responsiveLayoutUtil.shouldRenderMobileUI && - DeviceInfo.instance.isMobile) ...[ - Padding( - padding: const EdgeInsets.only(left: 14, top: 14), - child: Text( - S.of(context).appearance, - style: Theme.of(context).textTheme.labelLarge?.copyWith( + content: Column( + spacing: 16, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (responsiveLayoutUtil.shouldRenderMobileUI && DeviceInfo.instance.isMobile) ...[ + Padding( + padding: const EdgeInsets.only(left: 14, top: 14), + child: Text( + S.of(context).appearance, + style: Theme.of(context).textTheme.labelLarge?.copyWith( height: 0.2, color: Theme.of(context).colorScheme.onSurfaceVariant, ), + ), + ), + Container( + decoration: ShapeDecoration( + color: Theme.of(context).colorScheme.surfaceContainerHigh, + shape: RoundedSuperellipseBorder(borderRadius: BorderRadius.circular(18))), + child: Column( + children: [ + SettingsChoicesCell( + ChoicesListItem( + title: "", + items: ThemeMode.values, + selectedItem: _displaySettingsViewModel.themeMode, + onItemSelected: (ThemeMode themeMode) => + _displaySettingsViewModel.setThemeMode(themeMode), + displayItem: (ThemeMode themeMode) { + return themeMode.name[0].toUpperCase() + + themeMode.name.substring(1).toLowerCase(); + }, + ), + useGenericColor: false, + padding: EdgeInsets.all(14), ), - ), - Container( - decoration: ShapeDecoration( - color: Theme.of(context).colorScheme.surfaceContainerHigh, - shape: RoundedSuperellipseBorder( - borderRadius: BorderRadius.circular(18))), - child: Column( - children: [ - SettingsChoicesCell( - ChoicesListItem( - title: "", - items: ThemeMode.values, - selectedItem: _displaySettingsViewModel.themeMode, - onItemSelected: (ThemeMode themeMode) => - _displaySettingsViewModel.setThemeMode(themeMode), - displayItem: (ThemeMode themeMode) { - return themeMode.name[0].toUpperCase() + - themeMode.name.substring(1).toLowerCase(); - }, + Container( + decoration: ShapeDecoration( + color: Theme.of(context).colorScheme.surfaceContainer, + shape: RoundedSuperellipseBorder(borderRadius: BorderRadius.circular(18))), + child: Column( + children: [ + Semantics( + label: S.of(context).color_theme, + child: SettingsThemeChoicesCell(_displaySettingsViewModel), ), - useGenericColor: false, - padding: EdgeInsets.all(14), - ), - Container( - decoration: ShapeDecoration( - color: Theme.of(context).colorScheme.surfaceContainer, - shape: RoundedSuperellipseBorder( - borderRadius: BorderRadius.circular(18))), - child: Column( - children: [ - Semantics( - label: S.of(context).color_theme, - child: SettingsThemeChoicesCell(_displaySettingsViewModel), - ), - ], - ), - ), - ], + ], + ), ), - ), - ], - Observer( - builder: (_) => NewListSections( - sections: { - "": [ - ListItemToggle( - keyValue: "apps", - label: S.of(context).apps, - value: _displaySettingsViewModel.shouldShowMarketPlaceInDashboard, - onChanged: (val) { - _displaySettingsViewModel.setShouldShowMarketPlaceInDashbaord(val); - }), - ListItemToggle( - keyValue: "display_settings_show_address_book_popup", - label: S.of(context).show_address_book_popup, - value: _displaySettingsViewModel.showAddressBookPopup, - onChanged: (val) { - _displaySettingsViewModel.setShowAddressBookPopup(val); - }), - ListItemToggle( - keyValue: "display_settings_disable_buy_button", - label: S.of(context).disable_buy, - value: _displaySettingsViewModel.disableTradeOption, - onChanged: (val) { - _displaySettingsViewModel.setDisableTradeOption(val); - }), - if (_displaySettingsViewModel.showZcashCardSetting) - ListItemToggle( - keyValue: "display_settings_show_zcashcard", - label: S.of(context).show_zcash_card, - value: _displaySettingsViewModel.showZcashCard, - onChanged: (val) { - _displaySettingsViewModel.setShowZcashCard(val); - }), - ListItemSelector( - keyValue: "display_settings_sync_status_display", - label: S.of(context).sync_status_display_mode, - options: [_displaySettingsViewModel.syncStatusDisplayMode.title], - onTap: () async { - final items = SyncStatusDisplayMode.values.toList(); + ], + ), + ), + ], + Observer( + builder: (_) => NewListSections( + sections: { + "": [ + ListItemToggle( + keyValue: "apps", + label: S.of(context).apps, + value: _displaySettingsViewModel.shouldShowMarketPlaceInDashboard, + onChanged: (val) { + _displaySettingsViewModel.setShouldShowMarketPlaceInDashbaord(val); + }), + ListItemToggle( + keyValue: "display_settings_show_address_book_popup", + label: S.of(context).show_address_book_popup, + value: _displaySettingsViewModel.showAddressBookPopup, + onChanged: (val) { + _displaySettingsViewModel.setShowAddressBookPopup(val); + }), + ListItemToggle( + keyValue: "display_settings_disable_buy_button", + label: S.of(context).disable_buy, + value: _displaySettingsViewModel.disableTradeOption, + onChanged: (val) { + _displaySettingsViewModel.setDisableTradeOption(val); + }), + if (_displaySettingsViewModel.showZcashCardSetting) + ListItemToggle( + keyValue: "display_settings_show_zcashcard", + label: S.of(context).show_zcash_card, + value: _displaySettingsViewModel.showZcashCard, + onChanged: (val) { + _displaySettingsViewModel.setShowZcashCard(val); + }), + ListItemSelector( + keyValue: "display_settings_sync_status_display", + label: S.of(context).sync_status_display_mode, + options: [_displaySettingsViewModel.syncStatusDisplayMode.title], + onTap: () async { + final items = SyncStatusDisplayMode.values.toList(); - final selectedAtIndex = + final selectedAtIndex = items.indexOf(_displaySettingsViewModel.syncStatusDisplayMode); - await showPopUp( - context: context, - builder: (_) => Picker( - items: items, - selectedAtIndex: selectedAtIndex, - mainAxisAlignment: MainAxisAlignment.start, - onItemSelected: (SyncStatusDisplayMode mode) { - _displaySettingsViewModel.setSyncStatusDisplayMode(mode); - }, - displayItem: (SyncStatusDisplayMode mode) => mode.title, - isSeparated: false, - ), - ); - }), - if (_displaySettingsViewModel.showDisplayAmountsInSatoshiSetting) - ListItemRegularRow( - keyValue: "display_settings_bitcoin_amount_display", - label: S.of(context).bitcoin_amount_display, - trailingText: _displaySettingsViewModel.displayAmountsInSatoshi.title, - onTap: () async { - final items = BitcoinAmountDisplayMode.all; + await showPopUp( + context: context, + builder: (_) => Picker( + items: items, + selectedAtIndex: selectedAtIndex, + mainAxisAlignment: MainAxisAlignment.start, + onItemSelected: (SyncStatusDisplayMode mode) { + _displaySettingsViewModel.setSyncStatusDisplayMode(mode); + }, + displayItem: (SyncStatusDisplayMode mode) => mode.title, + isSeparated: false, + ), + ); + }), + if (_displaySettingsViewModel.showDisplayAmountsInSatoshiSetting) + ListItemRegularRow( + keyValue: "display_settings_bitcoin_amount_display", + label: S.of(context).bitcoin_amount_display, + trailingText: _displaySettingsViewModel.displayAmountsInSatoshi.title, + onTap: () async { + final items = BitcoinAmountDisplayMode.all; - final selectedAtIndex = + final selectedAtIndex = items.indexOf(_displaySettingsViewModel.displayAmountsInSatoshi); - await showPopUp( - context: context, - builder: (_) => Picker( - items: items, - selectedAtIndex: selectedAtIndex, - mainAxisAlignment: MainAxisAlignment.start, - onItemSelected: _displaySettingsViewModel.setDisplayAmountsInSatoshi, - displayItem: (BitcoinAmountDisplayMode mode) => mode.title, - isSeparated: false, - ), - ); - }), - if (!_displaySettingsViewModel.disabledFiatApiMode) - ListItemSelector( - keyValue: "display_settings_fiat_currency", - label: S.of(context).settings_currency, - options: [_displaySettingsViewModel.fiatCurrency.title], - onTap: () => FiatCurrencyPickerSheet.show( - context: context, - selected: _displaySettingsViewModel.fiatCurrency, - onSelected: _displaySettingsViewModel.setFiatCurrency, - )), - ListItemSelector( - keyValue: "display_settings_language", - label: S.of(context).settings_change_language, - options: [LanguageService.list[_displaySettingsViewModel.languageCode] ?? ''], - onTap: () async { - final items = LanguageService.list.keys.toList(); + await showPopUp( + context: context, + builder: (_) => Picker( + items: items, + selectedAtIndex: selectedAtIndex, + mainAxisAlignment: MainAxisAlignment.start, + onItemSelected: _displaySettingsViewModel.setDisplayAmountsInSatoshi, + displayItem: (BitcoinAmountDisplayMode mode) => mode.title, + isSeparated: false, + ), + ); + }), + if (!_displaySettingsViewModel.disabledFiatApiMode) + ListItemSelector( + keyValue: "display_settings_fiat_currency", + label: S.of(context).settings_currency, + options: [_displaySettingsViewModel.fiatCurrency.title], + onTap: () => FiatCurrencyPickerSheet.show( + context: context, + selected: _displaySettingsViewModel.fiatCurrency, + onSelected: _displaySettingsViewModel.setFiatCurrency, + )), + ListItemSelector( + keyValue: "display_settings_language", + label: S.of(context).settings_change_language, + options: [LanguageService.list[_displaySettingsViewModel.languageCode] ?? ''], + onTap: () async { + final items = LanguageService.list.keys.toList(); - final selectedAtIndex = + final selectedAtIndex = items.indexOf(_displaySettingsViewModel.languageCode); - await showPopUp( - context: context, - builder: (_) => Picker( - displayItem: (dynamic code) { - return LanguageService.list[code] ?? ''; - }, - items: items, - selectedAtIndex: selectedAtIndex, - mainAxisAlignment: MainAxisAlignment.start, - onItemSelected: _displaySettingsViewModel.onLanguageSelected, - images: LanguageService.list.keys - .map((e) => Image.asset( + await showPopUp( + context: context, + builder: (_) => Picker( + displayItem: (dynamic code) { + return LanguageService.list[code] ?? ''; + }, + items: items, + selectedAtIndex: selectedAtIndex, + mainAxisAlignment: MainAxisAlignment.start, + onItemSelected: _displaySettingsViewModel.onLanguageSelected, + images: LanguageService.list.keys + .map((e) => Image.asset( "assets/images/flags/${LanguageService.localeCountryCode[e]}.png")) - .toList(), - hintText: S.of(context).search_language, - matchingCriteria: (String code, String searchText) { - return LanguageService.list[code]?.toLowerCase().contains(searchText) ?? false; - }, - isSeparated: true, - - ), - ); - }), - ], - }, - ), - ), - if (FeatureFlag.customBackgroundEnabled) - StandardListRow( - title: "Custom background", - isSelected: false, - onTap: (_) => _pickImage(context), - ), - ], + .toList(), + hintText: S.of(context).search_language, + matchingCriteria: (String code, String searchText) { + return LanguageService.list[code] + ?.toLowerCase() + .contains(searchText) ?? + false; + }, + isSeparated: true, + ), + ); + }), + ], + }, + ), ), - ); + if (FeatureFlag.customBackgroundEnabled) + StandardListRow( + title: "Custom background", + isSelected: false, + onTap: (_) => _pickImage(context), + ), + ], + ), + ); } // Function to pick an image from the gallery diff --git a/lib/src/screens/settings/domain_lookups_page.dart b/lib/src/screens/settings/domain_lookups_page.dart index e8528939e..9a61a8e6f 100644 --- a/lib/src/screens/settings/domain_lookups_page.dart +++ b/lib/src/screens/settings/domain_lookups_page.dart @@ -25,11 +25,13 @@ class DomainLookupsPage extends BasePage { .map( (source) => SettingsSwitcherCell( title: source.label, - leading: source.iconPath.isNotEmpty ? CakeImageWidget( - imageUrl: source.iconPath, - width: 24, - height: 24, - ) : SizedBox(width: 24, height: 24), + leading: source.iconPath.isNotEmpty + ? CakeImageWidget( + imageUrl: source.iconPath, + width: 24, + height: 24, + ) + : SizedBox(width: 24, height: 24), value: _connectionsSyncViewModel.lookupValue(source), onValueChange: (_, bool value) => _connectionsSyncViewModel.setLookupValue(source, value), diff --git a/lib/src/screens/settings/items/item_headers.dart b/lib/src/screens/settings/items/item_headers.dart index cc8a3b9aa..eca0576f4 100644 --- a/lib/src/screens/settings/items/item_headers.dart +++ b/lib/src/screens/settings/items/item_headers.dart @@ -15,4 +15,4 @@ class ItemHeaders { static const termsAndConditions = 'Terms and conditions'; static const faq = 'FAQ'; static const version = 'Version'; -} \ No newline at end of file +} diff --git a/lib/src/screens/settings/manage_nodes_page.dart b/lib/src/screens/settings/manage_nodes_page.dart index 75c384374..c750397e0 100644 --- a/lib/src/screens/settings/manage_nodes_page.dart +++ b/lib/src/screens/settings/manage_nodes_page.dart @@ -55,10 +55,10 @@ class _ManageNodesPageState extends State { ModernButton( size: 36, icon: Icon(Icons.add), - onPressed: ()async { + onPressed: () async { final res = await Navigator.of(context) - .pushNamed(widget.isPow ? Routes.newPowNode : Routes.newNode); - if(res != null && res is Node) { + .pushNamed(widget.isPow ? Routes.newPowNode : Routes.newNode); + if (res != null && res is Node) { widget.nodeListViewModel.nodes.add(res); } }) @@ -75,12 +75,11 @@ class _ManageNodesPageState extends State { // horizontal: 0, node: widget.nodeListViewModel.currentNode, speed: widget.nodeListViewModel.nodeSpeedFor(widget.nodeListViewModel.currentNode), - onEditComplete: (res)async{ - if(res != null && res is Node) { - widget.nodeListViewModel.nodes.removeWhere((item)=>item.id == res.id); + onEditComplete: (res) async { + if (res != null && res is Node) { + widget.nodeListViewModel.nodes.removeWhere((item) => item.id == res.id); widget.nodeListViewModel.nodes.add(res); } - }, onTap: () {}, isSelected: true, @@ -93,7 +92,7 @@ class _ManageNodesPageState extends State { color: Theme.of(context).colorScheme.surfaceContainer, borderRadius: BorderRadius.circular(18)), child: ClipRRect( - borderRadius: BorderRadius.circular(18), + borderRadius: BorderRadius.circular(18), child: Observer( builder: (BuildContext context) { int itemsCount = widget.nodeListViewModel.nonCurrentNodes.length; @@ -119,11 +118,11 @@ class _ManageNodesPageState extends State { isPow: widget.isPow, speed: widget.nodeListViewModel.nodeSpeedFor(node), onEditComplete: (res) async { - if(res != null && res is Node) { - widget.nodeListViewModel.nodes.removeWhere((item)=>item.id == res.id); + if (res != null && res is Node) { + widget.nodeListViewModel.nodes + .removeWhere((item) => item.id == res.id); widget.nodeListViewModel.nodes.add(res); } - }, onTap: () async { await showPopUp( diff --git a/lib/src/screens/settings/mweb_logs_page.dart b/lib/src/screens/settings/mweb_logs_page.dart index 6fd6ead3e..baa12a82f 100644 --- a/lib/src/screens/settings/mweb_logs_page.dart +++ b/lib/src/screens/settings/mweb_logs_page.dart @@ -38,7 +38,8 @@ class MwebLogsPage extends BasePage { padding: EdgeInsets.all(16.0), child: Text( snapshot.data!, - style: Theme.of(context).textTheme.bodyMedium!.copyWith(fontFamily: 'Monospace'), + style: + Theme.of(context).textTheme.bodyMedium!.copyWith(fontFamily: 'Monospace'), ), ), ); @@ -103,11 +104,10 @@ class MwebLogsPage extends BasePage { } Future _saveFile() async { - String? outputFile = await FilePicker.platform - .saveFile( - dialogTitle: 'Save Your File to desired location', - fileName: "debug.log", - lockParentWindow: true); + String? outputFile = await FilePicker.platform.saveFile( + dialogTitle: 'Save Your File to desired location', + fileName: "debug.log", + lockParentWindow: true); if (outputFile == null) return; diff --git a/lib/src/screens/settings/mweb_node_page.dart b/lib/src/screens/settings/mweb_node_page.dart index 45b89ed9d..f5be67ee0 100644 --- a/lib/src/screens/settings/mweb_node_page.dart +++ b/lib/src/screens/settings/mweb_node_page.dart @@ -7,6 +7,7 @@ import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/view_model/settings/mweb_settings_view_model.dart'; import 'package:flutter/material.dart'; + class MwebNodePage extends StatefulWidget { const MwebNodePage(this.mwebSettingsViewModelBase, {super.key}); @@ -30,30 +31,28 @@ class _MwebNodePageState extends State { Widget build(BuildContext context) { return SafeArea( child: ModalPageWrapper( - topBar: ModalTopBar( + topBar: ModalTopBar( title: S.current.litecoin_mweb_settings, onLeadingPressed: Navigator.of(context).pop, leadingIcon: Icon(Icons.arrow_back_ios_new)), - content: Container( - child: NewListSections( - controllers: { - widget.mwebSettingsViewModelBase.mwebNodeUri: _nodeUriController, - }, - sections: { - 'main': [ - ListItemTextField( - keyValue: widget.mwebSettingsViewModelBase.mwebNodeUri, - label: S.current.node_address, - validator: NodePathValidator(), - ), - ] - }), - ), - bottomContent: LoadingPrimaryButton( - onPressed: () => save(context), - text: S.of(context).save, - color: Theme.of(context).colorScheme.primary, - textColor: Theme.of(context).colorScheme.onPrimary, + content: Container( + child: NewListSections(controllers: { + widget.mwebSettingsViewModelBase.mwebNodeUri: _nodeUriController, + }, sections: { + 'main': [ + ListItemTextField( + keyValue: widget.mwebSettingsViewModelBase.mwebNodeUri, + label: S.current.node_address, + validator: NodePathValidator(), + ), + ] + }), + ), + bottomContent: LoadingPrimaryButton( + onPressed: () => save(context), + text: S.of(context).save, + color: Theme.of(context).colorScheme.primary, + textColor: Theme.of(context).colorScheme.onPrimary, ), ), ); diff --git a/lib/src/screens/settings/mweb_settings.dart b/lib/src/screens/settings/mweb_settings.dart index ffe6184b7..5fef27436 100644 --- a/lib/src/screens/settings/mweb_settings.dart +++ b/lib/src/screens/settings/mweb_settings.dart @@ -41,7 +41,7 @@ class MwebSettingsPage extends BasePage { title: S.current.litecoin_mweb_scanning, handler: (BuildContext context) => Navigator.of(context).pushNamed(Routes.rescan), ), - SettingsCellWithArrow( + SettingsCellWithArrow( title: S.current.litecoin_mweb_logs, handler: (BuildContext context) => Navigator.of(context).pushNamed(Routes.mwebLogs), ), diff --git a/lib/src/screens/settings/other_settings_page.dart b/lib/src/screens/settings/other_settings_page.dart index 9593f8b6b..7a08d6d86 100644 --- a/lib/src/screens/settings/other_settings_page.dart +++ b/lib/src/screens/settings/other_settings_page.dart @@ -149,7 +149,8 @@ class OtherSettingsPage extends BasePage { Navigator.of(context).pushNamed(Routes.signPage); }), ], - if (_otherSettingsViewModel.walletType == WalletType.bitcoin) "btc_logging": [ + if (_otherSettingsViewModel.walletType == WalletType.bitcoin) + "btc_logging": [ ListItemRegularRow( keyValue: "export_lightning_logs", label: S.of(context).export_lightning_logs, @@ -158,72 +159,74 @@ class OtherSettingsPage extends BasePage { keyValue: "export_payjoin_logs", label: S.of(context).export_payjoin_logs, onTap: () => onExportPJLog(context)), - ], - "dev": FeatureFlag.hasDevOptions == false ? [] : [ - if (_otherSettingsViewModel.walletType == WalletType.monero) - ListItemRegularRow( - keyValue: "[dev] monero background sync", - label: "[dev] monero background sync", - onTap: () => Navigator.of(context).pushNamed(Routes.devMoneroBackgroundSync)), - if ([WalletType.monero, WalletType.wownero, WalletType.zano] - .contains(_otherSettingsViewModel.walletType)) - ListItemRegularRow( - keyValue: "[dev] xmr call profiler", - label: "[dev] xmr call profiler", - onTap: () => Navigator.of(context).pushNamed(Routes.devMoneroCallProfiler)), - if ([WalletType.monero].contains(_otherSettingsViewModel.walletType)) - ListItemRegularRow( - keyValue: '[dev] xmr wallet cache debug', - label: '[dev] xmr wallet cache debug', - onTap: () => Navigator.of(context).pushNamed(Routes.devMoneroWalletCacheDebug)), - ListItemRegularRow( - keyValue: '[dev] shared preferences', - label: '[dev] shared preferences', - onTap: () => Navigator.of(context).pushNamed(Routes.devSharedPreferences)), - ListItemRegularRow( - keyValue: '[dev] secure storage preferences', - label: '[dev] secure storage preferences', - onTap: () => Navigator.of(context).pushNamed(Routes.devSecurePreferences)), - ListItemRegularRow( - keyValue: '[dev] background sync logs', - label: '[dev] background sync logs', - onTap: () => Navigator.of(context).pushNamed(Routes.devBackgroundSyncLogs)), - ListItemRegularRow( - keyValue: '[dev] socket health logs', - label: '[dev] socket health logs', - onTap: () => Navigator.of(context).pushNamed(Routes.devSocketHealthLogs)), - ListItemRegularRow( - keyValue: '[dev] network requests logs', - label: '[dev] network requests logs', - onTap: () => Navigator.of(context).pushNamed(Routes.devNetworkRequests)), - ListItemRegularRow( - keyValue: '[dev] exchange provider logs', - label: '[dev] exchange provider logs', - onTap: () => Navigator.of(context).pushNamed(Routes.devExchangeProviderLogs)), - ListItemRegularRow( - keyValue: '[dev] *QR tools', - label: '[dev] *QR tools', - onTap: () => Navigator.of(context).pushNamed(Routes.devExchangeProviderLogs)), - ListItemRegularRow( - keyValue: '[dev] browse sqlite db', - label: '[dev] browse sqlite db', - onTap: () async { - final data = await dumpDb(); - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => JsonExplorerPage(data: data, title: 'sqlite db'), - ), - ); - }), - ListItemRegularRow( - keyValue: '[dev] fake corrupt sqlite db', - label: '[dev] fake corrupt sqlite db', - onTap: () async { - final dbDebugMarker = await sqliteDebugMarkerFile(); - dbDebugMarker.create(); - } - ), - ] + ], + "dev": FeatureFlag.hasDevOptions == false + ? [] + : [ + if (_otherSettingsViewModel.walletType == WalletType.monero) + ListItemRegularRow( + keyValue: "[dev] monero background sync", + label: "[dev] monero background sync", + onTap: () => Navigator.of(context).pushNamed(Routes.devMoneroBackgroundSync)), + if ([WalletType.monero, WalletType.wownero, WalletType.zano] + .contains(_otherSettingsViewModel.walletType)) + ListItemRegularRow( + keyValue: "[dev] xmr call profiler", + label: "[dev] xmr call profiler", + onTap: () => Navigator.of(context).pushNamed(Routes.devMoneroCallProfiler)), + if ([WalletType.monero].contains(_otherSettingsViewModel.walletType)) + ListItemRegularRow( + keyValue: '[dev] xmr wallet cache debug', + label: '[dev] xmr wallet cache debug', + onTap: () => + Navigator.of(context).pushNamed(Routes.devMoneroWalletCacheDebug)), + ListItemRegularRow( + keyValue: '[dev] shared preferences', + label: '[dev] shared preferences', + onTap: () => Navigator.of(context).pushNamed(Routes.devSharedPreferences)), + ListItemRegularRow( + keyValue: '[dev] secure storage preferences', + label: '[dev] secure storage preferences', + onTap: () => Navigator.of(context).pushNamed(Routes.devSecurePreferences)), + ListItemRegularRow( + keyValue: '[dev] background sync logs', + label: '[dev] background sync logs', + onTap: () => Navigator.of(context).pushNamed(Routes.devBackgroundSyncLogs)), + ListItemRegularRow( + keyValue: '[dev] socket health logs', + label: '[dev] socket health logs', + onTap: () => Navigator.of(context).pushNamed(Routes.devSocketHealthLogs)), + ListItemRegularRow( + keyValue: '[dev] network requests logs', + label: '[dev] network requests logs', + onTap: () => Navigator.of(context).pushNamed(Routes.devNetworkRequests)), + ListItemRegularRow( + keyValue: '[dev] exchange provider logs', + label: '[dev] exchange provider logs', + onTap: () => Navigator.of(context).pushNamed(Routes.devExchangeProviderLogs)), + ListItemRegularRow( + keyValue: '[dev] *QR tools', + label: '[dev] *QR tools', + onTap: () => Navigator.of(context).pushNamed(Routes.devExchangeProviderLogs)), + ListItemRegularRow( + keyValue: '[dev] browse sqlite db', + label: '[dev] browse sqlite db', + onTap: () async { + final data = await dumpDb(); + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => JsonExplorerPage(data: data, title: 'sqlite db'), + ), + ); + }), + ListItemRegularRow( + keyValue: '[dev] fake corrupt sqlite db', + label: '[dev] fake corrupt sqlite db', + onTap: () async { + final dbDebugMarker = await sqliteDebugMarkerFile(); + dbDebugMarker.create(); + }), + ] }), ); } diff --git a/lib/src/screens/settings/privacy_page.dart b/lib/src/screens/settings/privacy_page.dart index 883814617..3d8b651a1 100644 --- a/lib/src/screens/settings/privacy_page.dart +++ b/lib/src/screens/settings/privacy_page.dart @@ -38,44 +38,43 @@ class PrivacyPage extends BasePage { return Column( mainAxisSize: MainAxisSize.min, children: [ - NewListSections( - sections: { - "1": [ - if (_privacySettingsViewModel.isAutoGenerateSubaddressesVisible) - ListItemToggle( - keyValue: "auto_generate_subaddresses", - label: _privacySettingsViewModel.isMoneroWallet - ? S.of(context).auto_generate_subaddresses - : S.of(context).auto_generate_addresses, - value: _privacySettingsViewModel.isAutoGenerateSubaddressesEnabled, - onChanged: (val) { - _privacySettingsViewModel.setAutoGenerateSubaddresses(val); - }), + NewListSections(sections: { + "1": [ + if (_privacySettingsViewModel.isAutoGenerateSubaddressesVisible) ListItemToggle( - keyValue: "save_recipient_address", - label: S.of(context).settings_save_recipient_address, - value: _privacySettingsViewModel.shouldSaveRecipientAddress, + keyValue: "auto_generate_subaddresses", + label: _privacySettingsViewModel.isMoneroWallet + ? S.of(context).auto_generate_subaddresses + : S.of(context).auto_generate_addresses, + value: _privacySettingsViewModel.isAutoGenerateSubaddressesEnabled, onChanged: (val) { - _privacySettingsViewModel.setShouldSaveRecipientAddress(val); + _privacySettingsViewModel.setAutoGenerateSubaddresses(val); }), - if (_privacySettingsViewModel.canUsePayjoin) - ListItemToggle( - keyValue: "use_payjoin", - label: S.of(context).use_payjoin, - value: _privacySettingsViewModel.usePayjoin, - onChanged: (val) { - _privacySettingsViewModel.setUsePayjoin(val); - }), - if (_privacySettingsViewModel.canUseLightning) - ListItemToggle( - keyValue: "enable_lightning", - label: S.of(context).enable_lightning, - value: _privacySettingsViewModel.useLightning, - onChanged: (val) { - _privacySettingsViewModel.setUseLightning(val); - }), - ], - "": [ + ListItemToggle( + keyValue: "save_recipient_address", + label: S.of(context).settings_save_recipient_address, + value: _privacySettingsViewModel.shouldSaveRecipientAddress, + onChanged: (val) { + _privacySettingsViewModel.setShouldSaveRecipientAddress(val); + }), + if (_privacySettingsViewModel.canUsePayjoin) + ListItemToggle( + keyValue: "use_payjoin", + label: S.of(context).use_payjoin, + value: _privacySettingsViewModel.usePayjoin, + onChanged: (val) { + _privacySettingsViewModel.setUsePayjoin(val); + }), + if (_privacySettingsViewModel.canUseLightning) + ListItemToggle( + keyValue: "enable_lightning", + label: S.of(context).enable_lightning, + value: _privacySettingsViewModel.useLightning, + onChanged: (val) { + _privacySettingsViewModel.setUseLightning(val); + }), + ], + "": [ if (_privacySettingsViewModel.isBitcoin) ListItemRegularRow( iconPath: "assets/new-ui/settings_row_icons/silent-payments.svg", @@ -83,23 +82,22 @@ class PrivacyPage extends BasePage { label: S.of(context).silent_payments, onTap: () => Navigator.of(context).pushNamed(Routes.silentPaymentsSettings)), - if (_privacySettingsViewModel.hasMWEB) - ListItemRegularRow( - iconPath: "assets/new-ui/settings_row_icons/mweb.svg", - keyValue: "mweb", - label: "MWEB", - onTap: () => - Navigator.of(context).pushNamed(Routes.mwebSettings)), - if (_privacySettingsViewModel.hasCoinControl) + if (_privacySettingsViewModel.hasMWEB) + ListItemRegularRow( + iconPath: "assets/new-ui/settings_row_icons/mweb.svg", + keyValue: "mweb", + label: "MWEB", + onTap: () => Navigator.of(context).pushNamed(Routes.mwebSettings)), + if (_privacySettingsViewModel.hasCoinControl) ListItemRegularRow( iconPath: "assets/new-ui/settings_row_icons/coin-control.svg", keyValue: "coin_control", label: "Coin Control", - onTap: () => - Navigator.of(context).pushNamed(Routes.unspentCoinsList, arguments: CoinControlPageArgs(canEdit: false, coinTypeToSpendFrom: null))), + onTap: () => Navigator.of(context).pushNamed(Routes.unspentCoinsList, + arguments: + CoinControlPageArgs(canEdit: false, coinTypeToSpendFrom: null))), ], - } - ), + }), ], ); }), diff --git a/lib/src/screens/settings/security_backup_page.dart b/lib/src/screens/settings/security_backup_page.dart index 37fb7c710..fc8f5a1be 100644 --- a/lib/src/screens/settings/security_backup_page.dart +++ b/lib/src/screens/settings/security_backup_page.dart @@ -73,8 +73,8 @@ class SecurityBackupPage extends BasePage { isAuthenticatedSuccessfully); } } else { - _securitySettingsViewModel.setAllowBiometricalAuthentication( - isAuthenticatedSuccessfully); + _securitySettingsViewModel + .setAllowBiometricalAuthentication(isAuthenticatedSuccessfully); } }, conditionToDetermineIfToUse2FA: _securitySettingsViewModel @@ -85,13 +85,13 @@ class SecurityBackupPage extends BasePage { } }), if (DeviceInfo.instance.isMobile) - ListItemToggle( - keyValue: "display_settings_prevent_screen_capture", - label: S.of(context).prevent_screenshots, - value: _securitySettingsViewModel.isAppSecure, - onChanged: (val) { - _securitySettingsViewModel.setIsAppSecure(val); - }), + ListItemToggle( + keyValue: "display_settings_prevent_screen_capture", + label: S.of(context).prevent_screenshots, + value: _securitySettingsViewModel.isAppSecure, + onChanged: (val) { + _securitySettingsViewModel.setIsAppSecure(val); + }), if (FeatureFlag.duressPinEnabled) ListItemToggle( keyValue: "security_backup_page_duress_pin_button_key", @@ -114,8 +114,7 @@ class SecurityBackupPage extends BasePage { if (confirmation) { Navigator.of(context).pushNamed( Routes.setupDuressPin, - arguments: - (PinCodeState pinCtx, String _) async { + arguments: (PinCodeState pinCtx, String _) async { pinCtx.close(); _securitySettingsViewModel.setEnableDuressPin(true); }, diff --git a/lib/src/screens/settings/silent_payments_logs_page.dart b/lib/src/screens/settings/silent_payments_logs_page.dart index 102755f4e..71bf5d85d 100644 --- a/lib/src/screens/settings/silent_payments_logs_page.dart +++ b/lib/src/screens/settings/silent_payments_logs_page.dart @@ -105,11 +105,10 @@ class SilentPaymentsLogPage extends BasePage { } Future _saveFile() async { - String? outputFile = await FilePicker.platform - .saveFile( - dialogTitle: 'Save Your File to desired location', - fileName: "debug.log", - lockParentWindow: true); + String? outputFile = await FilePicker.platform.saveFile( + dialogTitle: 'Save Your File to desired location', + fileName: "debug.log", + lockParentWindow: true); if (outputFile == null) return; diff --git a/lib/src/screens/settings/silent_payments_settings.dart b/lib/src/screens/settings/silent_payments_settings.dart index 023c8f649..15184d5e5 100644 --- a/lib/src/screens/settings/silent_payments_settings.dart +++ b/lib/src/screens/settings/silent_payments_settings.dart @@ -19,7 +19,11 @@ class SilentPaymentsSettingsPage extends StatelessWidget { color: Theme.of(context).colorScheme.surface, child: Column( children: [ - ModalTopBar(title: S.current.silent_payments_settings,leadingIcon: Icon(Icons.arrow_back_ios_new),onLeadingPressed: Navigator.of(context).pop,), + ModalTopBar( + title: S.current.silent_payments_settings, + leadingIcon: Icon(Icons.arrow_back_ios_new), + onLeadingPressed: Navigator.of(context).pop, + ), Expanded( child: SingleChildScrollView( child: Observer(builder: (_) { @@ -27,14 +31,14 @@ class SilentPaymentsSettingsPage extends StatelessWidget { padding: EdgeInsets.only(top: 10), child: Column( children: [ - if(!FeatureFlag.hasNewUi) - SettingsSwitcherCell( - title: S.current.silent_payments_display_card, - value: _silentPaymentsSettingsViewModel.silentPaymentsCardDisplay, - onValueChange: (_, bool value) { - _silentPaymentsSettingsViewModel.setSilentPaymentsCardDisplay(value); - }, - ), + if (!FeatureFlag.hasNewUi) + SettingsSwitcherCell( + title: S.current.silent_payments_display_card, + value: _silentPaymentsSettingsViewModel.silentPaymentsCardDisplay, + onValueChange: (_, bool value) { + _silentPaymentsSettingsViewModel.setSilentPaymentsCardDisplay(value); + }, + ), SettingsSwitcherCell( title: S.current.silent_payments_always_scan, value: _silentPaymentsSettingsViewModel.silentPaymentsAlwaysScan, @@ -44,7 +48,8 @@ class SilentPaymentsSettingsPage extends StatelessWidget { ), SettingsCellWithArrow( title: S.current.silent_payments_scanning, - handler: (BuildContext context) => Navigator.of(context).pushNamed(Routes.rescan), + handler: (BuildContext context) => + Navigator.of(context).pushNamed(Routes.rescan), ), SettingsCellWithArrow( title: S.current.silent_payments_logs, diff --git a/lib/src/screens/settings/widgets/settings_choices_cell.dart b/lib/src/screens/settings/widgets/settings_choices_cell.dart index fdf81d569..1e470dfc0 100644 --- a/lib/src/screens/settings/widgets/settings_choices_cell.dart +++ b/lib/src/screens/settings/widgets/settings_choices_cell.dart @@ -44,9 +44,8 @@ class SettingsChoicesCell extends StatelessWidget { color: Theme.of(context).colorScheme.surfaceContainerHighest, width: 1.5, ), - color: useGenericColor - ? Theme.of(context).colorScheme.surfaceContainerHighest - : null, + color: + useGenericColor ? Theme.of(context).colorScheme.surfaceContainerHighest : null, ), child: LayoutBuilder( builder: (context, constraints) { @@ -106,4 +105,4 @@ class SettingsChoicesCell extends StatelessWidget { ), ); } -} \ No newline at end of file +} diff --git a/lib/src/screens/settings/widgets/settings_picker_row.dart b/lib/src/screens/settings/widgets/settings_picker_row.dart index 462dedb83..9115c19c8 100644 --- a/lib/src/screens/settings/widgets/settings_picker_row.dart +++ b/lib/src/screens/settings/widgets/settings_picker_row.dart @@ -6,4 +6,4 @@ class SettingsPickerRaw extends StatelessWidget { // TODO: implement build throw UnimplementedError(); } -} \ No newline at end of file +} diff --git a/lib/src/screens/settings/widgets/settings_theme_choice.dart b/lib/src/screens/settings/widgets/settings_theme_choice.dart index c96743eb3..1276ef1d5 100644 --- a/lib/src/screens/settings/widgets/settings_theme_choice.dart +++ b/lib/src/screens/settings/widgets/settings_theme_choice.dart @@ -58,11 +58,13 @@ class SettingsThemeChoicesCell extends StatelessWidget { curve: Curves.easeInOut, margin: EdgeInsets.only(right: 24), decoration: ShapeDecoration( - shape: RoundedSuperellipseBorder(borderRadius: BorderRadius.circular(cellRadius), - side: BorderSide( - color: isSelected ? Theme.of(context).colorScheme.primary : Colors.transparent, - strokeAlign: BorderSide.strokeAlignOutside) - )), + shape: RoundedSuperellipseBorder( + borderRadius: BorderRadius.circular(cellRadius), + side: BorderSide( + color: isSelected + ? Theme.of(context).colorScheme.primary + : Colors.transparent, + strokeAlign: BorderSide.strokeAlignOutside))), child: ClipRRect( borderRadius: BorderRadius.circular(cellRadius), child: CakeImageWidget( @@ -97,8 +99,8 @@ class SettingsThemeChoicesCell extends StatelessWidget { children: [ Padding( padding: EdgeInsets.only(top: 14), - child: Container(height: 1, color: Theme.of(context).colorScheme.outlineVariant) - ), + child: Container( + height: 1, color: Theme.of(context).colorScheme.outlineVariant)), SizedBox(height: cellHeight), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -123,10 +125,14 @@ class SettingsThemeChoicesCell extends StatelessWidget { duration: Duration(milliseconds: 350), opacity: isSelected ? 1 : 0, child: Container( - width:28,height:28,decoration: BoxDecoration(borderRadius: BorderRadius.circular(99999999),border: Border.all(color:Theme.of(context) - .colorScheme - .onSurface)) - ), + width: 28, + height: 28, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(99999999), + border: Border.all( + color: Theme.of(context) + .colorScheme + .onSurface))), ), AnimatedScale( duration: Duration(milliseconds: 350), @@ -153,19 +159,19 @@ class SettingsThemeChoicesCell extends StatelessWidget { if (_displaySettingsViewModel.currentTheme is BlackTheme) Padding( padding: EdgeInsets.only(top: 12, bottom: 4), - child: Container(height: 1, color: Theme.of(context).colorScheme.outlineVariant) - ), + child: + Container(height: 1, color: Theme.of(context).colorScheme.outlineVariant)), if (_displaySettingsViewModel.currentTheme is BlackTheme) - SettingsSwitcherCell( - height: 40, - title: S.current.oled_mode, - value: _displaySettingsViewModel.isBlackThemeOledEnabled, - onValueChange: (_, bool value) { - _displaySettingsViewModel.setBlackThemeOled(value); - }, - padding: EdgeInsets.zero, - switchBackgroundColor: currentTheme.colorScheme.secondaryContainer, - ), + SettingsSwitcherCell( + height: 40, + title: S.current.oled_mode, + value: _displaySettingsViewModel.isBlackThemeOledEnabled, + onValueChange: (_, bool value) { + _displaySettingsViewModel.setBlackThemeOled(value); + }, + padding: EdgeInsets.zero, + switchBackgroundColor: currentTheme.colorScheme.secondaryContainer, + ), ], ), ); diff --git a/lib/src/screens/settings/widgets/wallet_connect_button.dart b/lib/src/screens/settings/widgets/wallet_connect_button.dart index 855c399da..66b2072cf 100644 --- a/lib/src/screens/settings/widgets/wallet_connect_button.dart +++ b/lib/src/screens/settings/widgets/wallet_connect_button.dart @@ -26,8 +26,8 @@ class WalletConnectTile extends StatelessWidget { child: Text( S.current.walletConnect, style: Theme.of(context).textTheme.bodyLarge?.copyWith( - color: Theme.of(context).colorScheme.onSurface, - ), + color: Theme.of(context).colorScheme.onSurface, + ), ), ), Image.asset( diff --git a/lib/src/screens/setup_2fa/setup_2fa_enter_code_page.dart b/lib/src/screens/setup_2fa/setup_2fa_enter_code_page.dart index 4ecfc6fd4..a9f992787 100644 --- a/lib/src/screens/setup_2fa/setup_2fa_enter_code_page.dart +++ b/lib/src/screens/setup_2fa/setup_2fa_enter_code_page.dart @@ -115,7 +115,6 @@ class TOTPEnterCode extends StatefulWidget { required this.isClosable, }); - final Setup2FAViewModel setup2FAViewModel; final bool isForSetup; final bool isClosable; @@ -154,7 +153,6 @@ class _TOTPEnterCodeState extends State { ), child: Column( children: [ - BaseTextFormField( textAlign: TextAlign.left, hintText: S.current.totp_code, @@ -180,15 +178,17 @@ class _TOTPEnterCodeState extends State { return PrimaryButton( isDisabled: widget.setup2FAViewModel.enteredOTPCode.length != 8, onPressed: () async { - final result = - await widget.setup2FAViewModel.totp2FAAuth(totpController.text, widget.isForSetup); - final bannedState = widget.setup2FAViewModel.state is AuthenticationBanned; + final result = await widget.setup2FAViewModel + .totp2FAAuth(totpController.text, widget.isForSetup); + final bannedState = + widget.setup2FAViewModel.state is AuthenticationBanned; await showPopUp( context: context, builder: (BuildContext context) { return PopUpCancellableAlertDialog( - contentText: _textDisplayedInPopupOnResult(result, bannedState, context), + contentText: + _textDisplayedInPopupOnResult(result, bannedState, context), actionButtonText: S.of(context).ok, buttonAction: () { result ? widget.setup2FAViewModel.success() : null; diff --git a/lib/src/screens/setup_2fa/widgets/popup_cancellable_alert.dart b/lib/src/screens/setup_2fa/widgets/popup_cancellable_alert.dart index a12e64c15..98b05794a 100644 --- a/lib/src/screens/setup_2fa/widgets/popup_cancellable_alert.dart +++ b/lib/src/screens/setup_2fa/widgets/popup_cancellable_alert.dart @@ -25,12 +25,11 @@ class PopUpCancellableAlertDialog extends StatelessWidget { contentText, textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodyMedium!.copyWith( - fontSize: 16, - fontWeight: FontWeight.normal, - - color: Theme.of(context).colorScheme.onSurface, - decoration: TextDecoration.none, - ), + fontSize: 16, + fontWeight: FontWeight.normal, + color: Theme.of(context).colorScheme.onSurface, + decoration: TextDecoration.none, + ), ); } diff --git a/lib/src/screens/setup_pin_code/setup_pin_code.dart b/lib/src/screens/setup_pin_code/setup_pin_code.dart index 07b66dcf7..69e254f79 100644 --- a/lib/src/screens/setup_pin_code/setup_pin_code.dart +++ b/lib/src/screens/setup_pin_code/setup_pin_code.dart @@ -8,7 +8,7 @@ import 'package:cake_wallet/view_model/setup_pin_code_view_model.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; class SetupPinCodePage extends BasePage { - SetupPinCodePage(this.pinCodeViewModel,{this.onSuccessfulPinSetup, this.isDuressPin = false}) + SetupPinCodePage(this.pinCodeViewModel, {this.onSuccessfulPinSetup, this.isDuressPin = false}) : pinCodeStateKey = GlobalKey(); final SetupPinCodeViewModel pinCodeViewModel; @@ -24,8 +24,7 @@ class SetupPinCodePage extends BasePage { key: pinCodeStateKey, hasLengthSwitcher: true, onFullPin: (String pin, PinCodeState state) async { - if (pinCodeViewModel.isOriginalPinCodeFull && - !pinCodeViewModel.isRepeatedPinCodeFull) { + if (pinCodeViewModel.isOriginalPinCodeFull && !pinCodeViewModel.isRepeatedPinCodeFull) { state.title = S.current.enter_your_pin_again; state.clear(); return; @@ -64,7 +63,7 @@ class SetupPinCodePage extends BasePage { if (pinCodeStateKey.currentState != null) { onSuccessfulPinSetup?.call(pinCodeStateKey.currentState!, pin); } - + state.reset(); }, alertBarrierDismissible: false, @@ -76,8 +75,7 @@ class SetupPinCodePage extends BasePage { builder: (BuildContext context) { return AlertWithOneAction( alertTitle: isDuressPin ? S.current.durres_PIN : S.current.setup_pin, - alertContent: - '${S.current.setup_pin_is_failed} ${e.toString()}', + alertContent: '${S.current.setup_pin_is_failed} ${e.toString()}', buttonText: S.of(context).ok, buttonAction: () => Navigator.of(context).pop(), alertBarrierDismissible: false, @@ -108,7 +106,6 @@ class SetupPinCodePage extends BasePage { pinCodeViewModel.reset(); } }, - onChangedPinLength: (int length) => - pinCodeViewModel.pinCodeLength = length, + onChangedPinLength: (int length) => pinCodeViewModel.pinCodeLength = length, initialPinLength: pinCodeViewModel.pinCodeLength); } diff --git a/lib/src/screens/splash/splash_page.dart b/lib/src/screens/splash/splash_page.dart index 07bc1119d..daaaaa915 100644 --- a/lib/src/screens/splash/splash_page.dart +++ b/lib/src/screens/splash/splash_page.dart @@ -3,8 +3,6 @@ import 'package:flutter/material.dart'; class SplashPage extends StatelessWidget { @override Widget build(BuildContext context) { - return Scaffold( - body: Container() - ); + return Scaffold(body: Container()); } -} \ No newline at end of file +} diff --git a/lib/src/screens/start_tor/start_tor_page.dart b/lib/src/screens/start_tor/start_tor_page.dart index 97c5c6599..93d5c0887 100644 --- a/lib/src/screens/start_tor/start_tor_page.dart +++ b/lib/src/screens/start_tor/start_tor_page.dart @@ -34,7 +34,7 @@ class StartTorPage extends BasePage { CircularProgressIndicator(), SizedBox(height: 20), _buildWaitingText(context), - ], + ], if (startTorViewModel.showOptions) ...[ _buildOptionsButtons(context), ], @@ -93,4 +93,4 @@ class StartTorPage extends BasePage { ], ); } -} \ No newline at end of file +} diff --git a/lib/src/screens/support_chat/support_chat_page.dart b/lib/src/screens/support_chat/support_chat_page.dart index f15b248a4..d2a3cae76 100644 --- a/lib/src/screens/support_chat/support_chat_page.dart +++ b/lib/src/screens/support_chat/support_chat_page.dart @@ -11,15 +11,14 @@ class SupportChatPage extends StatelessWidget { final SupportViewModel supportViewModel; final SecureStorage secureStorage; - @override Widget build(BuildContext context) => Container( - color: Theme.of(context).colorScheme.surface, - child: SafeArea( - child: Padding( - padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), - child: Column( - children: [ + color: Theme.of(context).colorScheme.surface, + child: SafeArea( + child: Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), + child: Column( + children: [ ModalTopBar( title: S.current.settings_support, leadingIcon: Icon(Icons.arrow_back_ios_new), @@ -42,12 +41,11 @@ class SupportChatPage extends StatelessWidget { return Container(); }, ), - ], + ], + ), + ), ), - ), - ), - ); + ); - Future getCookie() async => - await secureStorage.read(key: COOKIE_KEY) ?? ""; + Future getCookie() async => await secureStorage.read(key: COOKIE_KEY) ?? ""; } diff --git a/lib/src/screens/support_chat/widgets/chatwoot_widget.dart b/lib/src/screens/support_chat/widgets/chatwoot_widget.dart index 217d0a1c6..baf2650f3 100644 --- a/lib/src/screens/support_chat/widgets/chatwoot_widget.dart +++ b/lib/src/screens/support_chat/widgets/chatwoot_widget.dart @@ -41,11 +41,10 @@ class ChatwootWidgetState extends State { controller.addWebMessageListener( WebMessageListener( jsObjectName: 'ReactNativeWebView', - onPostMessage: (WebMessage? message, WebUri? sourceOrigin, - bool isMainFrame, PlatformJavaScriptReplyProxy replyProxy) { + onPostMessage: (WebMessage? message, WebUri? sourceOrigin, bool isMainFrame, + PlatformJavaScriptReplyProxy replyProxy) { final shortenedMessage = message?.data.toString().substring(16); - if (shortenedMessage != null && - _isJsonString(shortenedMessage)) { + if (shortenedMessage != null && _isJsonString(shortenedMessage)) { final parsedMessage = jsonDecode(shortenedMessage); final eventType = parsedMessage["event"]; if (eventType == 'loaded') { diff --git a/lib/src/screens/trade_details/track_trade_list_item.dart b/lib/src/screens/trade_details/track_trade_list_item.dart index 916dd72da..ed6f592d6 100644 --- a/lib/src/screens/trade_details/track_trade_list_item.dart +++ b/lib/src/screens/trade_details/track_trade_list_item.dart @@ -1,10 +1,7 @@ import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart'; class TrackTradeListItem extends StandartListItem { - TrackTradeListItem({ - required String title, - required String value, - required this.onTap}) + TrackTradeListItem({required String title, required String value, required this.onTap}) : super(title: title, value: value); final Function() onTap; } diff --git a/lib/src/screens/trade_details/trade_details_list_card.dart b/lib/src/screens/trade_details/trade_details_list_card.dart index 64f727b91..c69bdc19c 100644 --- a/lib/src/screens/trade_details/trade_details_list_card.dart +++ b/lib/src/screens/trade_details/trade_details_list_card.dart @@ -19,13 +19,11 @@ class TradeDetailsListCardItem extends StandartListItem { required CryptoCurrency to, required void Function(BuildContext) onTap, String? extraId}) { - - - final extraIdTitle = from == CryptoCurrency.xrp - ? S.current.destination_tag - : from == CryptoCurrency.xlm - ? S.current.memo - : S.current.extra_id; + final extraIdTitle = from == CryptoCurrency.xrp + ? S.current.destination_tag + : from == CryptoCurrency.xlm + ? S.current.memo + : S.current.extra_id; return TradeDetailsListCardItem( id: '${S.current.trade_details_id} ${formatAsText(id)}', diff --git a/lib/src/screens/trade_details/trade_details_page.dart b/lib/src/screens/trade_details/trade_details_page.dart index 371bd265f..f4fc4c5b1 100644 --- a/lib/src/screens/trade_details/trade_details_page.dart +++ b/lib/src/screens/trade_details/trade_details_page.dart @@ -74,10 +74,10 @@ class TradeDetailsPageBodyState extends State { child: Text( '${item.value}', style: Theme.of(context).textTheme.bodyMedium!.copyWith( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Theme.of(context).colorScheme.onSurface, - ), + fontSize: 16, + fontWeight: FontWeight.w500, + color: Theme.of(context).colorScheme.onSurface, + ), ), ), image: GestureDetector( diff --git a/lib/src/screens/trade_details/trade_details_status_item.dart b/lib/src/screens/trade_details/trade_details_status_item.dart index b1fd89e3e..399b6baea 100644 --- a/lib/src/screens/trade_details/trade_details_status_item.dart +++ b/lib/src/screens/trade_details/trade_details_status_item.dart @@ -1,8 +1,7 @@ import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart'; class DetailsListStatusItem extends StandartListItem { - DetailsListStatusItem( - {required String title, required String value, this.status}) + DetailsListStatusItem({required String title, required String value, this.status}) : super(title: title, value: value); final String? status; // waiting, action required, created, fetching, finished, success diff --git a/lib/src/screens/transaction_details/address_list_item.dart b/lib/src/screens/transaction_details/address_list_item.dart index 1e969f581..850a8dca3 100644 --- a/lib/src/screens/transaction_details/address_list_item.dart +++ b/lib/src/screens/transaction_details/address_list_item.dart @@ -2,4 +2,4 @@ import 'package:cake_wallet/src/screens/transaction_details/transaction_details_ class AddressListItem extends TransactionDetailsListItem { AddressListItem({required super.title, required super.value, super.key}); -} \ No newline at end of file +} diff --git a/lib/src/screens/transaction_details/confirmations_list_item.dart b/lib/src/screens/transaction_details/confirmations_list_item.dart index a1c7d4414..3870b5715 100644 --- a/lib/src/screens/transaction_details/confirmations_list_item.dart +++ b/lib/src/screens/transaction_details/confirmations_list_item.dart @@ -6,7 +6,7 @@ class ConfirmationsListItem extends TransactionDetailsListItem { ConfirmationsListItem({required super.title, required super.value, super.key}) { final parts = value.split("/"); - current = int.tryParse(parts.first)??0; - needed = int.tryParse(parts.last)??0; + current = int.tryParse(parts.first) ?? 0; + needed = int.tryParse(parts.last) ?? 0; } -} \ No newline at end of file +} diff --git a/lib/src/screens/transaction_details/rbf_details_page.dart b/lib/src/screens/transaction_details/rbf_details_page.dart index 30de3892c..0ec4d5acc 100644 --- a/lib/src/screens/transaction_details/rbf_details_page.dart +++ b/lib/src/screens/transaction_details/rbf_details_page.dart @@ -105,7 +105,8 @@ class RBFDetailsPage extends BasePage { text: S.of(context).send, isLoading: transactionDetailsViewModel.sendViewModel.state is IsExecutingState, - isDisabled: transactionDetailsViewModel.sendViewModel.state is ExecutedSuccessfullyState, + isDisabled: transactionDetailsViewModel.sendViewModel.state + is ExecutedSuccessfullyState, color: Theme.of(context).colorScheme.primary, textColor: Theme.of(context).colorScheme.onPrimary, ))), diff --git a/lib/src/screens/transaction_details/textfield_list_item.dart b/lib/src/screens/transaction_details/textfield_list_item.dart index 846f9acd5..bc151626c 100644 --- a/lib/src/screens/transaction_details/textfield_list_item.dart +++ b/lib/src/screens/transaction_details/textfield_list_item.dart @@ -14,4 +14,4 @@ class TextFieldListItem extends TransactionDetailsListItem { ); final Function(String value) onSubmitted; -} \ No newline at end of file +} diff --git a/lib/src/screens/transaction_details/transaction_details_page.dart b/lib/src/screens/transaction_details/transaction_details_page.dart index 9679b8531..8709dfb29 100644 --- a/lib/src/screens/transaction_details/transaction_details_page.dart +++ b/lib/src/screens/transaction_details/transaction_details_page.dart @@ -103,8 +103,10 @@ class TransactionDetailsPage extends BasePage { child: SelectButton( text: S.of(context).bump_fee, onTap: () async { - Navigator.of(context).pushNamed(Routes.bumpFeePage, - arguments: [transactionDetailsViewModel.transactionInfo, transactionDetailsViewModel.rawTransaction]); + Navigator.of(context).pushNamed(Routes.bumpFeePage, arguments: [ + transactionDetailsViewModel.transactionInfo, + transactionDetailsViewModel.rawTransaction + ]); }, ), ); @@ -123,25 +125,17 @@ class TransactionDetailsPage extends BasePage { required WalletType walletType, }) { final textStyle = Theme.of(context).textTheme.bodyMedium!.copyWith( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Theme.of(context).colorScheme.onSurface, - ); + fontSize: 16, + fontWeight: FontWeight.w500, + color: Theme.of(context).colorScheme.onSurface, + ); final List children = []; final bool hasDoubleNewline = value.contains('\n\n'); if (hasDoubleNewline) { - final blocks = value - .split('\n\n') - .map((b) => b.trim()) - .where((b) => b.isNotEmpty) - .toList(); + final blocks = value.split('\n\n').map((b) => b.trim()).where((b) => b.isNotEmpty).toList(); for (final block in blocks) { - final lines = block - .split('\n') - .map((l) => l.trim()) - .where((l) => l.isNotEmpty) - .toList(); + final lines = block.split('\n').map((l) => l.trim()).where((l) => l.isNotEmpty).toList(); if (lines.length > 1) { children.add(Text(lines.first, style: textStyle)); for (int i = 1; i < lines.length; i++) { @@ -165,11 +159,7 @@ class TransactionDetailsPage extends BasePage { children.add(SizedBox(height: 8)); } } else { - final lines = value - .split('\n') - .map((l) => l.trim()) - .where((l) => l.isNotEmpty) - .toList(); + final lines = value.split('\n').map((l) => l.trim()).where((l) => l.isNotEmpty).toList(); bool firstLineIsContactName = (lines.length > 1 && lines.first.length < 20); int startIndex = 0; if (firstLineIsContactName) { diff --git a/lib/src/screens/transaction_details/transaction_expandable_list_item.dart b/lib/src/screens/transaction_details/transaction_expandable_list_item.dart index db6cf22ae..84149e84a 100644 --- a/lib/src/screens/transaction_details/transaction_expandable_list_item.dart +++ b/lib/src/screens/transaction_details/transaction_expandable_list_item.dart @@ -7,6 +7,6 @@ class StandardExpandableListItem extends TransactionDetailsListItem { required this.expandableItems, Key? key, }) : super(title: title, value: '', key: key); - + final List expandableItems; } diff --git a/lib/src/screens/unspent_coins/unspent_coins_list_page.dart b/lib/src/screens/unspent_coins/unspent_coins_list_page.dart index 33ee821a6..d162a593d 100644 --- a/lib/src/screens/unspent_coins/unspent_coins_list_page.dart +++ b/lib/src/screens/unspent_coins/unspent_coins_list_page.dart @@ -121,14 +121,14 @@ class UnspentCoinsListFormState extends State { canPop: false, onPopInvokedWithResult: (bool didPop, Object? result) async { if (didPop) return; - if(mounted) - await widget.handleOnPopInvoked(context); + if (mounted) await widget.handleOnPopInvoked(context); }, child: FutureBuilder( future: _initialization, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { - return Center(child: CircularProgressIndicator( + return Center( + child: CircularProgressIndicator( color: Theme.of(context).colorScheme.primary, )); } @@ -153,10 +153,10 @@ class UnspentCoinsListFormState extends State { Text( S.current.all_coins, style: Theme.of(context).textTheme.bodyMedium!.copyWith( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Theme.of(context).colorScheme.onSurface, - ), + fontSize: 16, + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.onSurface, + ), ), ], ), @@ -165,46 +165,44 @@ class UnspentCoinsListFormState extends State { child: unspentCoinsListViewModel.items.isEmpty ? Center( child: Text( - 'No unspent coins available', - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodyMedium!.copyWith( - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), - ) - ) + 'No unspent coins available', + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + )) : ListView.separated( itemCount: unspentCoinsListViewModel.items.length, separatorBuilder: (_, __) => SizedBox(height: 15), itemBuilder: (_, int index) { final item = unspentCoinsListViewModel.items[index]; - return Observer( - builder: (_) { - final fiatAmount = unspentCoinsListViewModel.fiatAmounts[item.amount] ?? ''; - return GestureDetector( - onTap: () => Navigator.of(context).pushNamed( - Routes.unspentCoinsDetails, - arguments: [item, unspentCoinsListViewModel], - ), - child: UnspentCoinsListItem( - note: item.note, - amount: item.amount, - fiatAmount: fiatAmount, - address: item.address, - isSending: item.isSending, - isFrozen: item.isFrozen, - isChange: item.isChange, - isSilentPayment: item.isSilentPayment, - onCheckBoxTap: item.isFrozen - ? null - : () async { - item.isSending = !item.isSending; - await unspentCoinsListViewModel - .saveUnspentCoinInfo(item); - }, - ), - ); - } - ); + return Observer(builder: (_) { + final fiatAmount = + unspentCoinsListViewModel.fiatAmounts[item.amount] ?? ''; + return GestureDetector( + onTap: () => Navigator.of(context).pushNamed( + Routes.unspentCoinsDetails, + arguments: [item, unspentCoinsListViewModel], + ), + child: UnspentCoinsListItem( + note: item.note, + amount: item.amount, + fiatAmount: fiatAmount, + address: item.address, + isSending: item.isSending, + isFrozen: item.isFrozen, + isChange: item.isChange, + isSilentPayment: item.isSilentPayment, + onCheckBoxTap: item.isFrozen + ? null + : () async { + item.isSending = !item.isSending; + await unspentCoinsListViewModel + .saveUnspentCoinInfo(item); + }, + ), + ); + }); }, ), ), diff --git a/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart b/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart index d95fd1347..72d3ad432 100644 --- a/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart +++ b/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart @@ -73,19 +73,19 @@ class UnspentCoinsListItem extends StatelessWidget { AutoSizeText( note, style: Theme.of(context).textTheme.bodyMedium!.copyWith( - color: amountColor, - fontSize: 15, - fontWeight: FontWeight.w600, - ), + color: amountColor, + fontSize: 15, + fontWeight: FontWeight.w600, + ), maxLines: 1, ), AutoSizeText( amount, style: Theme.of(context).textTheme.bodyMedium!.copyWith( - color: amountColor, - fontSize: 15, - fontWeight: FontWeight.w600, - ), + color: amountColor, + fontSize: 15, + fontWeight: FontWeight.w600, + ), maxLines: 1, ) ], @@ -111,21 +111,21 @@ class UnspentCoinsListItem extends StatelessWidget { ], ), if (fiatAmount.isNotEmpty) - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - AutoSizeText( - fiatAmount, - style: Theme.of(context).textTheme.bodyMedium!.copyWith( - color: amountColor, - fontSize: 1, - fontWeight: FontWeight.w600, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + AutoSizeText( + fiatAmount, + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: amountColor, + fontSize: 1, + fontWeight: FontWeight.w600, + ), + maxLines: 1, ), - maxLines: 1, - ), - ], - ), + ], + ), Expanded( child: Row( crossAxisAlignment: CrossAxisAlignment.center, @@ -134,8 +134,8 @@ class UnspentCoinsListItem extends StatelessWidget { AutoSizeText( '${address.substring(0, 5)}...${address.substring(address.length - 5)}', // ToDo: Maybe use address label style: Theme.of(context).textTheme.bodySmall!.copyWith( - color: addressColor, - ), + color: addressColor, + ), maxLines: 1, ), Row( diff --git a/lib/src/screens/ur/animated_ur_page.dart b/lib/src/screens/ur/animated_ur_page.dart index 06e34bfde..b6a57f491 100644 --- a/lib/src/screens/ur/animated_ur_page.dart +++ b/lib/src/screens/ur/animated_ur_page.dart @@ -64,8 +64,7 @@ class AnimatedURPage extends BasePage { hardwareWalletType: animatedURmodel.wallet.hardwareWalletType, ), ), - if (["ur:xmr-txunsigned", "ur:xmr-output", "ur:psbt", BBQR.header] - .contains(urQrType)) ...{ + if (["ur:xmr-txunsigned", "ur:xmr-output", "ur:psbt", BBQR.header].contains(urQrType)) ...{ Padding( padding: const EdgeInsets.all(16.0), child: SizedBox( @@ -89,8 +88,7 @@ class AnimatedURPage extends BasePage { case "ur:xmr-txunsigned": // ur:xmr-txsigned final ur = await presentQRScanner(context, showManualInput: false); if (ur == null) return; - final result = - await monero!.commitTransactionUR(animatedURmodel.wallet, ur); + final result = await monero!.commitTransactionUR(animatedURmodel.wallet, ur); if (result) { Navigator.of(context).pop(true); } @@ -98,8 +96,7 @@ class AnimatedURPage extends BasePage { case "ur:xmr-output": // xmr-keyimage final ur = await presentQRScanner(context, showManualInput: false); if (ur == null) return; - final result = - await monero!.importKeyImagesUR(animatedURmodel.wallet, ur); + final result = await monero!.importKeyImagesUR(animatedURmodel.wallet, ur); if (result) { Navigator.of(context).pop(true); } @@ -107,8 +104,7 @@ class AnimatedURPage extends BasePage { case "ur:psbt": // psbt final ur = await presentQRScanner(context, showManualInput: false); if (ur == null) return; - await bitcoin! - .commitPsbtUR(animatedURmodel.wallet, ur.trim().split("\n")); + await bitcoin!.commitPsbtUR(animatedURmodel.wallet, ur.trim().split("\n")); Navigator.of(context).pop(true); default: throw UnimplementedError("unable to handle UR: ${urQrType}"); diff --git a/lib/src/screens/ur/widgets/qr_format_info_bottom_sheet.dart b/lib/src/screens/ur/widgets/qr_format_info_bottom_sheet.dart index 3b634c7fa..0a26f913e 100644 --- a/lib/src/screens/ur/widgets/qr_format_info_bottom_sheet.dart +++ b/lib/src/screens/ur/widgets/qr_format_info_bottom_sheet.dart @@ -24,10 +24,7 @@ class QRFormatInfoBottomSheet extends StatelessWidget { width: 40, height: 4, decoration: BoxDecoration( - color: Theme.of(context) - .colorScheme - .onSurface - .withOpacity(0.3), + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.3), borderRadius: BorderRadius.circular(2), ), ), diff --git a/lib/src/screens/ur/widgets/qr_selection_dialog.dart b/lib/src/screens/ur/widgets/qr_selection_dialog.dart index 2d0f818bf..8a5cf5adf 100644 --- a/lib/src/screens/ur/widgets/qr_selection_dialog.dart +++ b/lib/src/screens/ur/widgets/qr_selection_dialog.dart @@ -90,9 +90,7 @@ class QRFormatSelectionDialog extends BaseAlertDialog { fontSize: 14, fontWeight: FontWeight.w600, color: isSelected - ? Theme.of(context) - .colorScheme - .onPrimaryContainer + ? Theme.of(context).colorScheme.onPrimaryContainer : Theme.of(context).colorScheme.onSurface, ), ), @@ -105,10 +103,7 @@ class QRFormatSelectionDialog extends BaseAlertDialog { .colorScheme .onPrimaryContainer .withOpacity(0.6) - : Theme.of(context) - .colorScheme - .onSurface - .withOpacity(0.6), + : Theme.of(context).colorScheme.onSurface.withOpacity(0.6), ), ), ], diff --git a/lib/src/screens/ur/widgets/urqr.dart b/lib/src/screens/ur/widgets/urqr.dart index c12b7c9e8..7ac4ecdd0 100644 --- a/lib/src/screens/ur/widgets/urqr.dart +++ b/lib/src/screens/ur/widgets/urqr.dart @@ -53,19 +53,17 @@ class _URQRState extends State { String get nextLabel => widget.urqr.keys.toList()[(selectedInt + 1) % widget.urqr.length]; void next() => setState(() { - final keys = widget.urqr.keys.toList(); + final keys = widget.urqr.keys.toList(); - selectedInt++; - selected = keys[(selectedInt) % keys.length]; - }); + selectedInt++; + selected = keys[(selectedInt) % keys.length]; + }); late String selected = (widget.urqr.isEmpty) ? "unknown" : widget.urqr.keys.first; - List get frames => widget.urqr[selected]?.split("\n") ?? []; void _nextFrame() => setState(() => frame++); - @override Widget build(BuildContext context) { return Column( @@ -85,9 +83,7 @@ class _URQRState extends State { ), ), if (widget.urqr.values.length > 1) - widget.walletType == WalletType.monero - ? _legacySwitch(context) - : _newSwitch(context), + widget.walletType == WalletType.monero ? _legacySwitch(context) : _newSwitch(context), if (FeatureFlag.hasDevOptions) ...{ TextButton( onPressed: () { diff --git a/lib/src/screens/wallet_connect/services/chain_service/eth/evm_supported_methods.dart b/lib/src/screens/wallet_connect/services/chain_service/eth/evm_supported_methods.dart index 32e00570c..77544b982 100644 --- a/lib/src/screens/wallet_connect/services/chain_service/eth/evm_supported_methods.dart +++ b/lib/src/screens/wallet_connect/services/chain_service/eth/evm_supported_methods.dart @@ -28,4 +28,4 @@ enum EVMSupportedMethods { return 'eth_sendTransaction'; } } -} \ No newline at end of file +} diff --git a/lib/src/screens/wallet_connect/services/walletkit_service.dart b/lib/src/screens/wallet_connect/services/walletkit_service.dart index a758b8b9c..aa3c26307 100644 --- a/lib/src/screens/wallet_connect/services/walletkit_service.dart +++ b/lib/src/screens/wallet_connect/services/walletkit_service.dart @@ -133,9 +133,9 @@ abstract class WalletKitServiceBase with Store { if (!isInitialized) { try { await _walletKit.init().timeout( - const Duration(seconds: 8), - onTimeout: () => throw TimeoutException('walletKit init timed out'), - ); + const Duration(seconds: 8), + onTimeout: () => throw TimeoutException('walletKit init timed out'), + ); debugPrint('Initialized'); isInitialized = true; } catch (e) { @@ -202,14 +202,16 @@ abstract class WalletKitServiceBase with Store { namespaces: session.namespaces, ); if (events.contains('accountsChanged')) { - await _walletKit.emitSessionEvent( - topic: session.topic, - chainId: chainID, - event: SessionEventParams( - name: 'accountsChanged', - data: [chain.publicKey], - ), - ).timeout(const Duration(seconds: 3)); + await _walletKit + .emitSessionEvent( + topic: session.topic, + chainId: chainID, + event: SessionEventParams( + name: 'accountsChanged', + data: [chain.publicKey], + ), + ) + .timeout(const Duration(seconds: 3)); } } on ReownSignError catch (e) { if (e.code == 6) { @@ -433,12 +435,10 @@ abstract class WalletKitServiceBase with Store { } final requesterMetadata = args.requester.metadata; - final requesterIcon = requesterMetadata.icons.isNotEmpty - ? requesterMetadata.icons.first - : null; + final requesterIcon = + requesterMetadata.icons.isNotEmpty ? requesterMetadata.icons.first : null; final chainKeysForAuth = walletKeyService.getKeysForChain(appStore.wallet!); - final addressForAuth = - chainKeysForAuth.isNotEmpty ? chainKeysForAuth.first.publicKey : ''; + final addressForAuth = chainKeysForAuth.isNotEmpty ? chainKeysForAuth.first.publicKey : ''; final combinedMessageBody = formattedMessages.map((m) => m.values.first as String).join('\n\n'); @@ -521,8 +521,7 @@ abstract class WalletKitServiceBase with Store { @action Future deletePairing({required String topic}) async { - final topicSessions = - sessions.where((element) => element.pairingTopic == topic).toList(); + final topicSessions = sessions.where((element) => element.pairingTopic == topic).toList(); await _walletKit.core.pairing.disconnect(topic: topic); for (var session in topicSessions) { @@ -557,7 +556,7 @@ abstract class WalletKitServiceBase with Store { reason: Errors.getSdkError(Errors.USER_DISCONNECTED).toSignError(), ); } catch (e) { - printV('disconnectSession: $e'); + printV('disconnectSession: $e'); } sessions.clear(); @@ -622,7 +621,7 @@ abstract class WalletKitServiceBase with Store { @action List getSessionsForPairingInfo(PairingInfo pairing) { - return sessions.where((element) => element.pairingTopic == pairing.topic).toList(); + return sessions.where((element) => element.pairingTopic == pairing.topic).toList(); } String getKeyForStoringTopicsForWallet() { diff --git a/lib/src/screens/wallet_connect/utils/method_utils.dart b/lib/src/screens/wallet_connect/utils/method_utils.dart index b22c57259..78a11f871 100644 --- a/lib/src/screens/wallet_connect/utils/method_utils.dart +++ b/lib/src/screens/wallet_connect/utils/method_utils.dart @@ -13,7 +13,7 @@ import 'package:reown_walletkit/reown_walletkit.dart'; class MethodsUtils { static final walletKit = getIt.get().walletKit; static final bottomSheetService = getIt.get(); - + static const _transactionMethods = { 'eth_sendTransaction', 'eth_signTransaction', @@ -34,18 +34,13 @@ class MethodsUtils { }) async { final appStore = getIt.get(); final pending = walletKit.pendingRequests.getAll(); - final session = pending.isNotEmpty - ? walletKit.sessions.get(pending.last.topic) - : null; + final session = pending.isNotEmpty ? walletKit.sessions.get(pending.last.topic) : null; final dAppMetadata = session?.peer.metadata; final isTransaction = method != null && _transactionMethods.contains(method); final resolvedTitle = title ?? - (isTransaction - ? S.current.wc_approve_request_title - : S.current.wc_signing_request_title); - final swipeLabel = - isTransaction ? S.current.wc_swipe_to_approve : S.current.wc_swipe_to_sign; + (isTransaction ? S.current.wc_approve_request_title : S.current.wc_signing_request_title); + final swipeLabel = isTransaction ? S.current.wc_swipe_to_approve : S.current.wc_swipe_to_sign; final extraRows = []; if (method != null && method.isNotEmpty) { diff --git a/lib/src/screens/wallet_connect/utils/wc_permissions_mapper.dart b/lib/src/screens/wallet_connect/utils/wc_permissions_mapper.dart index 6a84f826e..fceb5714a 100644 --- a/lib/src/screens/wallet_connect/utils/wc_permissions_mapper.dart +++ b/lib/src/screens/wallet_connect/utils/wc_permissions_mapper.dart @@ -39,7 +39,8 @@ class WCPermissionsMapper { } final permissions = [ - WCPermission(iconUrl: "assets/new-ui/global_view.svg", label: S.current.wc_permission_view_balance), + WCPermission( + iconUrl: "assets/new-ui/global_view.svg", label: S.current.wc_permission_view_balance), ]; final wantsTransactionApproval = methods.any(_transactionMethods.contains); diff --git a/lib/src/screens/wallet_connect/wc_connections_listing_view.dart b/lib/src/screens/wallet_connect/wc_connections_listing_view.dart index a6caff932..1a2e4f44a 100644 --- a/lib/src/screens/wallet_connect/wc_connections_listing_view.dart +++ b/lib/src/screens/wallet_connect/wc_connections_listing_view.dart @@ -227,4 +227,4 @@ class WalletConnectConnectionsView extends StatelessWidget { ), ); } -} \ No newline at end of file +} diff --git a/lib/src/screens/wallet_connect/widgets/enter_wallet_connect_uri_widget.dart b/lib/src/screens/wallet_connect/widgets/enter_wallet_connect_uri_widget.dart index 036fdd6dc..b73d738ff 100644 --- a/lib/src/screens/wallet_connect/widgets/enter_wallet_connect_uri_widget.dart +++ b/lib/src/screens/wallet_connect/widgets/enter_wallet_connect_uri_widget.dart @@ -78,12 +78,12 @@ class EnterWalletConnectURIWidget extends BaseAlertDialog { width: 36, height: 36, padding: EdgeInsets.only(top: 0), - child: Semantics( + child: Semantics( label: S.of(context).paste, child: InkWell( onTap: () => _pasteWalletConnectURI(), child: Container( - padding: EdgeInsets.all(8), + padding: EdgeInsets.all(8), decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(6)), ), diff --git a/lib/src/screens/wallet_connect/widgets/wc_hero_card.dart b/lib/src/screens/wallet_connect/widgets/wc_hero_card.dart index ebb69a347..8d77ca9e3 100644 --- a/lib/src/screens/wallet_connect/widgets/wc_hero_card.dart +++ b/lib/src/screens/wallet_connect/widgets/wc_hero_card.dart @@ -1,4 +1,3 @@ - import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/widgets/cake_image_widget.dart'; import 'package:flutter/material.dart'; diff --git a/lib/src/screens/wallet_keys/wallet_keys_page.dart b/lib/src/screens/wallet_keys/wallet_keys_page.dart index c2b736d1b..ebd4e7496 100644 --- a/lib/src/screens/wallet_keys/wallet_keys_page.dart +++ b/lib/src/screens/wallet_keys/wallet_keys_page.dart @@ -184,7 +184,7 @@ class _WalletKeysPageBodyState extends State Widget _buildSeedTab(BuildContext context, bool isLegacySeed) { return Column( children: [ - if (isLegacySeedOnly || isLegacySeed ||widget.walletKeysViewModel.shouldShowHeightBox) ...[ + if (isLegacySeedOnly || isLegacySeed || widget.walletKeysViewModel.shouldShowHeightBox) ...[ _buildHeightBox(), const SizedBox(height: 20), ], diff --git a/lib/src/screens/wallet_list/wallet_list_page.dart b/lib/src/screens/wallet_list/wallet_list_page.dart index d94808ad9..f227af07c 100644 --- a/lib/src/screens/wallet_list/wallet_list_page.dart +++ b/lib/src/screens/wallet_list/wallet_list_page.dart @@ -143,7 +143,7 @@ class WalletListBodyState extends State { @override Widget build(BuildContext context) { return GradientBackground( - scaffold: Container( + scaffold: Container( height: double.infinity, padding: EdgeInsets.only(top: 16), child: Stack( @@ -339,45 +339,45 @@ class WalletListBodyState extends State { Stack( alignment: Alignment.bottomCenter, children: [ - !FeatureFlag.hasNewUi - ? IgnorePointer( - child: Container( - alignment: Alignment.bottomCenter, - height: 185, - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - Theme.of(context).colorScheme.surface.withAlpha(10), - Theme.of(context).colorScheme.surface, - Theme.of(context).colorScheme.surface, - Theme.of(context).colorScheme.surface - ], + !FeatureFlag.hasNewUi + ? IgnorePointer( + child: Container( + alignment: Alignment.bottomCenter, + height: 185, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Theme.of(context).colorScheme.surface.withAlpha(10), + Theme.of(context).colorScheme.surface, + Theme.of(context).colorScheme.surface, + Theme.of(context).colorScheme.surface + ], + ), + ), + ), + ) + : IgnorePointer( + child: Container( + height: 275, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Theme.of(context).colorScheme.surfaceDim.withAlpha(10), + Theme.of(context).colorScheme.surfaceDim.withAlpha(150), + Theme.of(context).colorScheme.surfaceDim.withAlpha(255), + Theme.of(context).colorScheme.surfaceDim.withAlpha(255), + Theme.of(context).colorScheme.surfaceDim.withAlpha(255), + Theme.of(context).colorScheme.surfaceDim.withAlpha(255), + Theme.of(context).colorScheme.surfaceDim.withAlpha(255) + ], + ), + ), + ), ), - ), - ), - ) - : IgnorePointer( - child: Container( - height: 275, - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - Theme.of(context).colorScheme.surfaceDim.withAlpha(10), - Theme.of(context).colorScheme.surfaceDim.withAlpha(150), - Theme.of(context).colorScheme.surfaceDim.withAlpha(255), - Theme.of(context).colorScheme.surfaceDim.withAlpha(255), - Theme.of(context).colorScheme.surfaceDim.withAlpha(255), - Theme.of(context).colorScheme.surfaceDim.withAlpha(255), - Theme.of(context).colorScheme.surfaceDim.withAlpha(255) - ], - ), - ), - ), - ), Container( height: 240, width: MediaQuery.of(context).size.width, @@ -464,8 +464,7 @@ class WalletListBodyState extends State { color: Theme.of(context).colorScheme.primary, textColor: Theme.of(context).colorScheme.onPrimary, ), - if(FeatureFlag.hasNewUi) - SizedBox(height:52.0) + if (FeatureFlag.hasNewUi) SizedBox(height: 52.0) ], ), ), @@ -578,9 +577,8 @@ class WalletListBodyState extends State { if (_progressBar != null) { _progressBar!.dismiss(); } - _progressBar = createBar(text, context, duration: null) - ..show(context); - }catch(e){} + _progressBar = createBar(text, context, duration: null)..show(context); + } catch (e) {} } Future hideProgressText() async { diff --git a/lib/src/screens/wallet_unlock/wallet_unlock_arguments.dart b/lib/src/screens/wallet_unlock/wallet_unlock_arguments.dart index 5b6d4dd16..e73d211a3 100644 --- a/lib/src/screens/wallet_unlock/wallet_unlock_arguments.dart +++ b/lib/src/screens/wallet_unlock/wallet_unlock_arguments.dart @@ -5,10 +5,7 @@ typedef AuthPasswordHandler = Future Function(String); class WalletUnlockArguments { WalletUnlockArguments( - {required this.callback, - this.walletName, - this.walletType, - this.authPasswordHandler}); + {required this.callback, this.walletName, this.walletType, this.authPasswordHandler}); final OnAuthenticationFinished callback; final AuthPasswordHandler? authPasswordHandler; diff --git a/lib/src/screens/welcome/create_pin_welcome_page.dart b/lib/src/screens/welcome/create_pin_welcome_page.dart index f8312fc79..a2c4a6755 100644 --- a/lib/src/screens/welcome/create_pin_welcome_page.dart +++ b/lib/src/screens/welcome/create_pin_welcome_page.dart @@ -228,7 +228,8 @@ class CreatePinWelcomePage extends BasePage { child: PrimaryButton( key: ValueKey('create_pin_welcome_page_create_a_pin_button_key'), onPressed: () => Navigator.pushNamed(context, Routes.welcomeWallet), - text: isWalletPasswordDirectInput ? S.current.set_up_a_wallet : S.current.set_a_pin, + text: + isWalletPasswordDirectInput ? S.current.set_up_a_wallet : S.current.set_a_pin, color: Theme.of(context).colorScheme.primary, textColor: Theme.of(context).colorScheme.onPrimary, ), diff --git a/lib/src/screens/welcome/welcome_page.dart b/lib/src/screens/welcome/welcome_page.dart index d9890cd41..3b618f490 100644 --- a/lib/src/screens/welcome/welcome_page.dart +++ b/lib/src/screens/welcome/welcome_page.dart @@ -22,17 +22,19 @@ class WelcomePage extends BasePage { @override Widget Function(BuildContext, Widget) get rootWrapper => - (BuildContext context, Widget scaffold) => GradientBackground(scaffold: scaffold); + (BuildContext context, Widget scaffold) => GradientBackground(scaffold: scaffold); @override bool get resizeToAvoidBottomInset => false; @override Widget trailing(BuildContext context) { - final Uri _url = - Uri.parse('https://docs.cakewallet.com/get-started/setup/'); + final Uri _url = Uri.parse('https://docs.cakewallet.com/get-started/setup/'); return IconButton( - icon: Icon(Icons.info_outline, size: 26,), + icon: Icon( + Icons.info_outline, + size: 26, + ), onPressed: () async { await launchUrl(_url); }, diff --git a/lib/src/screens/yat/widgets/first_introduction.dart b/lib/src/screens/yat/widgets/first_introduction.dart index a007b408d..be7606376 100644 --- a/lib/src/screens/yat/widgets/first_introduction.dart +++ b/lib/src/screens/yat/widgets/first_introduction.dart @@ -25,65 +25,44 @@ class FirstIntroduction extends StatelessWidget { color: Theme.of(context).colorScheme.surfaceContainer, child: ScrollableWithBottomSection( contentPadding: EdgeInsets.only(top: 40, bottom: 40), - content: Column( - children: [ - Container( - height: 45, - padding: EdgeInsets.only(left: 24, right: 24), - child: YatBar(onClose: () => Navigator.of(context).pop()) - ), - animation, - Container( - padding: EdgeInsets.only(left: 30, right: 30), - child: Column( - children: [ - Text( - S.of(context).yat_alert_title, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodyMedium!.copyWith( - fontSize: 24, - fontWeight: FontWeight.bold, - + content: Column(children: [ + Container( + height: 45, + padding: EdgeInsets.only(left: 24, right: 24), + child: YatBar(onClose: () => Navigator.of(context).pop())), + animation, + Container( + padding: EdgeInsets.only(left: 30, right: 30), + child: Column(children: [ + Text(S.of(context).yat_alert_title, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + fontSize: 24, + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.onSurface, + decoration: TextDecoration.none, + )), + Padding( + padding: EdgeInsets.only(top: 20), + child: Text(S.of(context).yat_alert_content, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + fontSize: 16, + fontWeight: FontWeight.normal, color: Theme.of(context).colorScheme.onSurface, decoration: TextDecoration.none, - ) - ), - Padding( - padding: EdgeInsets.only(top: 20), - child: Text( - S.of(context).yat_alert_content, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodyMedium!.copyWith( - fontSize: 16, - fontWeight: FontWeight.normal, - - color: Theme.of(context).colorScheme.onSurface, - decoration: TextDecoration.none, - ) - ) - ) - ] - ) - ) - ] - ), + ))) + ])) + ]), bottomSectionPadding: EdgeInsets.fromLTRB(24, 0, 24, 24), - bottomSection: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - PrimaryButton( - text: S.of(context).restore_next, - textColor: Theme.of(context).colorScheme.onPrimary, - color: Theme.of(context).colorScheme.primary, - onPressed: onNext - ), - Padding( - padding: EdgeInsets.only(top: 24), - child: YatPageIndicator(filled: 0) - ) - ] - ), - ) - ); + bottomSection: Column(crossAxisAlignment: CrossAxisAlignment.center, children: [ + PrimaryButton( + text: S.of(context).restore_next, + textColor: Theme.of(context).colorScheme.onPrimary, + color: Theme.of(context).colorScheme.primary, + onPressed: onNext), + Padding(padding: EdgeInsets.only(top: 24), child: YatPageIndicator(filled: 0)) + ]), + )); } -} \ No newline at end of file +} diff --git a/lib/src/screens/yat/widgets/second_introduction.dart b/lib/src/screens/yat/widgets/second_introduction.dart index 4482e5f05..2d1fe3e08 100644 --- a/lib/src/screens/yat/widgets/second_introduction.dart +++ b/lib/src/screens/yat/widgets/second_introduction.dart @@ -29,60 +29,42 @@ class SecondIntroduction extends StatelessWidget { Container( height: 45, padding: EdgeInsets.only(left: 24, right: 24), - child: YatBar(onClose: onClose) - ), + child: YatBar(onClose: onClose)), animation, Padding( padding: EdgeInsets.only(top: 40, left: 30, right: 30), - child: Column( - children: [ - Text( - S.of(context).second_intro_title, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodyMedium!.copyWith( + child: Column(children: [ + Text(S.of(context).second_intro_title, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.bodyMedium!.copyWith( fontSize: 24, fontWeight: FontWeight.bold, - color: Theme.of(context).colorScheme.onSurface, decoration: TextDecoration.none, - ) - ), - Padding( - padding: EdgeInsets.only(top: 20), - child: Text( - S.of(context).second_intro_content, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodyMedium!.copyWith( + )), + Padding( + padding: EdgeInsets.only(top: 20), + child: Text(S.of(context).second_intro_content, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.bodyMedium!.copyWith( fontSize: 16, fontWeight: FontWeight.normal, - color: Theme.of(context).colorScheme.onSurface, decoration: TextDecoration.none, - ) - ) - ) - ] - ), + ))) + ]), ), ], ), bottomSectionPadding: EdgeInsets.fromLTRB(24, 0, 24, 24), - bottomSection: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - PrimaryButton( - text: S.of(context).restore_next, - textColor: Theme.of(context).colorScheme.onPrimary, - color: Theme.of(context).colorScheme.primary, - onPressed: onNext - ), - Padding( - padding: EdgeInsets.only(top: 24), - child: YatPageIndicator(filled: 1) - ) - ] - ), - ) - ); + bottomSection: Column(crossAxisAlignment: CrossAxisAlignment.center, children: [ + PrimaryButton( + text: S.of(context).restore_next, + textColor: Theme.of(context).colorScheme.onPrimary, + color: Theme.of(context).colorScheme.primary, + onPressed: onNext), + Padding(padding: EdgeInsets.only(top: 24), child: YatPageIndicator(filled: 1)) + ]), + )); } -} \ No newline at end of file +} diff --git a/lib/src/screens/yat/widgets/third_introduction.dart b/lib/src/screens/yat/widgets/third_introduction.dart index 63fcdcd18..43fb37efa 100644 --- a/lib/src/screens/yat/widgets/third_introduction.dart +++ b/lib/src/screens/yat/widgets/third_introduction.dart @@ -39,23 +39,21 @@ class ThirdIntroduction extends StatelessWidget { Text(S.of(context).third_intro_title, textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodyMedium!.copyWith( - fontSize: 24, - fontWeight: FontWeight.bold, - - color: Theme.of(context).colorScheme.onSurface, - decoration: TextDecoration.none, - )), + fontSize: 24, + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.onSurface, + decoration: TextDecoration.none, + )), Padding( padding: EdgeInsets.only(top: 20), child: Text(S.of(context).third_intro_content, textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodyMedium!.copyWith( - fontSize: 16, - fontWeight: FontWeight.normal, - - color: Theme.of(context).colorScheme.onSurface, - decoration: TextDecoration.none, - ))) + fontSize: 16, + fontWeight: FontWeight.normal, + color: Theme.of(context).colorScheme.onSurface, + decoration: TextDecoration.none, + ))) ])), ], ), diff --git a/lib/src/screens/yat/widgets/yat_bar.dart b/lib/src/screens/yat/widgets/yat_bar.dart index 32312bf64..a605e360c 100644 --- a/lib/src/screens/yat/widgets/yat_bar.dart +++ b/lib/src/screens/yat/widgets/yat_bar.dart @@ -9,19 +9,9 @@ class YatBar extends StatelessWidget { @override Widget build(BuildContext context) { - return Stack( - alignment: Alignment.bottomCenter, - children: [ - Positioned( - top: 0, - right: 0, - child: YatCloseButton(onClose: onClose) - ), - Positioned( - top: 16, - child: image - ) - ] - ); + return Stack(alignment: Alignment.bottomCenter, children: [ + Positioned(top: 0, right: 0, child: YatCloseButton(onClose: onClose)), + Positioned(top: 16, child: image) + ]); } -} \ No newline at end of file +} diff --git a/lib/src/screens/yat/widgets/yat_page_indicator.dart b/lib/src/screens/yat/widgets/yat_page_indicator.dart index ec1d40584..a5fd2d08e 100644 --- a/lib/src/screens/yat/widgets/yat_page_indicator.dart +++ b/lib/src/screens/yat/widgets/yat_page_indicator.dart @@ -22,11 +22,7 @@ class YatPageIndicator extends StatelessWidget { shape: BoxShape.circle, color: isFilled ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.onSurfaceVariant.withOpacity(0.1) - ) - ); - }) - ) - ); + : Theme.of(context).colorScheme.onSurfaceVariant.withOpacity(0.1))); + }))); } -} \ No newline at end of file +} diff --git a/lib/src/widgets/adaptable_page_view.dart b/lib/src/widgets/adaptable_page_view.dart index fc2506e01..8ef9a2068 100644 --- a/lib/src/widgets/adaptable_page_view.dart +++ b/lib/src/widgets/adaptable_page_view.dart @@ -156,7 +156,6 @@ class _RenderSizingContainer extends RenderProxyBox { final double t = (page - floorPage).clamp(0.0, 1.0); final double height = lerpDouble(a.height, b.height, t) ?? a.height; - child.layout( constraints.copyWith(minHeight: height, maxHeight: height), parentUsesSize: true, @@ -214,4 +213,4 @@ class _RenderSizeAware extends RenderProxyBox { ), ); } -} \ No newline at end of file +} diff --git a/lib/src/widgets/alert_with_picker_option.dart b/lib/src/widgets/alert_with_picker_option.dart index fdc8f0b98..79a7b204b 100644 --- a/lib/src/widgets/alert_with_picker_option.dart +++ b/lib/src/widgets/alert_with_picker_option.dart @@ -48,7 +48,6 @@ class AlertWithPickerOption extends BaseAlertDialog { style: Theme.of(context).textTheme.bodyMedium!.copyWith( fontSize: 10, fontWeight: FontWeight.w500, - color: Theme.of(context).colorScheme.onSurface, decoration: TextDecoration.none, ), diff --git a/lib/src/widgets/base_alert_dialog.dart b/lib/src/widgets/base_alert_dialog.dart index 25c08ff6a..c87ab99ce 100644 --- a/lib/src/widgets/base_alert_dialog.dart +++ b/lib/src/widgets/base_alert_dialog.dart @@ -5,36 +5,30 @@ import 'package:cake_wallet/src/widgets/cake_image_widget.dart'; import 'package:cake_wallet/src/widgets/section_divider.dart'; import 'package:flutter/material.dart'; - class AlertButtonStyle { final Color backgroundColor; final Color textColor; final FontWeight fontWeight; - const AlertButtonStyle({ - required this.backgroundColor, - required this.textColor, - this.fontWeight = FontWeight.w400 - }); + const AlertButtonStyle( + {required this.backgroundColor, required this.textColor, this.fontWeight = FontWeight.w400}); factory AlertButtonStyle.primary(BuildContext context) => AlertButtonStyle( - backgroundColor: Theme.of(context).colorScheme.primary, - textColor: Theme.of(context).colorScheme.onPrimary, - ); + backgroundColor: Theme.of(context).colorScheme.primary, + textColor: Theme.of(context).colorScheme.onPrimary, + ); factory AlertButtonStyle.secondary(BuildContext context) => AlertButtonStyle( - backgroundColor: Theme.of(context).colorScheme.surfaceContainer, - textColor: Theme.of(context).colorScheme.primary, - ); + backgroundColor: Theme.of(context).colorScheme.surfaceContainer, + textColor: Theme.of(context).colorScheme.primary, + ); factory AlertButtonStyle.error(BuildContext context) => AlertButtonStyle( - backgroundColor: Theme.of(context).colorScheme.errorContainer, - textColor: Theme.of(context).colorScheme.error, - fontWeight: FontWeight.w500 - ); + backgroundColor: Theme.of(context).colorScheme.errorContainer, + textColor: Theme.of(context).colorScheme.error, + fontWeight: FontWeight.w500); } - class BaseAlertDialog extends StatelessWidget { String? get headerText => ''; @@ -67,7 +61,7 @@ class BaseAlertDialog extends StatelessWidget { Key? rightActionButtonKey; Key? dialogKey; - + AlertButtonStyle? get leftAlertButtonStyle => null; AlertButtonStyle? get rightAlertButtonStyle => null; @@ -129,29 +123,30 @@ class BaseAlertDialog extends StatelessWidget { return Row( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.max, - spacing:8, + spacing: 8, children: [ - if(showLeftButton) - Expanded( - child: GestureDetector( - key: leftActionButtonKey, - onTap: actionLeft, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(999999), - color: leftButtonStyle.backgroundColor - ), - child: Padding( - padding: EdgeInsets.symmetric(vertical: 16, horizontal: 8), - child: AutoSizeText( - maxLines:1, - leftActionButtonText, - textAlign: TextAlign.center, - style: TextStyle(fontSize: 16, color: leftButtonStyle.textColor, fontWeight: leftButtonStyle.fontWeight) + if (showLeftButton) + Expanded( + child: GestureDetector( + key: leftActionButtonKey, + onTap: actionLeft, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(999999), + color: leftButtonStyle.backgroundColor), + child: Padding( + padding: EdgeInsets.symmetric(vertical: 16, horizontal: 8), + child: AutoSizeText( + maxLines: 1, + leftActionButtonText, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + color: leftButtonStyle.textColor, + fontWeight: leftButtonStyle.fontWeight)), ), - ), - )), - ), + )), + ), Expanded( child: GestureDetector( key: rightActionButtonKey, @@ -159,16 +154,17 @@ class BaseAlertDialog extends StatelessWidget { child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(999999), - color: rightButtonStyle.backgroundColor - ), + color: rightButtonStyle.backgroundColor), child: Padding( padding: EdgeInsets.symmetric(vertical: 16, horizontal: 8), child: AutoSizeText( - maxLines: 1, - rightActionButtonText, - textAlign: TextAlign.center, - style: TextStyle(fontSize: 16, color: rightButtonStyle.textColor, fontWeight: rightButtonStyle.fontWeight) - ), + maxLines: 1, + rightActionButtonText, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + color: rightButtonStyle.textColor, + fontWeight: rightButtonStyle.fontWeight)), ), )), ), @@ -185,7 +181,7 @@ class BaseAlertDialog extends StatelessWidget { radius: 50, backgroundColor: Theme.of(context).colorScheme.surfaceContainerHighest, child: ClipOval( - child: CakeImageWidget (imageUrl: imageUrl, width: 100, height: 100, fit: BoxFit.cover), + child: CakeImageWidget(imageUrl: imageUrl, width: 100, height: 100, fit: BoxFit.cover), ), ), ); @@ -201,8 +197,7 @@ class BaseAlertDialog extends StatelessWidget { child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0), child: Container( - decoration: - BoxDecoration(color: Theme.of(context).colorScheme.onSurface.withAlpha(25)), + decoration: BoxDecoration(color: Theme.of(context).colorScheme.onSurface.withAlpha(25)), child: Center( child: Padding( padding: const EdgeInsets.all(16.0), @@ -229,9 +224,7 @@ class BaseAlertDialog extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ if (headerText?.isNotEmpty ?? false) headerTitle(context), - titleText != null - ? title(context) - : SizedBox(height: 16), + titleText != null ? title(context) : SizedBox(height: 16), isDividerExists ? Padding( padding: EdgeInsets.only(top: 16, bottom: 8), diff --git a/lib/src/widgets/base_text_form_field.dart b/lib/src/widgets/base_text_form_field.dart index f6402bea0..7329be545 100644 --- a/lib/src/widgets/base_text_form_field.dart +++ b/lib/src/widgets/base_text_form_field.dart @@ -47,7 +47,8 @@ class BaseTextFormField extends StatelessWidget { this.suffixIconConstraints, super.key, this.suffixText, - this.borderRadius = const BorderRadius.all(Radius.circular(18)), this.onEditingComplete, + this.borderRadius = const BorderRadius.all(Radius.circular(18)), + this.onEditingComplete, }); final TextEditingController? controller; diff --git a/lib/src/widgets/blockchain_height_widget.dart b/lib/src/widgets/blockchain_height_widget.dart index 122806ded..c4cafb5a2 100644 --- a/lib/src/widgets/blockchain_height_widget.dart +++ b/lib/src/widgets/blockchain_height_widget.dart @@ -195,7 +195,7 @@ class BlockchainHeightState extends State { height = decred!.heightByDate(date); } else if (widget.walletType == WalletType.monero) { height = monero!.getHeightByDate(date: date); - } else if (widget.walletType == WalletType.wownero){ + } else if (widget.walletType == WalletType.wownero) { height = wownero!.getHeightByDate(date: date); } else if (widget.walletType == WalletType.zcash) { height = await zcash!.getHeightByDate(date); diff --git a/lib/src/widgets/bottom_sheet/cake_pay_transaction_sent_bottom_sheet.dart b/lib/src/widgets/bottom_sheet/cake_pay_transaction_sent_bottom_sheet.dart index e98ead96a..b546de0b4 100644 --- a/lib/src/widgets/bottom_sheet/cake_pay_transaction_sent_bottom_sheet.dart +++ b/lib/src/widgets/bottom_sheet/cake_pay_transaction_sent_bottom_sheet.dart @@ -49,8 +49,7 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget { color: Theme.of(ctx).colorScheme.onSurfaceVariant, ); - Widget _buildHeader(BuildContext ctx) => - Column( + Widget _buildHeader(BuildContext ctx) => Column( children: [ const SizedBox(height: 12), Container( @@ -81,8 +80,7 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget { ], ); - Widget _buildBody(BuildContext context) => - Padding( + Widget _buildBody(BuildContext context) => Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: Column( children: [ @@ -123,7 +121,6 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget { copyButtonOnPressed: () async => await Clipboard.setData(ClipboardData(text: paymentIdValue)), ), - Padding( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), child: Column( @@ -157,10 +154,7 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget { return ClipRRect( borderRadius: const BorderRadius.vertical(top: Radius.circular(30)), child: Material( - color: Theme - .of(context) - .colorScheme - .surface, + color: Theme.of(context).colorScheme.surface, child: ConstrainedBox( constraints: BoxConstraints(maxHeight: maxHeight), child: SingleChildScrollView( @@ -210,17 +204,14 @@ class _StandardTile extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), - color: Theme - .of(context) - .colorScheme - .surfaceContainerLowest - .withAlpha(80)), + color: Theme.of(context).colorScheme.surfaceContainerLowest.withAlpha(80)), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Row( - mainAxisAlignment: copyButton ? MainAxisAlignment.spaceBetween : MainAxisAlignment.start, + mainAxisAlignment: + copyButton ? MainAxisAlignment.spaceBetween : MainAxisAlignment.start, children: [ if (imagePath != null) Padding( diff --git a/lib/src/widgets/bottom_sheet/confirm_sending_bottom_sheet_widget.dart b/lib/src/widgets/bottom_sheet/confirm_sending_bottom_sheet_widget.dart index 965365285..91581fe94 100644 --- a/lib/src/widgets/bottom_sheet/confirm_sending_bottom_sheet_widget.dart +++ b/lib/src/widgets/bottom_sheet/confirm_sending_bottom_sheet_widget.dart @@ -168,7 +168,8 @@ class ConfirmSendingBottomSheet extends BaseBottomSheet { final batchContactTitle = '${index + 1}/${outputs.length} - ${contactName.isEmpty ? 'Address' : contactName}'; final _address = item.isParsedAddress ? item.extractedAddress : item.address; - final _amount = '${item.cryptoAmount.sanitized()} ${amountParsingProxy?.getCryptoSymbol(currency) ?? currency.title}'; + final _amount = + '${item.cryptoAmount.sanitized()} ${amountParsingProxy?.getCryptoSymbol(currency) ?? currency.title}'; return isBatchSending || (contactName.isNotEmpty && !isCakePayName) ? ExpansionAddressTile( contactType: isOpenCryptoPay ? 'Open CryptoPay' : S.of(context).contact, diff --git a/lib/src/widgets/bottom_sheet/info_bottom_sheet_widget.dart b/lib/src/widgets/bottom_sheet/info_bottom_sheet_widget.dart index 08705e18b..d343bc5f4 100644 --- a/lib/src/widgets/bottom_sheet/info_bottom_sheet_widget.dart +++ b/lib/src/widgets/bottom_sheet/info_bottom_sheet_widget.dart @@ -100,7 +100,9 @@ class InfoBottomSheet extends BaseBottomSheet { ) else Container(), - SizedBox(height: 24,), + SizedBox( + height: 24, + ), if (content != null) Expanded( flex: 2, diff --git a/lib/src/widgets/bottom_sheet/info_steps_bottom_sheet_widget.dart b/lib/src/widgets/bottom_sheet/info_steps_bottom_sheet_widget.dart index 1e78e4170..d14dab095 100644 --- a/lib/src/widgets/bottom_sheet/info_steps_bottom_sheet_widget.dart +++ b/lib/src/widgets/bottom_sheet/info_steps_bottom_sheet_widget.dart @@ -34,7 +34,7 @@ class InfoStepsBottomSheet extends BaseBottomSheet { ), child: SingleChildScrollView( child: Column( - children: [ + children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 28.0), child: Column( @@ -96,18 +96,18 @@ class InfoStepsBottomSheet extends BaseBottomSheet { )) .toList(), ), - ), - Padding( - padding: const EdgeInsets.all(16), - child: PrimaryButton( - text: S.of(context).close, - color: context.currentTheme.colorScheme.primary, + ), + Padding( + padding: const EdgeInsets.all(16), + child: PrimaryButton( + text: S.of(context).close, + color: context.currentTheme.colorScheme.primary, textColor: context.currentTheme.colorScheme.onPrimary, onPressed: () => Navigator.of(context).pop(), - ), - ) - ], - ), + ), + ) + ], + ), ), ), ); diff --git a/lib/src/widgets/bottom_sheet/payment_confirmation_bottom_sheet.dart b/lib/src/widgets/bottom_sheet/payment_confirmation_bottom_sheet.dart index 9540566c9..3c1dfde4f 100644 --- a/lib/src/widgets/bottom_sheet/payment_confirmation_bottom_sheet.dart +++ b/lib/src/widgets/bottom_sheet/payment_confirmation_bottom_sheet.dart @@ -139,8 +139,7 @@ class _PaymentConfirmationContent extends StatelessWidget { clipBehavior: Clip.none, children: [ Image.asset( - paymentFlowResult.addressDetectionResult?.detectedCurrency?.iconPath ?? - '', + paymentFlowResult.addressDetectionResult?.detectedCurrency?.iconPath ?? '', width: 70, height: 70, errorBuilder: (context, error, stackTrace) => Icon( diff --git a/lib/src/widgets/bottom_sheet/swap_confirmation_bottom_sheet.dart b/lib/src/widgets/bottom_sheet/swap_confirmation_bottom_sheet.dart index 3b2a9e630..2e6951354 100644 --- a/lib/src/widgets/bottom_sheet/swap_confirmation_bottom_sheet.dart +++ b/lib/src/widgets/bottom_sheet/swap_confirmation_bottom_sheet.dart @@ -288,7 +288,8 @@ class SwapConfirmationContentState extends State { SwapConfirmationTextfield( key: ValueKey('swap_confirmation_bottomsheet_address_textfield_key'), isAddress: true, - walletType: cryptoCurrencyOrTokenToWalletType(widget.exchangeViewModel.receiveCurrency), + walletType: + cryptoCurrencyOrTokenToWalletType(widget.exchangeViewModel.receiveCurrency), hintText: 'Destination Address', focusNode: _addressFocus, controller: _addressController, diff --git a/lib/src/widgets/bottom_sheet/swap_details_bottom_sheet.dart b/lib/src/widgets/bottom_sheet/swap_details_bottom_sheet.dart index 12544c8b6..ea563cab2 100644 --- a/lib/src/widgets/bottom_sheet/swap_details_bottom_sheet.dart +++ b/lib/src/widgets/bottom_sheet/swap_details_bottom_sheet.dart @@ -112,7 +112,7 @@ class _SwapDetailsBottomSheetState extends State { buttonAction: () { _showingFailureDialog = false; Navigator.of(popupContext).pop(); - if(mounted) { + if (mounted) { Navigator.of(context, rootNavigator: true).pop(); } }, @@ -318,15 +318,13 @@ class _SwapDetailsContent extends StatelessWidget { children: [ _SwapDetailsTile( label: 'You Send', - value: - '${trade.amount} ${trade.from?.title ?? ''}', + value: '${trade.amount} ${trade.from?.title ?? ''}', valueFiatFormatted: exchangeTradeViewModel.sendAmountFiatFormatted, ), const SizedBox(height: 8), _SwapDetailsTile( label: 'You Get', - value: - '${trade.receiveAmount ?? '0'} ${trade.to?.title ?? ''}', + value: '${trade.receiveAmount ?? '0'} ${trade.to?.title ?? ''}', valueFiatFormatted: exchangeTradeViewModel .getReceiveAmountFiatFormatted(trade.receiveAmount ?? '0.0'), ), @@ -351,7 +349,8 @@ class _SwapDetailsContent extends StatelessWidget { const SizedBox(height: 4), AddressFormatter.buildSegmentedAddress( address: trade.payoutAddress ?? '', - walletType: trade.to != null ? cryptoCurrencyOrTokenToWalletType(trade.to!) : null, + walletType: + trade.to != null ? cryptoCurrencyOrTokenToWalletType(trade.to!) : null, evenTextStyle: Theme.of(context) .textTheme .bodyMedium! diff --git a/lib/src/widgets/bottom_sheet/token_selection_bottom_sheet.dart b/lib/src/widgets/bottom_sheet/token_selection_bottom_sheet.dart index 3910f876c..c26045333 100644 --- a/lib/src/widgets/bottom_sheet/token_selection_bottom_sheet.dart +++ b/lib/src/widgets/bottom_sheet/token_selection_bottom_sheet.dart @@ -78,7 +78,8 @@ class _TokenSelectionContentState extends State<_TokenSelectionContent> { void initState() { super.initState(); final baseNetwork = widget.fixedNetwork ?? WalletType.ethereum; - selectedNetwork = _resolveGenericETHDetectionResultToSpecificChain(baseNetwork, widget.paymentRequest.scheme.isNotEmpty); + selectedNetwork = _resolveGenericETHDetectionResultToSpecificChain( + baseNetwork, widget.paymentRequest.scheme.isNotEmpty); _autoSelectToken(); } @@ -101,9 +102,10 @@ class _TokenSelectionContentState extends State<_TokenSelectionContent> { return null; } - WalletType _resolveGenericETHDetectionResultToSpecificChain(WalletType network, bool hasURIScheme) { - if(hasURIScheme || network != WalletType.ethereum) return network; - + WalletType _resolveGenericETHDetectionResultToSpecificChain( + WalletType network, bool hasURIScheme) { + if (hasURIScheme || network != WalletType.ethereum) return network; + final current = widget.paymentViewModel.currentWalletType; if (isEVMCompatibleChain(current)) return current; return network; @@ -113,7 +115,8 @@ class _TokenSelectionContentState extends State<_TokenSelectionContent> { final initialNetwork = selectedNetwork; if (initialNetwork == null || !mounted) return; - final network = _resolveGenericETHDetectionResultToSpecificChain(initialNetwork, widget.paymentRequest.scheme.isNotEmpty); + final network = _resolveGenericETHDetectionResultToSpecificChain( + initialNetwork, widget.paymentRequest.scheme.isNotEmpty); setState(() { selectedNetwork = network; diff --git a/lib/src/widgets/cake_image_widget.dart b/lib/src/widgets/cake_image_widget.dart index 5b865324c..efd843504 100644 --- a/lib/src/widgets/cake_image_widget.dart +++ b/lib/src/widgets/cake_image_widget.dart @@ -87,7 +87,11 @@ class CakeImageWidget extends StatelessWidget { allowDrawingOutsideViewBox: allowDrawingOutsideViewBox ?? false, fit: fit ?? BoxFit.contain, placeholderBuilder: (_) { - return loadingWidget ?? SizedBox(height: height, width: width, child: Center(child: CupertinoActivityIndicator())); + return loadingWidget ?? + SizedBox( + height: height, + width: width, + child: Center(child: CupertinoActivityIndicator())); }, errorBuilder: (_, __, ___) => _buildErrorWidget(context), ) @@ -100,7 +104,11 @@ class CakeImageWidget extends StatelessWidget { filterQuality: filterQuality ?? FilterQuality.medium, loadingBuilder: (_, Widget child, ImageChunkEvent? progress) { if (progress == null) return child; - return loadingWidget ?? SizedBox(height: height, width: width, child: Center(child: CupertinoActivityIndicator())); + return loadingWidget ?? + SizedBox( + height: height, + width: width, + child: Center(child: CupertinoActivityIndicator())); }, errorBuilder: (_, __, ___) => _buildErrorWidget(context), ); diff --git a/lib/src/widgets/check_box_picker.dart b/lib/src/widgets/check_box_picker.dart index 67aefba14..a43e501ab 100644 --- a/lib/src/widgets/check_box_picker.dart +++ b/lib/src/widgets/check_box_picker.dart @@ -40,12 +40,11 @@ class CheckBoxPickerState extends State { widget.title, textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodyMedium!.copyWith( - fontSize: 18, - - fontWeight: FontWeight.bold, - decoration: TextDecoration.none, - color: Theme.of(context).colorScheme.onSurface, - ), + fontSize: 18, + fontWeight: FontWeight.bold, + decoration: TextDecoration.none, + color: Theme.of(context).colorScheme.onSurface, + ), ), ), Padding( diff --git a/lib/src/widgets/checkbox_widget.dart b/lib/src/widgets/checkbox_widget.dart index f891de092..aa0ad9731 100644 --- a/lib/src/widgets/checkbox_widget.dart +++ b/lib/src/widgets/checkbox_widget.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; + class CheckboxWidget extends StatefulWidget { CheckboxWidget({required this.value, required this.caption, required this.onChanged}); diff --git a/lib/src/widgets/evm_switcher.dart b/lib/src/widgets/evm_switcher.dart index fb0cc9626..0e9799af5 100644 --- a/lib/src/widgets/evm_switcher.dart +++ b/lib/src/widgets/evm_switcher.dart @@ -83,8 +83,7 @@ class _EvmSwitcherState extends State { .toList(growable: false); } - bool _hiddenSetsEqual(Set a, Set b) => - a.length == b.length && a.containsAll(b); + bool _hiddenSetsEqual(Set a, Set b) => a.length == b.length && a.containsAll(b); int get _selectedIndex { if (widget.currentChain == null) return -1; diff --git a/lib/src/widgets/haven_wallet_removal_popup.dart b/lib/src/widgets/haven_wallet_removal_popup.dart index e4c1767a7..4e3793aba 100644 --- a/lib/src/widgets/haven_wallet_removal_popup.dart +++ b/lib/src/widgets/haven_wallet_removal_popup.dart @@ -36,12 +36,11 @@ class HavenWalletRemovalPopup extends StatelessWidget { alignment: Alignment.bottomCenter, child: DefaultTextStyle( style: Theme.of(context).textTheme.bodyMedium!.copyWith( - decoration: TextDecoration.none, - fontSize: 24.0, - fontWeight: FontWeight.bold, - - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), + decoration: TextDecoration.none, + fontSize: 24.0, + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), child: Text("Emergency Notice"), ), ), @@ -61,11 +60,10 @@ class HavenWalletRemovalPopup extends StatelessWidget { child: Text( "It looks like you have Haven wallets in your list. Haven is getting removed in next release of Cake Wallet, and you currently have Haven in the following wallets:\n\n[${affectedWalletNames.join(", ")}]\n\nPlease move your funds to other wallet, as you will lose access to your Haven funds in next update.\n\nFor assistance, please use the in-app support or email support@cakewallet.com", style: Theme.of(context).textTheme.bodyMedium!.copyWith( - decoration: TextDecoration.none, - fontSize: 16.0, - - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), + decoration: TextDecoration.none, + fontSize: 16.0, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), ), ) ], diff --git a/lib/src/widgets/index.dart b/lib/src/widgets/index.dart index e69de29bb..8b1378917 100644 --- a/lib/src/widgets/index.dart +++ b/lib/src/widgets/index.dart @@ -0,0 +1 @@ + diff --git a/lib/src/widgets/new_list_row/list_Item_style_wrapper.dart b/lib/src/widgets/new_list_row/list_Item_style_wrapper.dart index 110bf01e0..600ff4269 100644 --- a/lib/src/widgets/new_list_row/list_Item_style_wrapper.dart +++ b/lib/src/widgets/new_list_row/list_Item_style_wrapper.dart @@ -45,26 +45,28 @@ class ListItemStyleWrapper extends StatelessWidget { bottom: Radius.circular(isLastInSection ? 18 : 0), ); - return ClipRSuperellipse( - borderRadius: radius, - child: Column( - children: [ - Container( - height: height, - decoration: ShapeDecoration( - shape: RoundedSuperellipseBorder( - borderRadius: radius, - ), - color: backgroundColor ?? theme.colorScheme.surfaceContainer, + return ClipRSuperellipse( + borderRadius: radius, + child: Column( + children: [ + Container( + height: height, + decoration: ShapeDecoration( + shape: RoundedSuperellipseBorder( + borderRadius: radius, ), - child: Material( - color: Colors.transparent, - child: InkWell( - onTap: onTap, - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 12, vertical: height == null ? 11 : 0), - child: builder(context, textStyle, labelStyle))))), - if(iconPath != null && isLastInSection == false) Container( + color: backgroundColor ?? theme.colorScheme.surfaceContainer, + ), + child: Material( + color: Colors.transparent, + child: InkWell( + onTap: onTap, + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: 12, vertical: height == null ? 11 : 0), + child: builder(context, textStyle, labelStyle))))), + if (iconPath != null && isLastInSection == false) + Container( color: theme.colorScheme.surfaceContainer, child: Padding( padding: const EdgeInsets.only(left: 50, right: 13), diff --git a/lib/src/widgets/new_list_row/list_item_checkbox_widget.dart b/lib/src/widgets/new_list_row/list_item_checkbox_widget.dart index a2a75d552..6c4d769ba 100644 --- a/lib/src/widgets/new_list_row/list_item_checkbox_widget.dart +++ b/lib/src/widgets/new_list_row/list_item_checkbox_widget.dart @@ -14,7 +14,10 @@ class ListItemCheckboxWidget extends StatefulWidget { this.subtitleColor, this.onTap, this.isFirstInSection = false, - this.isLastInSection = false, this.subtitle, this.iconPath, this.showArrow = false, + this.isLastInSection = false, + this.subtitle, + this.iconPath, + this.showArrow = false, }); final String keyValue; @@ -34,15 +37,14 @@ class ListItemCheckboxWidget extends StatefulWidget { } class _ListItemCheckboxWidgetState extends State { - - @override Widget build(BuildContext context) { return ListItemStyleWrapper( iconPath: widget.iconPath, - onTap: widget.onTap ?? () { - widget.onChanged(!widget.value); - }, + onTap: widget.onTap ?? + () { + widget.onChanged(!widget.value); + }, isFirstInSection: widget.isFirstInSection, height: widget.subtitle != null ? 64 : 50, isLastInSection: widget.isLastInSection, @@ -56,7 +58,7 @@ class _ListItemCheckboxWidgetState extends State { children: [ if (widget.iconPath != null) widget.iconPath!.toLowerCase().endsWith("svg") - ? CakeImageWidget(imageUrl:widget.iconPath!, height: 26, width: 26) + ? CakeImageWidget(imageUrl: widget.iconPath!, height: 26, width: 26) : Image.asset( widget.iconPath!, width: 26, @@ -64,29 +66,31 @@ class _ListItemCheckboxWidgetState extends State { ), Expanded( child: Column( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Row( - children: [ - Flexible(child: Text(widget.label)), - if (widget.showArrow) - Icon( - Icons.chevron_right, - size: 18, - color: Theme.of(context).colorScheme.onSurfaceVariant, - ) - ], - ), - if (widget.subtitle != null) - Text( - widget.subtitle!, - style: TextStyle( - fontSize: 12, color: widget.subtitleColor ?? Theme.of(context).colorScheme.onSurfaceVariant), - ) - ], - ), + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + children: [ + Flexible(child: Text(widget.label)), + if (widget.showArrow) + Icon( + Icons.chevron_right, + size: 18, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ) + ], + ), + if (widget.subtitle != null) + Text( + widget.subtitle!, + style: TextStyle( + fontSize: 12, + color: widget.subtitleColor ?? + Theme.of(context).colorScheme.onSurfaceVariant), + ) + ], + ), ), ], ), diff --git a/lib/src/widgets/new_list_row/list_item_regular_row_widget.dart b/lib/src/widgets/new_list_row/list_item_regular_row_widget.dart index dc552fd66..8acbbcb2a 100644 --- a/lib/src/widgets/new_list_row/list_item_regular_row_widget.dart +++ b/lib/src/widgets/new_list_row/list_item_regular_row_widget.dart @@ -23,13 +23,14 @@ class ListItemRegularRowWidget extends StatelessWidget { this.foregroundColor, this.trailingIconSize, this.bottomWidget, - this.subtitleColor, + this.subtitleColor, this.trailingWidget, this.copyableText, this.leadingIconErrorWidget, this.leadingIconSize, this.badgeIconSize, - this.iconColor, this.secondaryLabel}); + this.iconColor, + this.secondaryLabel}); final String keyValue; final String label; @@ -88,7 +89,7 @@ class ListItemRegularRowWidget extends StatelessWidget { final imageWidget = badgeIconPath != null ? Stack( - clipBehavior: Clip.none, + clipBehavior: Clip.none, children: [ leadingIcon!, Positioned( @@ -141,14 +142,21 @@ class ListItemRegularRowWidget extends StatelessWidget { style: foregroundColor == null ? textStyle : textStyle.copyWith(color: foregroundColor)), - if(secondaryLabel != null) - Text(secondaryLabel!, style: textStyle.copyWith(color: Theme.of(context).colorScheme.onSurfaceVariant),) + if (secondaryLabel != null) + Text( + secondaryLabel!, + style: textStyle.copyWith( + color: + Theme.of(context).colorScheme.onSurfaceVariant), + ) ], ), if (subtitle != null) Text( subtitle!, - style: subtitleColor == null ? labelStyle.copyWith(fontSize: 12) : labelStyle.copyWith(fontSize: 12, color: subtitleColor), + style: subtitleColor == null + ? labelStyle.copyWith(fontSize: 12) + : labelStyle.copyWith(fontSize: 12, color: subtitleColor), ), ], ), diff --git a/lib/src/widgets/new_list_row/list_item_selector_widget.dart b/lib/src/widgets/new_list_row/list_item_selector_widget.dart index c0b9b9809..fc7071445 100644 --- a/lib/src/widgets/new_list_row/list_item_selector_widget.dart +++ b/lib/src/widgets/new_list_row/list_item_selector_widget.dart @@ -12,7 +12,8 @@ class ListItemSelectorWidget extends StatelessWidget { required this.selectedIndex, required this.onChanged, this.isFirstInSection = false, - this.isLastInSection = false, this.onTap, + this.isLastInSection = false, + this.onTap, }); final String keyValue; @@ -45,16 +46,16 @@ class ListItemSelectorWidget extends StatelessWidget { options[selectedIndex], style: labelStyle, ), - CakeImageWidget(imageUrl: - "assets/new-ui/chooser.svg", + CakeImageWidget( + imageUrl: "assets/new-ui/chooser.svg", colorFilter: - ColorFilter.mode(theme.colorScheme.onSurfaceVariant, BlendMode.srcIn), + ColorFilter.mode(theme.colorScheme.onSurfaceVariant, BlendMode.srcIn), ), ], ), ), ], - ); + ); }); } -} \ No newline at end of file +} diff --git a/lib/src/widgets/new_list_row/list_item_text_field_widget.dart b/lib/src/widgets/new_list_row/list_item_text_field_widget.dart index fd66df2c0..df91970dc 100644 --- a/lib/src/widgets/new_list_row/list_item_text_field_widget.dart +++ b/lib/src/widgets/new_list_row/list_item_text_field_widget.dart @@ -26,8 +26,7 @@ class ListItemTextFieldWidget extends StatefulWidget { final bool isLastInSection; @override - State createState() => - _ListItemTextFieldWidgetState(); + State createState() => _ListItemTextFieldWidgetState(); } class _ListItemTextFieldWidgetState extends State { @@ -36,7 +35,7 @@ class _ListItemTextFieldWidgetState extends State { return ListItemStyleWrapper( isFirstInSection: widget.isFirstInSection, isLastInSection: widget.isLastInSection, - height:50, + height: 50, builder: (context, textStyle, labelStyle) { return Row( children: [ diff --git a/lib/src/widgets/new_list_row/list_item_toggle_widget.dart b/lib/src/widgets/new_list_row/list_item_toggle_widget.dart index 899fcd246..c1743445d 100644 --- a/lib/src/widgets/new_list_row/list_item_toggle_widget.dart +++ b/lib/src/widgets/new_list_row/list_item_toggle_widget.dart @@ -27,7 +27,6 @@ class ListItemToggleWidget extends StatefulWidget { } class _ListItemToggleWidgetState extends State { - @override void initState() { super.initState(); @@ -52,7 +51,7 @@ class _ListItemToggleWidgetState extends State { Flexible( child: Text(widget.label, style: textStyle, softWrap: true), ), - if(widget.leadingEndWidget != null) widget.leadingEndWidget! + if (widget.leadingEndWidget != null) widget.leadingEndWidget! ], ), ), diff --git a/lib/src/widgets/new_list_row/new_list_section.dart b/lib/src/widgets/new_list_row/new_list_section.dart index 7b65f690a..b887ab715 100644 --- a/lib/src/widgets/new_list_row/new_list_section.dart +++ b/lib/src/widgets/new_list_row/new_list_section.dart @@ -14,15 +14,14 @@ import 'package:cake_wallet/src/widgets/new_list_row/list_item_toggle_widget.dar import 'package:flutter/material.dart'; class NewListSections extends StatelessWidget { - const NewListSections({ - super.key, - required this.sections, - this.controllers = const {}, - this.tapHandlers = const {}, - this.getCheckboxValue, - this.updateCheckboxValue, - this.showHeader = false - }); + const NewListSections( + {super.key, + required this.sections, + this.controllers = const {}, + this.tapHandlers = const {}, + this.getCheckboxValue, + this.updateCheckboxValue, + this.showHeader = false}); final Map> sections; final Map controllers; diff --git a/lib/src/widgets/number_text_fild_widget.dart b/lib/src/widgets/number_text_fild_widget.dart index 03c5a3cd1..6c5c1c8f5 100644 --- a/lib/src/widgets/number_text_fild_widget.dart +++ b/lib/src/widgets/number_text_fild_widget.dart @@ -56,7 +56,7 @@ class _NumberTextFieldState extends State { @override Widget build(BuildContext context) => TextField( - style: Theme.of(context).textTheme.titleMedium!, + style: Theme.of(context).textTheme.titleMedium!, enableInteractiveSelection: false, textAlign: TextAlign.center, textAlignVertical: TextAlignVertical.bottom, @@ -78,16 +78,16 @@ class _NumberTextFieldState extends State { type: MaterialType.transparency, child: InkWell( child: Container( - width: widget.arrowsWidth, + width: widget.arrowsWidth, alignment: Alignment.bottomCenter, - child: Icon(Icons.keyboard_arrow_left_outlined ,size: widget.arrowsWidth)), + child: Icon(Icons.keyboard_arrow_left_outlined, size: widget.arrowsWidth)), onTap: _canGoDown ? () => _update(false) : null)), suffixIcon: Material( type: MaterialType.transparency, child: InkWell( child: Container( - width: widget.arrowsWidth, - alignment: Alignment.bottomCenter, + width: widget.arrowsWidth, + alignment: Alignment.bottomCenter, child: Icon(Icons.keyboard_arrow_right_outlined, size: widget.arrowsWidth)), onTap: _canGoUp ? () => _update(true) : null))), maxLines: 1, diff --git a/lib/src/widgets/picker.dart b/lib/src/widgets/picker.dart index 4934010c2..117c15d95 100644 --- a/lib/src/widgets/picker.dart +++ b/lib/src/widgets/picker.dart @@ -345,7 +345,8 @@ class _PickerState extends State> { Flexible( child: Text( key: ValueKey('picker_items_index_${itemName}_text_key'), - widget.displayItem?.call(item) ?? (item == CryptoCurrency.btcln ? "BTC (LN)" : item.toString()), + widget.displayItem?.call(item) ?? + (item == CryptoCurrency.btcln ? "BTC (LN)" : item.toString()), softWrap: true, style: Theme.of(context).textTheme.bodyMedium!.copyWith( fontWeight: FontWeight.w600, diff --git a/lib/src/widgets/picker_inner_wrapper_widget.dart b/lib/src/widgets/picker_inner_wrapper_widget.dart index c8a5f81e9..5714719f4 100644 --- a/lib/src/widgets/picker_inner_wrapper_widget.dart +++ b/lib/src/widgets/picker_inner_wrapper_widget.dart @@ -3,8 +3,7 @@ import 'package:cake_wallet/src/widgets/picker_wrapper_widget.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart'; class PickerInnerWrapperWidget extends StatelessWidget { - PickerInnerWrapperWidget( - {required this.children, this.title, this.itemsHeight}); + PickerInnerWrapperWidget({required this.children, this.title, this.itemsHeight}); final List children; final String? title; @@ -48,10 +47,9 @@ class PickerInnerWrapperWidget extends StatelessWidget { color: Theme.of(context).colorScheme.surfaceContainerHighest, child: ConstrainedBox( constraints: BoxConstraints( - maxHeight: - itemsHeight != null && itemsHeight! <= containerHeight - ? itemsHeight! - : containerHeight, + maxHeight: itemsHeight != null && itemsHeight! <= containerHeight + ? itemsHeight! + : containerHeight, maxWidth: ResponsiveLayoutUtilBase.kPopupWidth, ), child: Column( diff --git a/lib/src/widgets/provider_optoin_tile.dart b/lib/src/widgets/provider_optoin_tile.dart index 45557309c..7bd7f70d0 100644 --- a/lib/src/widgets/provider_optoin_tile.dart +++ b/lib/src/widgets/provider_optoin_tile.dart @@ -105,8 +105,8 @@ class ProviderOptionTile extends StatelessWidget { children: [ Row( children: [ - ImageUtil.getImageFromPath(imagePath:imagePath, - height: imageHeight, width: imageWidth), + ImageUtil.getImageFromPath( + imagePath: imagePath, height: imageHeight, width: imageWidth), SizedBox(width: 8), Expanded( child: Container( diff --git a/lib/src/widgets/rounded_checkbox.dart b/lib/src/widgets/rounded_checkbox.dart index 8cd24e602..97a5ffda4 100644 --- a/lib/src/widgets/rounded_checkbox.dart +++ b/lib/src/widgets/rounded_checkbox.dart @@ -9,19 +9,19 @@ class RoundedCheckbox extends StatelessWidget { @override Widget build(BuildContext context) { - return value - ? Container( - height: 20.0, - width: 20.0, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(50.0)), - color: Theme.of(context).colorScheme.primary, - ), - child: Icon( - Icons.check, - color: Theme.of(context).colorScheme.surface, - size: 14.0, - )) - : Offstage(); + return value + ? Container( + height: 20.0, + width: 20.0, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(50.0)), + color: Theme.of(context).colorScheme.primary, + ), + child: Icon( + Icons.check, + color: Theme.of(context).colorScheme.surface, + size: 14.0, + )) + : Offstage(); } } diff --git a/lib/src/widgets/rounded_icon_button.dart b/lib/src/widgets/rounded_icon_button.dart index 72d5491f3..912e67d49 100644 --- a/lib/src/widgets/rounded_icon_button.dart +++ b/lib/src/widgets/rounded_icon_button.dart @@ -27,7 +27,7 @@ class RoundedIconButton extends StatelessWidget { onPressed: onPressed, fillColor: fillColor ?? colorScheme.surfaceContainerHighest, elevation: 0, - constraints: BoxConstraints.tightFor(width: width ?? 30, height: height ?? 30), + constraints: BoxConstraints.tightFor(width: width ?? 30, height: height ?? 30), padding: EdgeInsets.zero, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, shape: shape ?? const CircleBorder(), diff --git a/lib/src/widgets/scrollable_with_bottom_section.dart b/lib/src/widgets/scrollable_with_bottom_section.dart index 295e5ca2d..07a070204 100644 --- a/lib/src/widgets/scrollable_with_bottom_section.dart +++ b/lib/src/widgets/scrollable_with_bottom_section.dart @@ -52,4 +52,4 @@ class ScrollableWithBottomSectionState extends State { cursorColor: Theme.of(context).colorScheme.primary, backgroundCursorColor: Theme.of(context).colorScheme.primary, validStyle: Theme.of(context).textTheme.bodyMedium!.copyWith( - color: Theme.of(context).colorScheme.onSurfaceVariant, - backgroundColor: Colors.transparent, - fontWeight: FontWeight.normal, - fontSize: 16, - ), + color: Theme.of(context).colorScheme.onSurfaceVariant, + backgroundColor: Colors.transparent, + fontWeight: FontWeight.normal, + fontSize: 16, + ), invalidStyle: Theme.of(context).textTheme.bodyMedium!.copyWith( color: Theme.of(context).colorScheme.errorContainer, backgroundColor: Colors.transparent, @@ -157,7 +157,8 @@ class SeedWidgetState extends State { padding: EdgeInsets.all(6), decoration: ShapeDecoration( color: Theme.of(context).colorScheme.surface, - shape: RoundedSuperellipseBorder(borderRadius: BorderRadius.circular(18)), + shape: RoundedSuperellipseBorder( + borderRadius: BorderRadius.circular(18)), ), child: Image.asset( 'assets/images/paste_ios.png', diff --git a/lib/src/widgets/seedphrase_grid_widget.dart b/lib/src/widgets/seedphrase_grid_widget.dart index a32cf1c1c..be1d34f43 100644 --- a/lib/src/widgets/seedphrase_grid_widget.dart +++ b/lib/src/widgets/seedphrase_grid_widget.dart @@ -9,8 +9,6 @@ class SeedPhraseGridWidget extends StatelessWidget { final List list; - - @override Widget build(BuildContext context) { int minTiles = 1; @@ -23,7 +21,6 @@ class SeedPhraseGridWidget extends StatelessWidget { int crossAxisCount = ((screenWidth + spacing - (2 * padding)) / (desiredTileWidth + spacing)).floor(); - if (crossAxisCount > maxTiles) crossAxisCount = maxTiles; if (crossAxisCount < minTiles) crossAxisCount = minTiles; @@ -44,9 +41,8 @@ class SeedPhraseGridWidget extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 8), alignment: Alignment.center, decoration: BoxDecoration( - borderRadius: BorderRadius.circular(8), - color: Theme.of(context).colorScheme.surfaceContainerHigh - ), + borderRadius: BorderRadius.circular(8), + color: Theme.of(context).colorScheme.surfaceContainerHigh), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ diff --git a/lib/src/widgets/simple_checkbox.dart b/lib/src/widgets/simple_checkbox.dart index 850d9bac1..93d7b8abc 100644 --- a/lib/src/widgets/simple_checkbox.dart +++ b/lib/src/widgets/simple_checkbox.dart @@ -26,9 +26,9 @@ class _SimpleCheckboxState extends State { checkColor: Theme.of(context).textTheme.titleLarge!.color, activeColor: Colors.transparent, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - side: WidgetStateBorderSide.resolveWith((states) => BorderSide( - color: Theme.of(context).textTheme.titleLarge!.color!, width: 1.0)), + side: WidgetStateBorderSide.resolveWith((states) => + BorderSide(color: Theme.of(context).textTheme.titleLarge!.color!, width: 1.0)), ), ); } -} \ No newline at end of file +} diff --git a/lib/src/widgets/standard_checkbox.dart b/lib/src/widgets/standard_checkbox.dart index 64de5337d..d67e368ac 100644 --- a/lib/src/widgets/standard_checkbox.dart +++ b/lib/src/widgets/standard_checkbox.dart @@ -66,12 +66,11 @@ class StandardCheckbox extends StatelessWidget { caption, softWrap: true, style: Theme.of(context).textTheme.bodyMedium!.copyWith( - fontSize: 16.0, - - fontWeight: FontWeight.normal, - color: captionColor ?? Theme.of(context).colorScheme.onSurface, - decoration: TextDecoration.none, - ), + fontSize: 16.0, + fontWeight: FontWeight.normal, + color: captionColor ?? Theme.of(context).colorScheme.onSurface, + decoration: TextDecoration.none, + ), ), ), ) diff --git a/lib/src/widgets/standard_list.dart b/lib/src/widgets/standard_list.dart index f0afdcde5..52a06a740 100644 --- a/lib/src/widgets/standard_list.dart +++ b/lib/src/widgets/standard_list.dart @@ -50,7 +50,6 @@ class StandardListRow extends StatelessWidget { Widget? buildLeading(BuildContext context) => null; Widget buildCenter(BuildContext context, {required bool hasLeftOffset}) { - return Expanded( child: Row( mainAxisAlignment: MainAxisAlignment.start, @@ -84,17 +83,17 @@ class StandardListRow extends StatelessWidget { ), ), ) - else - Align( - alignment: Alignment.centerLeft, - child: Text( - title, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: titleColor(context), - fontWeight: isSelected ? FontWeight.w800 : FontWeight.w400, + else + Align( + alignment: Alignment.centerLeft, + child: Text( + title, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: titleColor(context), + fontWeight: isSelected ? FontWeight.w800 : FontWeight.w400, + ), ), ), - ), ], ), ) diff --git a/lib/src/widgets/standard_list_status_row.dart b/lib/src/widgets/standard_list_status_row.dart index 42032593d..7a84cb87a 100644 --- a/lib/src/widgets/standard_list_status_row.dart +++ b/lib/src/widgets/standard_list_status_row.dart @@ -47,9 +47,9 @@ class StandardListStatusRow extends StatelessWidget { Text( value, style: Theme.of(context).textTheme.bodyMedium!.copyWith( - fontSize: 16, - fontWeight: FontWeight.w500, - ), + fontSize: 16, + fontWeight: FontWeight.w500, + ), ) ], ), diff --git a/lib/src/widgets/standard_slide_button_widget.dart b/lib/src/widgets/standard_slide_button_widget.dart index 5a7c019c9..4f143c05f 100644 --- a/lib/src/widgets/standard_slide_button_widget.dart +++ b/lib/src/widgets/standard_slide_button_widget.dart @@ -113,7 +113,9 @@ class StandardSlideButtonState extends State { child: Icon( key: ValueKey('standard_slide_button_widget_slider_icon_key'), Icons.arrow_forward, - color: widget.isDisabled ? Theme.of(context).colorScheme.onSurface.withOpacity(0.2) : Theme.of(context).colorScheme.onSurface, + color: widget.isDisabled + ? Theme.of(context).colorScheme.onSurface.withOpacity(0.2) + : Theme.of(context).colorScheme.onSurface, ), ), ), diff --git a/lib/src/widgets/validable_annotated_editable_text.dart b/lib/src/widgets/validable_annotated_editable_text.dart index 7a01506d7..91400811d 100644 --- a/lib/src/widgets/validable_annotated_editable_text.dart +++ b/lib/src/widgets/validable_annotated_editable_text.dart @@ -65,7 +65,6 @@ class ValidatableAnnotatedEditableText extends EditableText { backgroundCursorColor: backgroundCursorColor, onChanged: onChanged, onSubmitted: onSubmitted, - toolbarOptions: const ToolbarOptions( copy: true, cut: true, @@ -120,17 +119,17 @@ class ValidatableAnnotatedEditableTextState extends EditableTextState { annotation = Annotation( range: TextRange(start: 0, end: item.range.start), style: Theme.of(context).textTheme.bodyMedium!.copyWith( - color: Theme.of(context).colorScheme.onSurfaceVariant, - backgroundColor: Colors.transparent, - ), + color: Theme.of(context).colorScheme.onSurfaceVariant, + backgroundColor: Colors.transparent, + ), ); } else if (prev.range.end < item.range.start) { annotation = Annotation( range: TextRange(start: prev.range.end, end: item.range.start), style: Theme.of(context).textTheme.bodyMedium!.copyWith( - color: Theme.of(context).colorScheme.onError, - backgroundColor: Colors.transparent, - ), + color: Theme.of(context).colorScheme.onError, + backgroundColor: Colors.transparent, + ), ); } @@ -146,9 +145,9 @@ class ValidatableAnnotatedEditableTextState extends EditableTextState { Annotation( range: TextRange(start: result.last.range.end, end: text.length), style: Theme.of(context).textTheme.bodyMedium!.copyWith( - color: Theme.of(context).colorScheme.onSurfaceVariant, - backgroundColor: Colors.transparent, - ), + color: Theme.of(context).colorScheme.onSurfaceVariant, + backgroundColor: Colors.transparent, + ), ), ); } diff --git a/lib/src/widgets/vulnerable_seeds_popup.dart b/lib/src/widgets/vulnerable_seeds_popup.dart index 2326360ae..b4c2dc3e8 100644 --- a/lib/src/widgets/vulnerable_seeds_popup.dart +++ b/lib/src/widgets/vulnerable_seeds_popup.dart @@ -36,12 +36,11 @@ class VulnerableSeedsPopup extends StatelessWidget { alignment: Alignment.bottomCenter, child: DefaultTextStyle( style: Theme.of(context).textTheme.bodyMedium!.copyWith( - decoration: TextDecoration.none, - fontSize: 24.0, - fontWeight: FontWeight.bold, - - color: Theme.of(context).colorScheme.onSurface, - ), + decoration: TextDecoration.none, + fontSize: 24.0, + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.onSurface, + ), child: Text("Emergency Notice"), ), ), @@ -61,12 +60,10 @@ class VulnerableSeedsPopup extends StatelessWidget { child: Text( "Your Bitcoin wallet(s) below use a legacy seed format that is vulnerable, which MAY result in you losing money from these wallet(s) if no action is taken.\nWe recommend that you IMMEDIATELY create wallet(s) in Cake Wallet and immediately transfer the funds to these wallet(s).\nVulnerable wallet name(s):\n\n[${affectedWalletNames.join(", ")}]\n\nFor assistance, please use the in-app support or email support@cakewallet.com", style: Theme.of(context).textTheme.bodyMedium!.copyWith( - decoration: TextDecoration.none, - fontSize: 16.0, - - color: Theme.of(context) - .colorScheme.onSurface, - ), + decoration: TextDecoration.none, + fontSize: 16.0, + color: Theme.of(context).colorScheme.onSurface, + ), ), ) ], diff --git a/lib/store/app_store.dart b/lib/store/app_store.dart index 70ff844d8..b3ac158d9 100644 --- a/lib/store/app_store.dart +++ b/lib/store/app_store.dart @@ -48,7 +48,6 @@ abstract class AppStoreBase with Store { SettingsStore settingsStore; - ThemeStore themeStore; @observable diff --git a/lib/store/dashboard/order_filter_store.dart b/lib/store/dashboard/order_filter_store.dart index d7f856d72..f82d4241b 100644 --- a/lib/store/dashboard/order_filter_store.dart +++ b/lib/store/dashboard/order_filter_store.dart @@ -30,8 +30,7 @@ abstract class OrderFilterStoreBase with Store { required List orders, required WalletBase wallet, }) { - final walletOrders = - orders.where((item) => item.order.walletId == wallet.id).toList(); + final walletOrders = orders.where((item) => item.order.walletId == wallet.id).toList(); final cakePayOrders = walletOrders.where((item) { final order = item.order; @@ -43,4 +42,4 @@ abstract class OrderFilterStoreBase with Store { if (!displayCakePay) return []; return cakePayOrders; } -} \ No newline at end of file +} diff --git a/lib/store/dashboard/payjoin_transactions_store.dart b/lib/store/dashboard/payjoin_transactions_store.dart index 9a22921bf..37841e412 100644 --- a/lib/store/dashboard/payjoin_transactions_store.dart +++ b/lib/store/dashboard/payjoin_transactions_store.dart @@ -8,8 +8,7 @@ import 'package:mobx/mobx.dart'; part 'payjoin_transactions_store.g.dart'; -class PayjoinTransactionsStore = PayjoinTransactionsStoreBase - with _$PayjoinTransactionsStore; +class PayjoinTransactionsStore = PayjoinTransactionsStoreBase with _$PayjoinTransactionsStore; abstract class PayjoinTransactionsStoreBase with Store { PayjoinTransactionsStoreBase({ diff --git a/lib/store/dashboard/trade_filter_store.dart b/lib/store/dashboard/trade_filter_store.dart index 57e3149ee..18d4038e7 100644 --- a/lib/store/dashboard/trade_filter_store.dart +++ b/lib/store/dashboard/trade_filter_store.dart @@ -71,20 +71,20 @@ abstract class TradeFilterStoreBase with Store { @computed int get enabledProvidersCount => [ - displayChangeNow, - displaySideShift, - displaySimpleSwap, - displayTrocador, - displayExolix, - displayChainflip, - displayThorChain, - displayLetsExchange, - displayStealthEx, - displayXOSwap, - displaySwapTrade, - displaySwapXyz, - displayNearIntents - ].where((item) => item).length; + displayChangeNow, + displaySideShift, + displaySimpleSwap, + displayTrocador, + displayExolix, + displayChainflip, + displayThorChain, + displayLetsExchange, + displayStealthEx, + displayXOSwap, + displaySwapTrade, + displaySwapXyz, + displayNearIntents + ].where((item) => item).length; @computed bool get displayAllTrades => @@ -189,12 +189,12 @@ abstract class TradeFilterStoreBase with Store { } List filtered({required List trades, required WalletBase wallet}) { - final _trades = trades - .where((item) { - final isSameChain = item.trade.chainId != null ? item.trade.chainId == wallet.chainId : true; // returning default as true here so it falls back to the default checks if there's no chainId - return item.trade.walletId == wallet.id && isTradeInAccount(item, wallet) && isSameChain; - }) - .toList(); + final _trades = trades.where((item) { + final isSameChain = item.trade.chainId != null + ? item.trade.chainId == wallet.chainId + : true; // returning default as true here so it falls back to the default checks if there's no chainId + return item.trade.walletId == wallet.id && isTradeInAccount(item, wallet) && isSameChain; + }).toList(); final needToFilter = !displayAllTrades; return needToFilter @@ -217,12 +217,14 @@ abstract class TradeFilterStoreBase with Store { item.trade.provider == ExchangeProviderDescription.thorChain) || (displayLetsExchange && item.trade.provider == ExchangeProviderDescription.letsExchange) || - (displayStealthEx && item.trade.provider == ExchangeProviderDescription.stealthEx) || + (displayStealthEx && + item.trade.provider == ExchangeProviderDescription.stealthEx) || (displayXOSwap && item.trade.provider == ExchangeProviderDescription.xoSwap) || - (displaySwapTrade && item.trade.provider == ExchangeProviderDescription.swapTrade) || - (displaySwapXyz && - item.trade.provider == ExchangeProviderDescription.swapsXyz) || - (displayNearIntents && item.trade.provider == ExchangeProviderDescription.nearIntents)) + (displaySwapTrade && + item.trade.provider == ExchangeProviderDescription.swapTrade) || + (displaySwapXyz && item.trade.provider == ExchangeProviderDescription.swapsXyz) || + (displayNearIntents && + item.trade.provider == ExchangeProviderDescription.nearIntents)) .toList() : _trades; } diff --git a/lib/store/node_list_store.dart b/lib/store/node_list_store.dart index e69de29bb..8b1378917 100644 --- a/lib/store/node_list_store.dart +++ b/lib/store/node_list_store.dart @@ -0,0 +1 @@ + diff --git a/lib/store/seed_settings_store.dart b/lib/store/seed_settings_store.dart index 90c02ba97..dff167cb5 100644 --- a/lib/store/seed_settings_store.dart +++ b/lib/store/seed_settings_store.dart @@ -5,7 +5,6 @@ part 'seed_settings_store.g.dart'; class SeedSettingsStore = SeedSettingsStoreBase with _$SeedSettingsStore; abstract class SeedSettingsStoreBase with Store { - @observable String? passphrase; } diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index b06e2acb6..d67c1f045 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -247,7 +247,6 @@ abstract class SettingsStoreBase with Store { priority[WalletType.ethereum] = initialEthereumTransactionPriority; } - if (initialPolygonTransactionPriority != null) { priority[WalletType.polygon] = initialPolygonTransactionPriority; } @@ -302,7 +301,8 @@ abstract class SettingsStoreBase with Store { reaction((_) => shouldShowRepWarning, (bool val) => sharedPreferences.setBool(PreferencesKey.shouldShowRepWarning, val)); - reaction((_)=>mwebAdDismissed, (val)=>sharedPreferences.setBool(PreferencesKey.mwebAdDismissed, val)); + reaction((_) => mwebAdDismissed, + (val) => sharedPreferences.setBool(PreferencesKey.mwebAdDismissed, val)); priority.observe((change) { final String? key; @@ -570,42 +570,40 @@ abstract class SettingsStoreBase with Store { reaction( (_) => lookupsZcashNames, - (bool looksUpZcashNames) => _sharedPreferences.setBool( - PreferencesKey.lookupsZcashNames, looksUpZcashNames)); + (bool looksUpZcashNames) => + _sharedPreferences.setBool(PreferencesKey.lookupsZcashNames, looksUpZcashNames)); reaction( - (_) => lookupsZcashAddress, - (bool lookupsZcashAddress) => _sharedPreferences.setBool( - PreferencesKey.lookupsZcashAddress, lookupsZcashAddress)); + (_) => lookupsZcashAddress, + (bool lookupsZcashAddress) => + _sharedPreferences.setBool(PreferencesKey.lookupsZcashAddress, lookupsZcashAddress)); reaction( (_) => lookupsWellKnown, (bool looksUpWellKnown) => _sharedPreferences.setBool(PreferencesKey.lookupsWellKnown, looksUpWellKnown)); - reaction( - (_) => lookupsFio, - (bool lookupsFio) => - _sharedPreferences.setBool(PreferencesKey.lookupsFio, lookupsFio)); + reaction((_) => lookupsFio, + (bool lookupsFio) => _sharedPreferences.setBool(PreferencesKey.lookupsFio, lookupsFio)); reaction( - (_) => lookupsNostr, - (bool lookupsNostr) => + (_) => lookupsNostr, + (bool lookupsNostr) => _sharedPreferences.setBool(PreferencesKey.lookupsNostr, lookupsNostr)); reaction( - (_) => lookupsThorChain, - (bool lookupsThorChain) => + (_) => lookupsThorChain, + (bool lookupsThorChain) => _sharedPreferences.setBool(PreferencesKey.lookupsThorChain, lookupsThorChain)); reaction( - (_) => lookupsBip353, - (bool lookupsBip353) => + (_) => lookupsBip353, + (bool lookupsBip353) => _sharedPreferences.setBool(PreferencesKey.lookupsBip353, lookupsBip353)); reaction( - (_) => lookupsLNUrl, - (bool lookupsLNUrl) => + (_) => lookupsLNUrl, + (bool lookupsLNUrl) => _sharedPreferences.setBool(PreferencesKey.lookupsLNUrl, lookupsLNUrl)); reaction((_) => usePayjoin, @@ -726,8 +724,8 @@ abstract class SettingsStoreBase with Store { reaction( (_) => showZcashMissingFundsCard, - (bool showZcashMissingFundsCard) => - _sharedPreferences.setBool(PreferencesKey.showZcashMissingFundsCard, showZcashMissingFundsCard)); + (bool showZcashMissingFundsCard) => _sharedPreferences.setBool( + PreferencesKey.showZcashMissingFundsCard, showZcashMissingFundsCard)); reaction((_) => mwebEnabled, (bool mwebEnabled) => _sharedPreferences.setBool(PreferencesKey.mwebEnabled, mwebEnabled)); @@ -765,8 +763,8 @@ abstract class SettingsStoreBase with Store { reaction( (_) => balanceHideCounter, - (int balanceHideCounter) => _sharedPreferences.setInt(PreferencesKey.balanceHideCounter, balanceHideCounter) - ); + (int balanceHideCounter) => + _sharedPreferences.setInt(PreferencesKey.balanceHideCounter, balanceHideCounter)); this.nodes.observe((change) { if (change.newValue != null && change.key != null) { @@ -1147,7 +1145,8 @@ abstract class SettingsStoreBase with Store { return priority[walletType]; } - void setPriority(WalletType walletType, TransactionPriority priority, {int? chainId}) => this.priority[walletType] = priority; + void setPriority(WalletType walletType, TransactionPriority priority, {int? chainId}) => + this.priority[walletType] = priority; bool isBitcoinBuyEnabled; @@ -1158,8 +1157,7 @@ abstract class SettingsStoreBase with Store { _sharedPreferences.setBool(PreferencesKey.shouldShowReceiveWarning, value); static Future load( - { - required bool isBitcoinBuyEnabled, + {required bool isBitcoinBuyEnabled, FiatCurrency initialFiatCurrency = FiatCurrency.usd, BalanceDisplayMode initialBalanceDisplayMode = BalanceDisplayMode.availableBalance}) async { final sharedPreferences = await getIt.getAsync(); @@ -1281,10 +1279,14 @@ abstract class SettingsStoreBase with Store { sharedPreferences.getBool(PreferencesKey.shouldShowMarketPlaceInDashboard) ?? true; final showAddressBookPopupEnabled = sharedPreferences.getBool(PreferencesKey.showAddressBookPopupEnabled) ?? true; - final forceDecentralizedExchanges = await sharedPreferences.getBool(PreferencesKey.forceDecentralizedExchanges) ?? false; - final decentralizedExchangesPromptDismissed = await sharedPreferences.getBool(PreferencesKey.decentralizedExchangesPromptDismissed) ?? false; + final forceDecentralizedExchanges = + await sharedPreferences.getBool(PreferencesKey.forceDecentralizedExchanges) ?? false; + final decentralizedExchangesPromptDismissed = + await sharedPreferences.getBool(PreferencesKey.decentralizedExchangesPromptDismissed) ?? + false; final syncStatusDisplayMode = SyncStatusDisplayModeExtension.fromString( - sharedPreferences.getString(PreferencesKey.syncStatusDisplayMode) ?? SyncStatusDisplayMode.blocksRemaining.name); + sharedPreferences.getString(PreferencesKey.syncStatusDisplayMode) ?? + SyncStatusDisplayMode.blocksRemaining.name); final exchangeStatus = ExchangeApiMode.deserialize( raw: sharedPreferences.getInt(PreferencesKey.exchangeStatusKey) ?? ExchangeApiMode.enabled.raw); @@ -1323,7 +1325,8 @@ abstract class SettingsStoreBase with Store { final lookupsOpenAlias = sharedPreferences.getBool(PreferencesKey.lookupsOpenAlias) ?? true; final lookupsENS = sharedPreferences.getBool(PreferencesKey.lookupsENS) ?? true; final lookupsZcashNames = sharedPreferences.getBool(PreferencesKey.lookupsZcashNames) ?? true; - final lookupsZcashAddress = sharedPreferences.getBool(PreferencesKey.lookupsZcashAddress) ?? true; + final lookupsZcashAddress = + sharedPreferences.getBool(PreferencesKey.lookupsZcashAddress) ?? true; final lookupsWellKnown = sharedPreferences.getBool(PreferencesKey.lookupsWellKnown) ?? true; final lookupsFio = sharedPreferences.getBool(PreferencesKey.lookupsFio) ?? true; final lookupsNostr = sharedPreferences.getBool(PreferencesKey.lookupsNostr) ?? true; @@ -1337,7 +1340,8 @@ abstract class SettingsStoreBase with Store { sharedPreferences.getBool(PreferencesKey.silentPaymentsCardDisplay) ?? true; final mwebAlwaysScan = sharedPreferences.getBool(PreferencesKey.mwebAlwaysScan) ?? false; final mwebCardDisplay = sharedPreferences.getBool(PreferencesKey.mwebCardDisplay) ?? true; - final showZcashMissingFundsCard = sharedPreferences.getBool(PreferencesKey.showZcashMissingFundsCard) ?? true; + final showZcashMissingFundsCard = + sharedPreferences.getBool(PreferencesKey.showZcashMissingFundsCard) ?? true; final mwebEnabled = sharedPreferences.getBool(PreferencesKey.mwebEnabled) ?? false; final hasEnabledMwebBefore = sharedPreferences.getBool(PreferencesKey.hasEnabledMwebBefore) ?? false; @@ -1376,7 +1380,6 @@ abstract class SettingsStoreBase with Store { final decredNodeId = sharedPreferences.getInt(PreferencesKey.currentDecredNodeIdKey); final dogecoinNodeId = sharedPreferences.getInt(PreferencesKey.currentDogecoinNodeIdKey); - final nodeSource = await Node.getAll(); final powNodeSource = await Node.getAllPow(); @@ -1389,52 +1392,36 @@ abstract class SettingsStoreBase with Store { final litecoinElectrumServer = nodeSource.firstWhereOrNull((e) => e.id == litecoinElectrumServerId) ?? nodeSource.firstWhereOrNull((e) => e.uriRaw == cakeWalletLitecoinElectrumUri); - final ethereumNode = - nodeSource.firstWhereOrNull((e) => e.id == ethereumNodeId) ?? - nodeSource.firstWhereOrNull((e) => e.uriRaw == ethereumDefaultNodeUri); - final polygonNode = - nodeSource.firstWhereOrNull((e) => e.id == polygonNodeId) ?? - nodeSource.firstWhereOrNull((e) => e.uriRaw == polygonDefaultNodeUri); - final baseNode = - nodeSource.firstWhereOrNull((e) => e.id == baseNodeId) ?? - nodeSource.firstWhereOrNull((e) => e.uriRaw == baseDefaultNodeUri); - final arbitrumNode = - nodeSource.firstWhereOrNull((e) => e.id == arbitrumNodeId) ?? - nodeSource.firstWhereOrNull((e) => e.uriRaw == arbitrumDefaultNodeUri); + final ethereumNode = nodeSource.firstWhereOrNull((e) => e.id == ethereumNodeId) ?? + nodeSource.firstWhereOrNull((e) => e.uriRaw == ethereumDefaultNodeUri); + final polygonNode = nodeSource.firstWhereOrNull((e) => e.id == polygonNodeId) ?? + nodeSource.firstWhereOrNull((e) => e.uriRaw == polygonDefaultNodeUri); + final baseNode = nodeSource.firstWhereOrNull((e) => e.id == baseNodeId) ?? + nodeSource.firstWhereOrNull((e) => e.uriRaw == baseDefaultNodeUri); + final arbitrumNode = nodeSource.firstWhereOrNull((e) => e.id == arbitrumNodeId) ?? + nodeSource.firstWhereOrNull((e) => e.uriRaw == arbitrumDefaultNodeUri); final bitcoinCashElectrumServer = nodeSource.firstWhereOrNull((e) => e.id == bitcoinCashElectrumServerId) ?? - nodeSource.firstWhereOrNull( - (e) => e.uriRaw == cakeWalletBitcoinCashDefaultNodeUri); - final nanoNode = - nodeSource.firstWhereOrNull((e) => e.id == nanoNodeId) ?? - nodeSource.firstWhereOrNull((e) => e.uriRaw == nanoDefaultNodeUri); - final decredNode = - nodeSource.firstWhereOrNull((e) => e.id == decredNodeId) ?? - nodeSource.firstWhereOrNull((e) => e.uriRaw == decredDefaultUri); - final nanoPowNode = - powNodeSource.firstWhereOrNull((e) => e.id == nanoPowNodeId) ?? - powNodeSource.firstWhereOrNull( - (e) => e.uriRaw == nanoDefaultPowNodeUri); - final solanaNode = - nodeSource.firstWhereOrNull((e) => e.id == solanaNodeId) ?? - nodeSource.firstWhereOrNull((e) => e.uriRaw == solanaDefaultNodeUri); - final tronNode = - nodeSource.firstWhereOrNull((e) => e.id == tronNodeId) ?? - nodeSource.firstWhereOrNull((e) => e.uriRaw == tronDefaultNodeUri); - final wowneroNode = - nodeSource.firstWhereOrNull((e) => e.id == wowneroNodeId) ?? - nodeSource.firstWhereOrNull((e) => e.uriRaw == wowneroDefaultNodeUri); - final zanoNode = - nodeSource.firstWhereOrNull((e) => e.id == zanoNodeId) ?? - nodeSource.firstWhereOrNull((e) => e.uriRaw == zanoDefaultNodeUri); - final dogecoinNode = - nodeSource.firstWhereOrNull((e) => e.id == dogecoinNodeId) ?? - nodeSource.firstWhereOrNull((e) => e.uriRaw == dogecoinDefaultNodeUri); - final zcashNode = - nodeSource.firstWhereOrNull((e) => e.id == zcashNodeId) ?? + nodeSource.firstWhereOrNull((e) => e.uriRaw == cakeWalletBitcoinCashDefaultNodeUri); + final nanoNode = nodeSource.firstWhereOrNull((e) => e.id == nanoNodeId) ?? + nodeSource.firstWhereOrNull((e) => e.uriRaw == nanoDefaultNodeUri); + final decredNode = nodeSource.firstWhereOrNull((e) => e.id == decredNodeId) ?? + nodeSource.firstWhereOrNull((e) => e.uriRaw == decredDefaultUri); + final nanoPowNode = powNodeSource.firstWhereOrNull((e) => e.id == nanoPowNodeId) ?? + powNodeSource.firstWhereOrNull((e) => e.uriRaw == nanoDefaultPowNodeUri); + final solanaNode = nodeSource.firstWhereOrNull((e) => e.id == solanaNodeId) ?? + nodeSource.firstWhereOrNull((e) => e.uriRaw == solanaDefaultNodeUri); + final tronNode = nodeSource.firstWhereOrNull((e) => e.id == tronNodeId) ?? + nodeSource.firstWhereOrNull((e) => e.uriRaw == tronDefaultNodeUri); + final wowneroNode = nodeSource.firstWhereOrNull((e) => e.id == wowneroNodeId) ?? + nodeSource.firstWhereOrNull((e) => e.uriRaw == wowneroDefaultNodeUri); + final zanoNode = nodeSource.firstWhereOrNull((e) => e.id == zanoNodeId) ?? + nodeSource.firstWhereOrNull((e) => e.uriRaw == zanoDefaultNodeUri); + final dogecoinNode = nodeSource.firstWhereOrNull((e) => e.id == dogecoinNodeId) ?? + nodeSource.firstWhereOrNull((e) => e.uriRaw == dogecoinDefaultNodeUri); + final zcashNode = nodeSource.firstWhereOrNull((e) => e.id == zcashNodeId) ?? nodeSource.firstWhereOrNull((e) => e.uriRaw == zcashDefaultNodeUri); - final bscNode = - nodeSource.firstWhereOrNull((e) => e.id == bscNodeId) ?? + final bscNode = nodeSource.firstWhereOrNull((e) => e.id == bscNodeId) ?? nodeSource.firstWhereOrNull((e) => e.uriRaw == bscDefaultNodeUri); final packageInfo = await PackageInfo.fromPlatform(); @@ -1654,7 +1641,8 @@ abstract class SettingsStoreBase with Store { final mwebAdDismissed = await sharedPreferences.getBool(PreferencesKey.mwebAdDismissed) ?? false; - final balanceHideCounter = await sharedPreferences.getInt(PreferencesKey.balanceHideCounter) ?? 0; + final balanceHideCounter = + await sharedPreferences.getInt(PreferencesKey.balanceHideCounter) ?? 0; return SettingsStore( secureStorage: secureStorage, @@ -1899,7 +1887,8 @@ abstract class SettingsStoreBase with Store { sharedPreferences.getBool(PreferencesKey.showAddressBookPopupEnabled) ?? showAddressBookPopupEnabled; syncStatusDisplayMode = SyncStatusDisplayModeExtension.fromString( - sharedPreferences.getString(PreferencesKey.syncStatusDisplayMode) ?? SyncStatusDisplayMode.blocksRemaining.name); + sharedPreferences.getString(PreferencesKey.syncStatusDisplayMode) ?? + SyncStatusDisplayMode.blocksRemaining.name); exchangeStatus = ExchangeApiMode.deserialize( raw: sharedPreferences.getInt(PreferencesKey.exchangeStatusKey) ?? ExchangeApiMode.enabled.raw); @@ -1950,15 +1939,15 @@ abstract class SettingsStoreBase with Store { sharedPreferences.getBool(PreferencesKey.lookupsUnstoppableDomains) ?? true; lookupsOpenAlias = sharedPreferences.getBool(PreferencesKey.lookupsOpenAlias) ?? true; lookupsENS = sharedPreferences.getBool(PreferencesKey.lookupsENS) ?? true; - lookupsZcashNames = - sharedPreferences.getBool(PreferencesKey.lookupsZcashNames) ?? true; + lookupsZcashNames = sharedPreferences.getBool(PreferencesKey.lookupsZcashNames) ?? true; lookupsWellKnown = sharedPreferences.getBool(PreferencesKey.lookupsWellKnown) ?? true; customBitcoinFeeRate = sharedPreferences.getInt(PreferencesKey.customBitcoinFeeRate) ?? 1; silentPaymentsCardDisplay = sharedPreferences.getBool(PreferencesKey.silentPaymentsCardDisplay) ?? true; mwebAlwaysScan = sharedPreferences.getBool(PreferencesKey.mwebAlwaysScan) ?? false; mwebCardDisplay = sharedPreferences.getBool(PreferencesKey.mwebCardDisplay) ?? true; - showZcashMissingFundsCard = sharedPreferences.getBool(PreferencesKey.showZcashMissingFundsCard) ?? true; + showZcashMissingFundsCard = + sharedPreferences.getBool(PreferencesKey.showZcashMissingFundsCard) ?? true; mwebEnabled = sharedPreferences.getBool(PreferencesKey.mwebEnabled) ?? false; hasEnabledMwebBefore = sharedPreferences.getBool(PreferencesKey.hasEnabledMwebBefore) ?? false; final nodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey); @@ -2001,7 +1990,6 @@ abstract class SettingsStoreBase with Store { final decredNode = await Node.get(decredNodeId ?? -1); final dogecoinNode = await Node.get(dogecoinNodeId ?? -1); - if (moneroNode != null) { nodes[WalletType.monero] = moneroNode; } @@ -2173,15 +2161,12 @@ abstract class SettingsStoreBase with Store { } Future _saveCurrentNode(Node node, WalletType walletType) async { - switch (walletType) { case WalletType.bitcoin: - await _sharedPreferences.setInt( - PreferencesKey.currentBitcoinElectrumSererIdKey, node.id); + await _sharedPreferences.setInt(PreferencesKey.currentBitcoinElectrumSererIdKey, node.id); break; case WalletType.litecoin: - await _sharedPreferences.setInt( - PreferencesKey.currentLitecoinElectrumSererIdKey, node.id); + await _sharedPreferences.setInt(PreferencesKey.currentLitecoinElectrumSererIdKey, node.id); break; case WalletType.monero: await _sharedPreferences.setInt(PreferencesKey.currentNodeIdKey, node.id); @@ -2200,8 +2185,7 @@ abstract class SettingsStoreBase with Store { nodes[node.type] = node; break; case WalletType.bitcoinCash: - await _sharedPreferences.setInt( - PreferencesKey.currentBitcoinCashNodeIdKey, node.id); + await _sharedPreferences.setInt(PreferencesKey.currentBitcoinCashNodeIdKey, node.id); break; case WalletType.nano: await _sharedPreferences.setInt(PreferencesKey.currentNanoNodeIdKey, node.id); diff --git a/lib/store/templates/send_template_store.dart b/lib/store/templates/send_template_store.dart index 3ffa1deda..a1fff698e 100644 --- a/lib/store/templates/send_template_store.dart +++ b/lib/store/templates/send_template_store.dart @@ -8,8 +8,7 @@ part 'send_template_store.g.dart'; class SendTemplateStore = SendTemplateBase with _$SendTemplateStore; abstract class SendTemplateBase with Store { - SendTemplateBase({required this.templateSource}) - : templates = ObservableList