From 5264eed642f46644e5274348bb6ce62db4d9bab9 Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Tue, 30 Jun 2026 21:07:00 +0200 Subject: [PATCH 01/46] 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 0000000000..88ddcf9861 --- /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 238f20502c..ca6cb6bb8c 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 0000000000..e6b5f64e3c --- /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 0000000000..50c8271e77 --- /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 1debd0a8fc..8cf1c7835b 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/46] 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 7cc92eb9b1..5053a7ef85 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 821f51130a..a4ae25ca97 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 0000000000..b29a747dba --- /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 88ddcf9861..85b82566ad 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/46] 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 5053a7ef85..d2a4bc9661 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 b29a747dba..3b36c5834e 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 85b82566ad..7e6efd0044 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 a299aebac8..4d181d7f63 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 8cf1c7835b..207ee288cf 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/46] 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 a4ae25ca97..faf6196af4 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 3b36c5834e..3270089386 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 0000000000..acbc7f71e2 --- /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 0000000000..3367d0a3ad --- /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 73c238b577..9c05309c6e 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 4d181d7f63..100f13329e 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/46] 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 b264b57eb9..542c00c129 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 1e154de6bf..ee301e82fa 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 06f1cfe34f..b6b23093da 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 faf6196af4..4439a68bd7 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 4660199ff4..bfa2571d8e 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 3270089386..ec22421b1d 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 0000000000..5de696f7b1 --- /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 0000000000..e1ca3f449a --- /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 acbc7f71e2..f965459513 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 0000000000..f96531b82c --- /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 7e6efd0044..6d5343ccc8 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 b41eb9c209..d345156dd0 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 f714bcd0c8..5b865324c0 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 011f90ea9e..1a0ef17def 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 b2301b8814..dc552fd668 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 8abef379da..e5097e0a7a 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 100f13329e..25e8ef8628 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 c5525007bc..c46043726c 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 0000000000..b3e2adade1 --- /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 0000000000..dd7f18b622 --- /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 0000000000..2eccd60ca6 --- /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 0000000000..4b68f70c63 --- /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 0000000000..836c8acb18 --- /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 0000000000..d723ede3e3 --- /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 207ee288cf..9929af1182 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/46] reformat --- .../pages/buy_sell/buy_sell_amount_page.dart | 768 +++++++++--------- .../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, 567 insertions(+), 494 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 ec22421b1d..772cfc77a6 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(); -} + @override + State createState() => _NewBuySellAmountPageState(); + } -class _NewBuySellAmountPageState extends State { - bool _customAmountMode = false; - bool _isLoadingPaymentMethods = false; - final customInputController = TextEditingController(); - final customInputFocusNode = FocusNode(); + class _NewBuySellAmountPageState extends State { + bool _customAmountMode = false; + bool _isLoadingPaymentMethods = false; + final customInputController = TextEditingController(); + final customInputFocusNode = FocusNode(); - @override - void initState() { - super.initState(); + @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)); - } + // 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) { - 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, + @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, + 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); + 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; - }); + 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, + )); - await asyncWhen((_) => [PaymentMethodLoaded, PaymentMethodFailed] - .contains(widget.buySellViewModel.paymentMethodState.runtimeType)); + String get _pageTitle => widget.buySellViewModel.mode == BuySellPageMode.buy + ? S.current.buy + : S.current.sell + + ((widget.buySellViewModel.cryptoCurrencies.length == 1) + ? " ${widget.buySellViewModel.cryptoCurrencies.first.fullName}" + : ""); - 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)); + Future navigateToProviders(BuildContext context) async { + if (_isLoadingPaymentMethods) { return; } - 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()); + // 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; - }); + 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}); + 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; + 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), + @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, ), - ) - ], - ), - 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), + ) + ], + ); + } } -} -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}); + 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; + 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]; + @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), - ); - }), - ), - ], - ); + 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}); + 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; + 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, + @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( + child: Material( + color: Colors.transparent, borderRadius: BorderRadius.circular(9999999999), - onTap: onTap, - child: isLoading - ? CupertinoActivityIndicator() - : Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Row( - spacing: 4, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (amount != null) + 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!.toStringWithPrecision(fractionalDigits: 0), - style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600), - ), + 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( - amount?.currency.symbol ?? S.of(context).custom, + S.of(context).enter_amount, style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w600, - color: amount == null - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.onSurfaceVariant), + fontSize: 12, color: 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}); + class BuySellCurrencyPickerPill extends StatelessWidget { + const BuySellCurrencyPickerPill({super.key, required this.curr, required this.onTap}); - final CryptoCurrency curr; - final VoidCallback 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), + @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 f965459513..4f16292278 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 6d5343ccc8..0c10134436 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 ca6cb6bb8c..85812c80c8 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 3367d0a3ad..7fb2304523 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 e46650517a..fe9c0844ad 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 25e8ef8628..4e6e2bf51a 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/46] 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 4e6e2bf51a..4228057cbe 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/46] 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 772cfc77a6..b9a44673f0 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/46] 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 4f16292278..1963f9a921 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/46] 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 4228057cbe..0116b943b7 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/46] 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 b9a44673f0..a0d250facb 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/46] 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 | 216 +- 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 | 36 +- 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 | 997 ++++- 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 | 242 +- 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 | 30 +- .../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 | 478 ++- 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 | 768 ++-- .../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 | 135 +- .../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 | 330 +- .../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 | 104 +- .../nodes/pow_node_create_or_edit_page.dart | 43 +- lib/src/screens/nodes/widgets/node_form.dart | 183 +- .../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 | 665 ++-- .../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 | 144 +- .../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, 15360 insertions(+), 14549 deletions(-) diff --git a/cw_bitcoin/lib/address_from_output.dart b/cw_bitcoin/lib/address_from_output.dart index 0d985b2370..072a92dc29 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 d6de05051b..65b728904b 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 d5a42d984b..dd73635695 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 7bf488f3f1..ffadb65ae3 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 46e4ca8e43..e38cbce305 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 a0d88ce9ac..7e76132a88 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 6abce9689a..0bc7f8c9c8 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 479cf64164..658969c3e1 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 4ed0da49cc..9a9bdcba46 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 8a034d8cfe..836b006d26 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 052d18ef0f..44aa096c4e 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 6457832ec9..5de131c77c 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 a58829e009..48c006ae24 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 ccd41d010f..bc7d45ec44 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 239fa491d8..239977ecbd 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) { + @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); - } 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: 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.p2tr, isLegacyDerivation: true); + await _generateInitialAddresses(type: SegwitAddresType.p2tr); - await _generateInitialAddresses(type: SegwitAddresType.p2wsh, isLegacyDerivation: true); - await _generateInitialAddresses(type: SegwitAddresType.p2wsh); - } + 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; - } + {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; + var countOfReceiveAddresses = 0; + var countOfHiddenAddresses = 0; - _addresses.forEach((addr) { - if (addr.type == type && addr.isLegacyDerivation == isLegacyDerivation) { - if (addr.isHidden) { - countOfHiddenAddresses += 1; - } else { - countOfReceiveAddresses += 1; - } + _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); - } + if (countOfReceiveAddresses < defaultReceiveAddressesCount) { + final addressesCount = defaultReceiveAddressesCount - countOfReceiveAddresses; + final newAddresses = await _createNewAddresses(addressesCount, + startIndex: countOfReceiveAddresses, + isHidden: false, + 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); + if (countOfHiddenAddresses < defaultChangeAddressesCount) { + final addressesCount = defaultChangeAddressesCount - countOfHiddenAddresses; + final newAddresses = await _createNewAddresses(addressesCount, + startIndex: countOfHiddenAddresses, + isHidden: true, + type: type, + isLegacyDerivation: isLegacyDerivation); + addAddresses(newAddresses); + } + } - final address = BitcoinAddressRecord( - await getAddressAsync(index: i, hd: hd, addressType: addrType), - index: i, - isHidden: isHidden, - isLegacyDerivation: isLegacyDerivation, - type: addrType, - network: network, - ); - list.add(address); - } + 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 9bdb66eef0..d43ce823a7 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 3f47c5c621..a71dcacbc2 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 9ede5714e3..aa73fc106e 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 42dda7cb22..ea9399c6ab 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 d2bcabb1fa..237a6b824d 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 2f9ae0b3c4..23959374c4 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 a4a4b7590f..fe85cd7823 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 0156ee6724..09ab924d1f 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 4fa2e2a63a..efa120f997 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 641399504c..f1980e39e8 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 75f58a4e36..0d46d1f62a 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 5fb9d57161..e4132ebfa9 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; - - Future markReceiverSessionComplete( - String sessionId, String txId, String amount) async { + 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 { 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 c46517e665..fbd00b8427 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; + } + + _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 = + 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 09c3bb0b9e..a792560a1c 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 be46befc3b..5a04b86e29 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 e8528939e2..9a61a8e6fc 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 cc8a3b9aa3..eca0576f46 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 75c384374f..c750397e03 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 6fd6ead3e1..baa12a82ff 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 45b89ed9da..f5be67ee0e 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 ffe6184b79..5fef27436f 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 9593f8b6b0..7a08d6d860 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 8838146173..3d8b651a1a 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 37fb7c7105..fc8f5a1bec 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 102755f4e9..71bf5d85dc 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 023c8f649a..15184d5e51 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 fdf81d5695..1e470dfc0a 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 462dedb83b..9115c19c8c 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 c96743eb3e..1276ef1d55 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 855c399da0..66b2072cff 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 4ecfc6fd47..a9f9927873 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 a12e64c15b..98b05794ae 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 07b66dcf7d..69e254f798 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 07bc1119d6..daaaaa915d 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 97c5c65999..93d5c08879 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 f15b248a4e..d2a3cae764 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 217d0a1c63..baf2650f35 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 916dd72da7..ed6f592d60 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 64f727b91e..c69bdc19cc 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 371bd265f0..f4fc4c5b1a 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 b1fd89e3ef..399b6baea6 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 1e969f581d..850a8dca3e 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 a1c7d44148..3870b57153 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 30de3892cf..0ec4d5acca 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 846f9acd5d..bc151626cd 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 9679b8531b..8709dfb29e 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 db6cf22ae6..84149e84ab 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 33ee821a6a..d162a593da 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 d95fd13474..72d3ad4324 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 06e34bfded..b6a57f4915 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 3b634c7fa1..0a26f913e8 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 2d0f818bf6..8a5cf5adf2 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 c12b7c9e8e..7ac4ecdd06 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 32e00570c0..77544b9820 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 a758b8b9c7..aa3c26307a 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 b22c572597..78a11f8712 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 6a84f826e8..fceb5714ac 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 a6caff932a..1a2e4f44a7 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 036fdd6dca..b73d738ff3 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 ebb69a347b..8d77ca9e3e 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 c2b736d1bc..ebd4e7496a 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 d94808ad9f..f227af07c1 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 - ], - ), - ), - ), - ) - : 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) - ], + !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) + ], + ), + ), + ), ), - ), - ), - ), 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 5b6d4dd162..e73d211a30 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 f8312fc798..a2c4a67559 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 d9890cd417..3b618f4908 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 a007b408d1..be76063768 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 4482e5f05b..2d1fe3e086 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 63fcdcd186..43fb37efa1 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 32312bf64f..a605e360c6 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 ec1d405841..a5fd2d08ee 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 fc2506e01b..8ef9a20682 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 fdc8f0b981..79a7b204b9 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 25c08ff6af..c87ab99ce4 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 f6402bea01..7329be545b 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 122806ded3..c4cafb5a2a 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 e98ead96a2..b546de0b47 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 9653652850..91581fe945 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 08705e18b7..d343bc5f4f 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 1e78e41702..d14dab0952 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 9540566c90..3c1dfde4f0 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 3b2a9e630e..2e69513546 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 12544c8b6e..ea563cab2b 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 3910f876c5..c260453338 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 5b865324c0..efd8435042 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 67aefba146..a43e501abc 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 f891de0927..aa0ad9731e 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 fb0cc96263..0e9799af5a 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 e4c1767a79..4e3793aba7 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 e69de29bb2..8b13789179 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 110bf01e08..600ff42690 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 a2a75d5524..6c4d769ba4 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 dc552fd668..8acbbcb2ad 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 c0b9b98096..fc70714450 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 fd66df2c06..df91970dcc 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 899fcd2467..c1743445d5 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 7b65f690ab..b887ab715a 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 03c5a3cd15..6c5c1c8f57 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 4934010c22..117c15d953 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 c8a5f81e90..5714719f46 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 45557309c4..7bd7f70d0d 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 8cd24e602b..97a5ffda41 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 72d5491f38..912e67d494 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 295e5ca2d7..07a0702049 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 a32cf1c1c6..be1d34f43f 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 850d9bac1c..93d7b8abcd 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 64de5337d2..d67e368acf 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 f0afdcde51..52a06a7409 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 42032593d6..7a84cb87a8 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 5a7c019c94..4f143c05fa 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 7a01506d7d..91400811dd 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 2326360ae9..b4c2dc3e85 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 70ff844d8c..b3ac158d9e 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 d7f856d720..f82d4241b6 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 9a22921bff..37841e412d 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 57e3149eee..18d4038e71 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 e69de29bb2..8b13789179 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 90c02ba978..dff167cb58 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 b06e2acb62..d67c1f045b 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 3ffa1dedab..a1fff698ef 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