From 09a3428245d9db4ab61e5c06db91af457127563a Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Thu, 27 Aug 2026 12:27:49 +0200 Subject: [PATCH] address review comments --- cw_core/lib/amount/money.dart | 8 - lib/buy/buy_quote.dart | 2 +- .../pages/buy_sell/buy_sell_amount_page.dart | 592 ++++++++++-------- .../buy_sell/buy_sell_confirmation_page.dart | 236 +++---- .../buy_sell_payment_method_page.dart | 138 ++-- .../buy_sell/buy_sell_provider_page.dart | 280 +++++---- .../buy_sell/buy_sell_redirecting_page.dart | 174 ++--- .../buy_sell/buy_sell_selector_modal.dart | 179 +++--- lib/new-ui/widgets/floating_amount_input.dart | 131 ++-- lib/router.dart | 6 +- lib/view_model/buy/buy_sell_view_model.dart | 94 +-- res/values/strings_en.arb | 2 + 12 files changed, 979 insertions(+), 863 deletions(-) diff --git a/cw_core/lib/amount/money.dart b/cw_core/lib/amount/money.dart index 8ec0793ff..0e4bc0de6 100644 --- a/cw_core/lib/amount/money.dart +++ b/cw_core/lib/amount/money.dart @@ -25,14 +25,6 @@ class Money implements Comparable { return Money(amount, currency); } - factory Money.safeParse(source, Currency currency, {bool isBaseUnit = false}) { - final amount = isBaseUnit - ? BigInt.parse(source.toString()) - : parseFixed(source.toString().withDecimals(currency.decimals), currency.decimals); - - return Money(amount, currency); - } - /// Parse the [source] and turn it into [Money] if possible /// /// As [parse] except that this method returns `null` if the input is not diff --git a/lib/buy/buy_quote.dart b/lib/buy/buy_quote.dart index ee301e82f..370e57a8f 100644 --- a/lib/buy/buy_quote.dart +++ b/lib/buy/buy_quote.dart @@ -16,7 +16,7 @@ extension RecommendationTitle on ProviderRecommendation { case ProviderRecommendation.bestRate: return S.current.best_rate; case ProviderRecommendation.lowKyc: - return 'LOW KYC'; + return S.current.low_kyc; case ProviderRecommendation.successRate: return S.current.highest_success_rate; } 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 8012f7210..c7031c8d6 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 @@ -5,7 +5,6 @@ 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/currency_picker/fiat_currency_picker_sheet.dart"; @@ -43,6 +42,13 @@ class _NewBuySellAmountPageState extends State { final customInputController = TextEditingController(); final customInputFocusNode = FocusNode(); + @override + void dispose() { + customInputController.dispose(); + customInputFocusNode.dispose(); + super.dispose(); + } + @override void initState() { super.initState(); @@ -50,107 +56,120 @@ class _NewBuySellAmountPageState extends State { // 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.changeFiatAmount(amount: widget.buySellViewModel.defaultAmounts[3]); widget.buySellViewModel.calculateBestRate(); when( - (_) => widget.buySellViewModel.paymentMethodState is PaymentMethodLoaded, - () => widget.buySellViewModel.selectedPaymentMethod = widget.buySellViewModel.paymentMethods - .firstWhere((item) => item.paymentMethodType == PaymentType.all),); + (_) => widget.buySellViewModel.paymentMethodState is PaymentMethodLoaded, + () => widget.buySellViewModel.selectedPaymentMethod = widget.buySellViewModel.paymentMethods + .firstWhere((item) => item.paymentMethodType == PaymentType.all), + ); } @override Widget build(BuildContext context) => Container( - decoration: BoxDecoration( - borderRadius: const 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: const Icon(Icons.close), - leadingSemanticLabel: S.of(context).close, - onLeadingPressed: Navigator.of(context, rootNavigator: true).pop, - ), + decoration: BoxDecoration( + borderRadius: const 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, ), - Expanded( - child: Observer( - builder: (_) => AnimatedSwitcher( - duration: const 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: const ValueKey(0), - defaultAmounts: widget.buySellViewModel.defaultAmounts, - fiatCurrency: widget.buySellViewModel.fiatCurrency, - currentAmount: widget.buySellViewModel.fiatAmount, - hasCurrencySelector: widget.buySellViewModel.hasMultipleCurrencies, - onCurrencySelectorPressed: () => selectCryptoCurrency(context), - onFiatSelectorPressed: () => selectFiatCurrency(context), + ), + 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: const Icon(Icons.close), + leadingSemanticLabel: S.of(context).close, + onLeadingPressed: Navigator.of(context, rootNavigator: true).pop, + ), + ), + Expanded( + child: Observer( + builder: (_) => AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + child: _customAmountMode + ? BuySellCustomAmountInput( + fiatCurrency: widget.buySellViewModel.fiatCurrency, 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); - if(context.mounted) { - await navigateToProviders(context); - } - } - }, - ), - ), - ),), - ], - ), - ), - ); + cryptoAmount: widget.buySellViewModel.cryptoAmount, + isLoading: _isLoadingPaymentMethods, + hasCurrencySelector: widget.buySellViewModel.hasMultipleCurrencies, + onCurrencySelectorPressed: () => selectCryptoCurrency(context), + controller: customInputController, + focusNode: customInputFocusNode, + onContinuePressed: () { + navigateToProviders(context); + }, + onChanged: (amount) { + if (widget.buySellViewModel.mode == BuySellPageMode.sell) { + widget.buySellViewModel.setCryptoAmountFromFiat(amount); + } else { + widget.buySellViewModel.changeFiatAmount(amount: amount); + } + }, + ) + : BuySellDefaultAmountSelector( + key: const ValueKey(0), + defaultAmounts: widget.buySellViewModel.defaultAmounts, + fiatCurrency: widget.buySellViewModel.fiatCurrency, + currentAmount: widget.buySellViewModel.fiatAmount, + hasCurrencySelector: widget.buySellViewModel.hasMultipleCurrencies, + onCurrencySelectorPressed: () => selectCryptoCurrency(context), + onFiatSelectorPressed: () => selectFiatCurrency(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 { + if (widget.buySellViewModel.mode == BuySellPageMode.sell) { + await widget.buySellViewModel.setCryptoAmountFromFiat(amount); + } else { + await widget.buySellViewModel.changeFiatAmount(amount: amount); + } + if (context.mounted) { + await 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, - ),); + context: context, + args: CurrencyPickerArgs( + items: widget.buySellViewModel.activeWalletCurrencies.toList(), + onSelected: (item) => widget.buySellViewModel.changeCryptoCurrency(currency: item), + symbolResolver: widget.buySellViewModel.amountParsingProxy.getCryptoSymbol, + ), + ); void selectFiatCurrency(BuildContext context) => FiatCurrencyPickerSheet.show( - context: context, - selected: widget.buySellViewModel.fiatCurrency, - onSelected: (item) => widget.buySellViewModel.changeFiatCurrency(currency: item)); + context: context, + selected: widget.buySellViewModel.fiatCurrency, + onSelected: (item) => widget.buySellViewModel.changeFiatCurrency(currency: item), + ); String get _pageTitle => widget.buySellViewModel.mode == BuySellPageMode.buy ? S.current.buy @@ -169,18 +188,22 @@ class _NewBuySellAmountPageState extends State { _isLoadingPaymentMethods = true; }); - await asyncWhen((_) => [PaymentMethodLoaded, PaymentMethodFailed] - .contains(widget.buySellViewModel.paymentMethodState.runtimeType),); + await asyncWhen( + (_) => [PaymentMethodLoaded, PaymentMethodFailed] + .contains(widget.buySellViewModel.paymentMethodState.runtimeType), + ); if (widget.buySellViewModel.paymentMethodState is PaymentMethodFailed) { - if(context.mounted) { + if (context.mounted) { await 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,),); + buttonAction: Navigator.of(context).pop, + ), + ); } return; } @@ -192,11 +215,15 @@ class _NewBuySellAmountPageState extends State { unawaited(widget.buySellViewModel.calculateBestRate()); final page = BuySellProviderPage(buySellViewModel: widget.buySellViewModel); - if(context.mounted) { - unawaited(Navigator.of(context).push(CupertinoPageRoute( - builder: (context) => Material(color: Colors.transparent, child: page),),)); + if (context.mounted) { + unawaited( + Navigator.of(context).push( + CupertinoPageRoute( + builder: (context) => Material(color: Colors.transparent, child: page), + ), + ), + ); } - } finally { setState(() { _isLoadingPaymentMethods = false; @@ -233,50 +260,62 @@ class BuySellCustomAmountInput extends StatelessWidget { @override Widget build(BuildContext context) => Padding( - padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const SizedBox.shrink(), - Column( - spacing: 8, - children: [ - FloatingAmountInput( - currency: fiatCurrency, - focusNode: focusNode, - controller: controller, - onChanged: onChanged, - ), - Opacity( - opacity: cryptoAmount.isEmpty ? 0 : 1, - child: Text( - "≈ ${cryptoAmount} ${cryptoCurrency.symbol}", - style: TextStyle( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const SizedBox.shrink(), + Column( + spacing: 8, + children: [ + 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,), + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), ), - ), - ], - ), - Padding( - padding: const EdgeInsets.all(18), - child: NewPrimaryButton( + ], + ), + Padding( + padding: const EdgeInsets.all(18), + child: NewPrimaryButton( onPressed: onContinuePressed, isLoading: isLoading, text: S.of(context).continue_text, color: Theme.of(context).colorScheme.primary, - textColor: Theme.of(context).colorScheme.onPrimary,), - ), - ], - ), - ); + textColor: Theme.of(context).colorScheme.onPrimary, + ), + ), + ], + ), + ); } class BuySellDefaultAmountSelector extends StatelessWidget { - const BuySellDefaultAmountSelector( - {required this.onFiatSelectorPressed, required this.defaultAmounts, required this.fiatCurrency, required this.mode, required this.onSelected, required this.isLoading, required this.hasCurrencySelector, required this.onCurrencySelectorPressed, required this.cryptoCurrency, super.key, - this.currentAmount,}); + const BuySellDefaultAmountSelector({ + required this.onFiatSelectorPressed, + required this.defaultAmounts, + required this.fiatCurrency, + required this.mode, + required this.onSelected, + required this.isLoading, + required this.hasCurrencySelector, + required this.onCurrencySelectorPressed, + required this.cryptoCurrency, + super.key, + this.currentAmount, + }); final List defaultAmounts; final String? currentAmount; @@ -291,37 +330,42 @@ class BuySellDefaultAmountSelector extends StatelessWidget { @override Widget build(BuildContext context) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 18), - child: Column( - spacing: 24, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (hasCurrencySelector) - BuySellCurrencyPickerPill(title: mode == BuySellPageMode.sell ? S - .of(context) - .sell : S - .of(context) - .buy, curr: cryptoCurrency, onTap: onCurrencySelectorPressed), - BuySellCurrencyPickerPill(title: mode == BuySellPageMode.sell ? S - .of(context) - .receive : S - .of(context) - .purchase_with, curr: fiatCurrency, onTap: onFiatSelectorPressed), - - 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), - ), - GridView.builder( + padding: const EdgeInsets.symmetric(horizontal: 18), + child: Column( + spacing: 24, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (hasCurrencySelector) + BuySellCurrencyPickerPill( + title: mode == BuySellPageMode.sell ? S.of(context).sell : S.of(context).buy, + curr: cryptoCurrency, + onTap: onCurrencySelectorPressed, + ), + BuySellCurrencyPickerPill( + title: mode == BuySellPageMode.sell + ? S.of(context).receive + : S.of(context).purchase_with, + curr: fiatCurrency, + onTap: onFiatSelectorPressed, + ), + 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), + ), + GridView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), // +1 for "custom" option itemCount: defaultAmounts.length + 1, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, crossAxisSpacing: 8, mainAxisSpacing: 16, mainAxisExtent: 105,), + crossAxisCount: 2, + crossAxisSpacing: 8, + mainAxisSpacing: 16, + mainAxisExtent: 105, + ), itemBuilder: (context, index) { final String? item = index == defaultAmounts.length ? null : defaultAmounts[index]; @@ -330,10 +374,11 @@ class BuySellDefaultAmountSelector extends StatelessWidget { amount: item == null ? null : Money.parse(item, fiatCurrency), onTap: () => onSelected(item), ); - },), - ], - ), - ); + }, + ), + ], + ), + ); } class BuySellAmountPill extends StatelessWidget { @@ -345,68 +390,72 @@ class BuySellAmountPill extends StatelessWidget { @override Widget build(BuildContext context) => 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( + decoration: BoxDecoration( borderRadius: BorderRadius.circular(9999999999), - onTap: onTap, - child: isLoading - ? const CupertinoActivityIndicator() - : Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Row( - spacing: 4, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (amount != null) - MoneyText( - amount!, - style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600), - showSymbol: false, - ), - Text( - amount?.currency.symbol ?? S.of(context).custom, - style: TextStyle( + border: Border.all( + width: 1, + color: Theme.of(context).colorScheme.surfaceContainerHigh, + ), + gradient: LinearGradient( + colors: [ + context.customColors.cardGradientColorPrimary, + context.customColors.cardGradientColorSecondary, + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + child: Material( + color: Colors.transparent, + borderRadius: BorderRadius.circular(9999999999), + child: InkWell( + borderRadius: BorderRadius.circular(9999999999), + onTap: onTap, + child: isLoading + ? const CupertinoActivityIndicator() + : Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + spacing: 4, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (amount != null) + MoneyText( + amount!, + style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600), + showSymbol: false, + ), + 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,), + : 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({required this.title, required this.curr, required this.onTap, super.key}); + const BuySellCurrencyPickerPill( + {required this.title, required this.curr, required this.onTap, super.key}); final String title; final Currency curr; @@ -414,65 +463,74 @@ class BuySellCurrencyPickerPill extends StatelessWidget { @override Widget build(BuildContext context) => Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - spacing: 12, - children: [ - Text(title, style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),), - GestureDetector( - onTap: onTap, - child: Container( - width: MediaQuery.sizeOf(context).width, - height: 62, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surfaceContainer, - borderRadius: BorderRadius.circular(18), - ), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 12), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - - children: [ - Row(spacing:12, children: [ - if(curr is CryptoCurrency) - TokenChainDisplay(size: 30, asset: curr as CryptoCurrency) - else if(curr is FiatCurrency) - SizedBox(width: 30, child: Text((curr as FiatCurrency).emoji, style: const TextStyle(fontSize: 24),)), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - spacing: 2, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + spacing: 12, + children: [ + Text( + title, + style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant), + ), + GestureDetector( + onTap: onTap, + child: Container( + width: MediaQuery.sizeOf(context).width, + height: 62, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surfaceContainer, + borderRadius: BorderRadius.circular(18), + ), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + spacing: 12, children: [ - Text( - curr.fullName ?? curr.symbol, + if (curr is CryptoCurrency) + TokenChainDisplay(size: 30, asset: curr as CryptoCurrency) + else if (curr is FiatCurrency) + SizedBox( + width: 30, + child: Text( + (curr as FiatCurrency).emoji, + style: const TextStyle(fontSize: 24), + )), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + spacing: 2, + children: [ + Text( + curr.fullName ?? curr.symbol, + ), + CurrencySymbolText( + curr, + style: TextStyle( + fontSize: 12, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + ], ), - CurrencySymbolText( - curr, - style: TextStyle( - fontSize: 12, - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), - ) ], ), - ],), - - 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), + 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_confirmation_page.dart b/lib/new-ui/pages/buy_sell/buy_sell_confirmation_page.dart index 7d6c3c7d1..3b95faea7 100644 --- a/lib/new-ui/pages/buy_sell/buy_sell_confirmation_page.dart +++ b/lib/new-ui/pages/buy_sell/buy_sell_confirmation_page.dart @@ -1,134 +1,144 @@ -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'; +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/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}); + const BuySellConfirmationPage({required this.buySellViewModel, super.key}); 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, + Widget build(BuildContext context) => Container( + decoration: BoxDecoration( + borderRadius: const 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: Theme.of(context).brightness == Brightness.light - ? buySellViewModel.selectedQuote!.lightIconPath - : buySellViewModel.selectedQuote!.darkIconPath, - width: 24, - height: 24, - ), - Text( - buySellViewModel.selectedQuote!.rampName ?? - buySellViewModel.selectedQuote!.provider.title, - style: TextStyle( + child: SafeArea( + child: Column( + children: [ + ModalTopBar( + title: _pageTitle, + leadingIcon: const Icon(Icons.arrow_back_ios_new), + onLeadingPressed: Navigator.of(context).pop, + leadingSemanticLabel: S.of(context).seed_alert_back, + ), + Expanded( + child: Observer( + builder: (_) => Column( + spacing: 24, + children: [ + Column( + spacing: 4, + children: [ + Text( + "${buySellViewModel.fiatAmount} ${buySellViewModel.fiatCurrency}", + style: const TextStyle(fontSize: 32), + ), + if (buySellViewModel.amountForQuote(buySellViewModel.selectedQuote!) != + null) + 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), + child: NewListSections( + sections: { + "": [ + ListItemRegularRow( + keyValue: "provider", + label: S.of(context).provider, + trailingWidget: Row( + spacing: 8, + children: [ + CakeImageWidget( + imageUrl: Theme.of(context).brightness == Brightness.light + ? buySellViewModel.selectedQuote!.lightIconPath + : 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() - ], - )), - if(buySellViewModel.selectedQuote!.paymentType.title?.isNotEmpty ?? false) - 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( + fontWeight: FontWeight.w500, + ), + ), + const SizedBox.shrink(), + ], + ), + ), + if (buySellViewModel.selectedQuote!.paymentType.title?.isNotEmpty ?? + false) + 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: const 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), - ) - ], + textColor: Theme.of(context).colorScheme.onPrimary, + ), + ), + ], + ), ), - ), - ); - } + ); String get _pageTitle => - (buySellViewModel.mode == BuySellPageMode.buy ? S.current.buy : S.current.sell) + - " " + - (buySellViewModel.cryptoCurrency.fullName ?? ""); + "${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( + Navigator.of(context, rootNavigator: true).push( + CupertinoPageRoute( builder: (context) => Material( - child: page, - ))); + 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 index c4ad495d2..2e5b3740f 100644 --- 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 @@ -1,73 +1,81 @@ -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'; +import "dart:async"; + +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}); + const BuySellPaymentMethodPage({required this.buySellViewModel, super.key}); 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, + Widget build(BuildContext context) => Container( + decoration: BoxDecoration( + borderRadius: const 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, ), - 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: Theme.of(context).brightness == Brightness.light - ? item.lightIconPath - : 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() - }), - ), - )) - ], - )), - ); - } + ), + child: SafeArea( + child: Column( + children: [ + ModalTopBar( + title: S.of(context).payment_method, + leadingIcon: const Icon(Icons.arrow_back_ios_new), + onLeadingPressed: Navigator.of(context).pop, + leadingSemanticLabel: S.of(context).seed_alert_back, + ), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 18), + child: Observer( + builder: (_) => NewListSections( + sections: { + "": buySellViewModel.paymentMethods + .map( + (item) => ListItemRegularRow( + keyValue: item.title, + label: item.title, + showArrow: false, + iconPath: Theme.of(context).brightness == Brightness.light + ? item.lightIconPath + : 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: () { + buySellViewModel.changeOption(item); + unawaited(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 99c91c13b..a9e5c1e10 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,23 +1,22 @@ -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/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/money/money_text.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'; +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"; import "package:modal_bottom_sheet/modal_bottom_sheet.dart"; class BuySellProviderPage extends StatefulWidget { - const BuySellProviderPage({super.key, required this.buySellViewModel}); + const BuySellProviderPage({required this.buySellViewModel, super.key}); final BuySellViewModel buySellViewModel; @@ -29,116 +28,128 @@ class _BuySellProviderPageState extends State { bool _allProvidersExpanded = false; @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, + Widget build(BuildContext context) => Container( + decoration: BoxDecoration( + borderRadius: const 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, ), - 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), + ), + child: SafeArea( + child: Column( + children: [ + ModalTopBar( + title: _pageTitle, + leadingIcon: const Icon(Icons.arrow_back_ios_new), + onLeadingPressed: Navigator.of(context).pop, + leadingSemanticLabel: S.of(context).seed_alert_back, + ), + Expanded( + child: Observer( + builder: (_) { + if (widget.buySellViewModel.buySellQuotState is BuySellQuotFailed) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + spacing: 24, + children: [ + const Icon(Icons.warning_amber_outlined, size: 48), + Column( + spacing: 10, + children: [ + Text( + S.of(context).could_not_load_quotes, + style: const 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: [ + const CupertinoActivityIndicator(), + Text( + S.of(context).loading_rates, + style: + TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant), + ), + ], ), - 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 SingleChildScrollView( - controller: ModalScrollController.of(context), - child: 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, - ))); + return SingleChildScrollView( + controller: ModalScrollController.of(context), + child: Padding( + padding: const 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), + if (widget.buySellViewModel.sortedQuotes.isNotEmpty) + ListItemDropdown( + keyValue: "more options", + label: S.of(context).more_options, + onTap: () { + setState(() { + _allProvidersExpanded = !_allProvidersExpanded; + }); + }, + ), + if (_allProvidersExpanded) + ...widget.buySellViewModel.sortedQuotes.map(quoteListItem), + ], }, - trailingText: widget.buySellViewModel.selectedPaymentMethod?.title) - ], - 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, - onTap: () { - setState(() { - _allProvidersExpanded = !_allProvidersExpanded; - }); - }), - if (_allProvidersExpanded) - ...widget.buySellViewModel.sortedQuotes.map(quoteListItem) - ] - }), + ), + ), + ); + }, ), - ); - }, - )) - ], - )), - ); - } + ), + ], + ), + ), + ); String get _pageTitle => - (widget.buySellViewModel.mode == BuySellPageMode.buy ? S.current.buy : S.current.sell) + - " " + - (widget.buySellViewModel.cryptoCurrency.fullName ?? ""); + "${widget.buySellViewModel.mode == BuySellPageMode.buy ? S.current.buy : S.current.sell} ${widget.buySellViewModel.cryptoCurrency.fullName ?? ""}"; ListItem quoteListItem(Quote quote) { final fiatAmount = widget.buySellViewModel.fiatAmountForQuote(quote); @@ -148,10 +159,10 @@ class _BuySellProviderPageState extends State { secondaryLabel: quote.rampName != null ? quote.provider.title : null, subtitle: quote.badges.isEmpty ? null : quote.badges.join(" - "), subtitleColor: Theme.of(context).colorScheme.primary, - iconPath: Theme.of(context).brightness == Brightness.light - ? quote.lightIconPath - : quote.darkIconPath, - onTap: () { + iconPath: Theme.of(context).brightness == Brightness.light + ? quote.lightIconPath + : quote.darkIconPath, + onTap: () { widget.buySellViewModel.changeOption(quote); navigateToConfirmation(context); }, @@ -161,21 +172,26 @@ class _BuySellProviderPageState extends State { mainAxisAlignment: MainAxisAlignment.center, spacing: 4, children: [ - MoneyText.optional(widget.buySellViewModel.amountForQuote(quote)), - if (fiatAmount != null) - Text( + MoneyText.optional(widget.buySellViewModel.amountForQuote(quote)), + if (fiatAmount != null) + Text( "= ${fiatAmount.toStringWithSymbol(fractionalDigits: 2, trimZeros: false)}", - style: TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant)) + 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( + Navigator.of(context).push( + CupertinoPageRoute( builder: (context) => Material( - color: Colors.transparent, - child: page, - ))); + 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 index bf7d716f7..39c949713 100644 --- a/lib/new-ui/pages/buy_sell/buy_sell_redirecting_page.dart +++ b/lib/new-ui/pages/buy_sell/buy_sell_redirecting_page.dart @@ -1,13 +1,13 @@ -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'; +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}); + const BuySellRedirectingPage({required this.buySellViewModel, super.key}); final BuySellViewModel buySellViewModel; @@ -21,7 +21,7 @@ class _BuySellRedirectingPageState extends State { @override void initState() { super.initState(); - Future.delayed(Duration(seconds: 2)).then((_) async { + Future.delayed(const Duration(seconds: 2)).then((_) { WidgetsBinding.instance .addPostFrameCallback((_) => widget.buySellViewModel.launchTrade(context)); setState(() { @@ -31,97 +31,99 @@ class _BuySellRedirectingPageState extends State { } @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, + Widget build(BuildContext context) => PopScope( + canPop: false, + child: Container( + decoration: BoxDecoration( + borderRadius: const 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; + 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 SafeArea( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const SizedBox.shrink(), + Observer( + builder: (_) { + if (widget.buySellViewModel.buySellQuotState is BuySellQuotFailed) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + spacing: 24, + children: [ + const Icon(Icons.warning_amber_outlined, size: 48), + Column( + spacing: 10, + children: [ + Text( + S.of(context).could_not_proceed_with_trade, + style: + const 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: [ - Icon(Icons.warning_amber_outlined, size: 48), + CakeImageWidget( + imageUrl: Theme.of(context).brightness == Brightness.light + ? widget.buySellViewModel.selectedQuote!.lightIconPath + : widget.buySellViewModel.selectedQuote!.darkIconPath, + width: 64, + height: 64, + ), Column( spacing: 10, children: [ Text( - S.of(context).could_not_proceed_with_trade, - style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500), + "${S.of(context).connecting_you_to} ${widget.buySellViewModel.selectedQuote!.provider.title}...", + style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w500), + ), + Text( + "${widget.buySellViewModel.fiatAmount} ${widget.buySellViewModel.fiatCurrency} → ${widget.buySellViewModel.amountForQuote(widget.buySellViewModel.selectedQuote!)?.toStringWithSymbol(fractionalDigits: 8)}", ), - Text((widget.buySellViewModel.buySellQuotState as BuySellQuotFailed) - .errorMessage ?? - S.of(context).please_try_again_later) ], - ) + ), ], ); - } - return Column( - mainAxisAlignment: MainAxisAlignment.center, - spacing: 24, - children: [ - CakeImageWidget( - imageUrl: Theme.of(context).brightness == Brightness.light - ? widget.buySellViewModel.selectedQuote!.lightIconPath - : 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: const 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, + ), ) - ], - ); - }, - ), - 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() - ], - ), - ); - }, + : const 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 36f70f35d..ed1073398 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,22 +1,20 @@ -import 'package:cake_wallet/di.dart'; -import 'package:cake_wallet/generated/i18n.dart'; +import "package:cake_wallet/di.dart"; +import "package:cake_wallet/generated/i18n.dart"; import "package:cake_wallet/main.dart"; -import 'package:cake_wallet/new-ui/modal_navigator.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'; - -enum BuySellPageMode { buy, sell } +import "package:cake_wallet/new-ui/modal_navigator.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:cake_wallet/view_model/buy/buy_sell_view_model.dart"; +import "package:flutter/material.dart"; class BuySellSelectorModal extends StatelessWidget { const BuySellSelectorModal({super.key}); @override - Widget build(BuildContext context) { - return Container( + Widget build(BuildContext context) => Container( decoration: BoxDecoration( - borderRadius: BorderRadius.vertical(top: Radius.circular(18)), + borderRadius: const BorderRadius.vertical(top: Radius.circular(18)), gradient: LinearGradient( colors: [ Theme.of(context).colorScheme.surface, @@ -27,12 +25,15 @@ class BuySellSelectorModal extends StatelessWidget { ), ), child: SafeArea( - top: false, - child: Column(spacing: 24, mainAxisSize: MainAxisSize.min, children: [ - SizedBox.shrink(), + top: false, + child: Column( + spacing: 24, + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox.shrink(), Text( S.of(context).buy_or_sell, - style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500), + style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w500), ), Text( S.of(context).buy_or_sell_desc, @@ -40,7 +41,7 @@ class BuySellSelectorModal extends StatelessWidget { TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant), ), Padding( - padding: EdgeInsets.symmetric(horizontal: 18.0), + padding: const EdgeInsets.symmetric(horizontal: 18), child: Column( spacing: 12, children: [ @@ -51,39 +52,43 @@ class BuySellSelectorModal extends StatelessWidget { 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)) + 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() - ]))); - } + const SizedBox.shrink(), + ], + ), + ), + ); void openBuySellPage(BuildContext context, BuySellPageMode mode) { Navigator.of(context).pop(); final page = getIt.get(param1: mode); showModalBottomSheet( - useSafeArea: true, - isScrollControlled: true, - context: navigatorKey.currentContext!, - builder: (modalContext) => ModalNavigator( - rootPage: page, - parentContext: modalContext, - )); + useSafeArea: true, + isScrollControlled: true, + context: navigatorKey.currentContext!, + builder: (modalContext) => ModalNavigator( + rootPage: page, + parentContext: modalContext, + ), + ); } - } class BuySellSelectorModalButton extends StatelessWidget { - const BuySellSelectorModalButton( - {super.key, - required this.title, - required this.description, - required this.iconPath, - required this.onTap}); + const BuySellSelectorModalButton({ + required this.title, + required this.description, + required this.iconPath, + required this.onTap, + super.key, + }); final String title; final String description; @@ -91,56 +96,58 @@ class BuySellSelectorModalButton extends StatelessWidget { 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, + Widget build(BuildContext context) => 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, + ), ), - 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), + child: Padding( + padding: const 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, ), - Text( - description, - style: TextStyle( - fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant), - ) - ], - ) - ], + ), + Column( + spacing: 8, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: const 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/floating_amount_input.dart b/lib/new-ui/widgets/floating_amount_input.dart index 237f85825..5dd5b1d82 100644 --- a/lib/new-ui/widgets/floating_amount_input.dart +++ b/lib/new-ui/widgets/floating_amount_input.dart @@ -1,17 +1,18 @@ import "package:cake_wallet/new-ui/widgets/money/currency_symbol_text.dart"; -import 'package:cw_core/currency.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; +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}); + const FloatingAmountInput({ + required this.currency, + required this.controller, + super.key, + this.focusNode, + this.inputFormatters, + this.onChanged, + this.validator, + }); final Currency currency; final TextEditingController controller; @@ -35,64 +36,70 @@ class _FloatingAmountInputState extends State { } @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*$'), + void dispose() { + if (focusNode != widget.focusNode) { + focusNode.dispose(); + } + super.dispose(); + } + + @override + Widget build(BuildContext context) => 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: const TextInputType.numberWithOptions( + signed: false, + decimal: true, ), - ], - 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( + 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, - color: Theme.of(context).colorScheme.onSurfaceVariant, + fontSize: 45, + color: Theme.of(context).colorScheme.onSurface, ), ), + ), + const SizedBox(width: 8), + CurrencySymbolText( + widget.currency, + maxLines: 1, + overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.displayMedium?.copyWith( fontWeight: FontWeight.w400, fontSize: 45, - color: Theme.of(context).colorScheme.onSurface, + color: Theme.of(context).colorScheme.onSurfaceVariant, ), ), - ), - const SizedBox(width: 8), - CurrencySymbolText( - widget.currency, - 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/router.dart b/lib/router.dart index 2b01b079f..736ec9228 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -7,11 +7,13 @@ import 'package:cake_wallet/new-ui/pages/about_page.dart'; import 'package:cake_wallet/new-ui/pages/bridge/bridge_history_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/lightning_username_page.dart'; import "package:cake_wallet/new-ui/pages/receive_page.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/new-ui/widgets/hardware_wallet/sync_key_images_sheet.dart'; import 'package:cake_wallet/order/order.dart'; import 'package:cake_wallet/core/new_wallet_type_arguments.dart'; @@ -142,6 +144,7 @@ import 'package:cake_wallet/utils/payment_request.dart'; import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart'; import 'package:cake_wallet/view_model/bridge/bridge_view_model.dart'; import 'package:cake_wallet/view_model/bridge/bridge_history_view_model.dart'; +import "package:cake_wallet/view_model/buy/buy_sell_view_model.dart"; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/sign_view_model.dart'; @@ -703,9 +706,8 @@ Route createRoute(RouteSettings settings) { builder: (_) => getIt.get(param1: settings.arguments as Order)); case Routes.buySellPage: - final args = settings.arguments as bool?; return handleRouteWithPlatformAwareness( - (context) => getIt.get(param1: args), + (context) => Material(child: getIt.get(param1: BuySellPageMode.buy)), ); case Routes.buyOptionsPage: diff --git a/lib/view_model/buy/buy_sell_view_model.dart b/lib/view_model/buy/buy_sell_view_model.dart index 5e87267c8..048e61628 100644 --- a/lib/view_model/buy/buy_sell_view_model.dart +++ b/lib/view_model/buy/buy_sell_view_model.dart @@ -25,6 +25,8 @@ import 'package:mobx/mobx.dart'; part 'buy_sell_view_model.g.dart'; +enum BuySellPageMode { buy, sell } + class BuySellViewModel = BuySellViewModelBase with _$BuySellViewModel; abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with Store { @@ -89,9 +91,16 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S return mode == BuySellPageMode.buy ? formattedFiatAmount ?? 200.0 - : formattedCryptoAmount ?? (cryptoCurrency == CryptoCurrency.btc ? 0.001 : 1); + : formattedCryptoAmount ?? (cryptoCurrency == CryptoCurrency.btc ? 0.01 : 1); } + // sets based on the absolute amout (from the fiat/charts api) + // works even if you have no rates + Future setCryptoAmountFromFiat(String fiatAmount) async => changeCryptoAmount( + amount: (double.parse(fiatAmount) / (fiatConversionStore.prices[cryptoCurrency] ?? 0)) + .toString(), + ); + final AppStore _appStore; AmountParsingProxy get amountParsingProxy => _appStore.amountParsingProxy; @@ -190,18 +199,22 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S // 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.ars: ["50000", "100000", "500000", "1000000", "2500000"], FiatCurrency.aud: ["100", "200", "1000", "2000", "5000"], + FiatCurrency.bdt: ["5000", "10000", "50000", "100000", "250000"], 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.cny: ["500", "1000", "5000", "10000", "25000"], 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.ghs: ["500", "1000", "5000", "10000", "25000"], FiatCurrency.gtq: ["400", "800", "4000", "8000", "20000"], FiatCurrency.hkd: ["400", "800", "4000", "8000", "20000"], FiatCurrency.hrk: ["400", "800", "4000", "8000", "20000"], @@ -209,8 +222,10 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S FiatCurrency.idr: ["800000", "1600000", "8000000", "16000000", "40000000"], FiatCurrency.ils: ["200", "400", "2000", "4000", "10000"], FiatCurrency.inr: ["5000", "10000", "50000", "100000", "250000"], + FiatCurrency.irr: ["2000000", "4000000", "20000000", "40000000", "100000000"], FiatCurrency.isk: ["7000", "14000", "70000", "140000", "350000"], FiatCurrency.jpy: ["10000", "20000", "100000", "200000", "500000"], + FiatCurrency.kes: ["5000", "10000", "50000", "100000", "250000"], FiatCurrency.krw: ["50000", "100000", "500000", "1000000", "2500000"], FiatCurrency.mad: ["500", "1000", "5000", "10000", "25000"], FiatCurrency.mxn: ["1000", "2000", "10000", "20000", "50000"], @@ -222,17 +237,21 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S FiatCurrency.pkr: ["15000", "30000", "150000", "300000", "750000"], FiatCurrency.pln: ["200", "400", "2000", "4000", "10000"], FiatCurrency.ron: ["250", "500", "2500", "5000", "12500"], + FiatCurrency.rub: ["5000", "10000", "50000", "100000", "250000"], + FiatCurrency.sar: ["200", "400", "2000", "4000", "10000"], 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.uah: ["2000", "4000", "20000", "40000", "100000"], FiatCurrency.usd: ["50", "100", "500", "1000", "2500"], + FiatCurrency.vef: ["2000", "4000", "20000", "40000", "100000"], 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 => @@ -445,50 +464,45 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S await calculateBestRate(); } - String _getInitialCryptoCurrencyAddress() { - if (cryptoCurrency == wallet.currency) { - if ([CryptoCurrency.zec, CryptoCurrency.btc].contains(cryptoCurrency)) { - return wallet.walletAddresses.addressForBuy; - } - - return wallet.walletAddresses.address; - } - return ''; - } + String _getInitialCryptoCurrencyAddress() => wallet.walletAddresses.addressForBuy; @action Future _getAvailablePaymentTypes() async { - paymentMethodState = PaymentMethodLoading(); - selectedPaymentMethod = null; - final result = await Future.wait(providerList.map((element) => element - .getAvailablePaymentTypes(fiatCurrency.title, cryptoCurrency, mode == BuySellPageMode.buy) - .timeout( - Duration(seconds: 10), - onTimeout: () => [], - ))); + try{ + paymentMethodState = PaymentMethodLoading(); + selectedPaymentMethod = null; + final result = await Future.wait(providerList.map((element) => element + .getAvailablePaymentTypes(fiatCurrency.title, cryptoCurrency, mode == BuySellPageMode.buy) + .timeout( + Duration(seconds: 10), + onTimeout: () => [], + ))); - final List tempPaymentMethods = []; + final List tempPaymentMethods = []; - for (var methods in result) { - for (var method in methods) { - final alreadyExists = tempPaymentMethods.any((m) { - return m.paymentMethodType == method.paymentMethodType; - }); + for (var methods in result) { + for (var method in methods) { + final alreadyExists = tempPaymentMethods.any((m) { + return m.paymentMethodType == method.paymentMethodType; + }); - if (!alreadyExists) { - tempPaymentMethods.add(method); + if (!alreadyExists) { + tempPaymentMethods.add(method); + } } } - } - paymentMethods = ObservableList.of(tempPaymentMethods); + paymentMethods = ObservableList.of(tempPaymentMethods); - if (paymentMethods.isNotEmpty) { - paymentMethods.insert(0, PaymentMethod.all()); - selectedPaymentMethod = paymentMethods.first; - selectedPaymentMethod!.isSelected = true; - paymentMethodState = PaymentMethodLoaded(); - } else { + if (paymentMethods.isNotEmpty) { + paymentMethods.insert(0, PaymentMethod.all()); + selectedPaymentMethod = paymentMethods.first; + selectedPaymentMethod!.isSelected = true; + paymentMethodState = PaymentMethodLoaded(); + } else { + paymentMethodState = PaymentMethodFailed(); + } + } catch(e) { paymentMethodState = PaymentMethodFailed(); } } @@ -502,12 +516,10 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S 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); + pair.to.symbol == fiatCurrency.symbol); } else { 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); } @@ -515,7 +527,7 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S if (validProviders.isEmpty) { buySellQuotState = BuySellQuotFailed( - errorMessage: "Couldn't find a provider that supports ${cryptoCurrency.fullName}."); + errorMessage: S.current.no_buy_providers(cryptoCurrency.fullName??cryptoCurrency.title)); return; } @@ -544,7 +556,7 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S if (validQuotes.isEmpty) { buySellQuotState = BuySellQuotFailed( - errorMessage: "No provider could create a quote for ${cryptoCurrency.fullName}"); + errorMessage: S.current.no_buy_providers(cryptoCurrency.fullName??cryptoCurrency.title)); return; } diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 09f01168c..c5dce28c5 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -646,6 +646,7 @@ "long_press_to_copy": "Long press to copy", "low_fee": "Low fee", "low_fee_alert": "You currently are using a low network fee priority. This could cause long waits, different rates, or canceled trades. We recommend setting a higher fee for a better experience.", + "low_kyc": "Low KYC", "made_easy": "made easy", "manage_nodes": "Manage nodes", "manage_pow_nodes": "Manage PoW nodes", @@ -714,6 +715,7 @@ "newConnection": "New Connection", "nfts": "NFTs", "no": "No", + "no_buy_providers": "No providers found for ${curr}.", "no_cards_found": "No cards found", "no_extra_detail": "No extra details available", "no_id_needed": "No ID needed!",