| 1 | import 'package:cake_wallet/buy/sell_buy_states.dart'; |
| 2 | import 'package:cake_wallet/core/address_resolver/address_resolver_service.dart'; |
| 3 | import 'package:cake_wallet/core/address_validator.dart'; |
| 4 | import 'package:cake_wallet/di.dart'; |
| 5 | import 'package:cake_wallet/generated/i18n.dart'; |
| 6 | import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_args.dart'; |
| 7 | import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_sheet.dart'; |
| 8 | import 'package:cake_wallet/new-ui/widgets/currency_picker/fiat_currency_picker_sheet.dart'; |
| 9 | import 'package:cake_wallet/routes.dart'; |
| 10 | import 'package:cake_wallet/src/screens/base_page.dart'; |
| 11 | import 'package:cake_wallet/src/screens/exchange/widgets/desktop_exchange_cards_section.dart'; |
| 12 | import 'package:cake_wallet/src/screens/exchange/widgets/exchange_card.dart'; |
| 13 | import 'package:cake_wallet/src/screens/exchange/widgets/mobile_exchange_cards_section.dart'; |
| 14 | import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; |
| 15 | import 'package:cake_wallet/src/widgets/primary_button.dart'; |
| 16 | import 'package:cake_wallet/src/widgets/provider_optoin_tile.dart'; |
| 17 | import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart'; |
| 18 | import 'package:cake_wallet/src/widgets/trail_button.dart'; |
| 19 | import 'package:cake_wallet/utils/responsive_layout_util.dart'; |
| 20 | import 'package:cake_wallet/view_model/buy/buy_sell_view_model.dart'; |
| 21 | import 'package:cw_core/crypto_currency.dart'; |
| 22 | import 'package:cw_core/currency.dart'; |
| 23 | import 'package:cw_core/wallet_type.dart'; |
| 24 | import 'package:flutter/material.dart'; |
| 25 | import 'package:flutter_mobx/flutter_mobx.dart'; |
| 26 | import 'package:keyboard_actions/keyboard_actions.dart'; |
| 27 | import 'package:mobx/mobx.dart'; |
| 28 | |
| 29 | class BuySellPage extends BasePage { |
| 30 | BuySellPage(this.buySellViewModel, this._resolver); |
| 31 | |
| 32 | final BuySellViewModel buySellViewModel; |
| 33 | final AddressResolverService _resolver; |
| 34 | final cryptoCurrencyKey = GlobalKey<ExchangeCardState>(); |
| 35 | final fiatCurrencyKey = GlobalKey<ExchangeCardState>(); |
| 36 | final _formKey = GlobalKey<FormState>(); |
| 37 | final _fiatAmountFocus = FocusNode(); |
| 38 | final _cryptoAmountFocus = FocusNode(); |
| 39 | final _cryptoAddressFocus = FocusNode(); |
| 40 | var _isReactionsSet = false; |
| 41 | |
| 42 | late final String? depositWalletName; |
| 43 | late final String? receiveWalletName; |
| 44 | |
| 45 | @override |
| 46 | String get title => S.current.buy + '/' + S.current.sell; |
| 47 | |
| 48 | @override |
| 49 | bool get gradientBackground => true; |
| 50 | |
| 51 | @override |
| 52 | bool get gradientAll => true; |
| 53 | |
| 54 | @override |
| 55 | bool get resizeToAvoidBottomInset => false; |
| 56 | |
| 57 | @override |
| 58 | bool get extendBodyBehindAppBar => true; |
| 59 | |
| 60 | @override |
| 61 | AppBarStyle get appBarStyle => AppBarStyle.completelyTransparent; |
| 62 | |
| 63 | @override |
| 64 | Function(BuildContext)? get pushToNextWidget => (context) { |
| 65 | FocusScopeNode currentFocus = FocusScope.of(context); |
| 66 | if (!currentFocus.hasPrimaryFocus) { |
| 67 | currentFocus.focusedChild?.unfocus(); |
| 68 | } |
| 69 | }; |
| 70 | |
| 71 | @override |
| 72 | Widget trailing(BuildContext context) => TrailButton( |
| 73 | caption: S.of(context).clear, |
| 74 | onPressed: () { |
| 75 | _formKey.currentState?.reset(); |
| 76 | buySellViewModel.reset(); |
| 77 | }); |
| 78 | |
| 79 | @override |
| 80 | Widget? leading(BuildContext context) { |
| 81 | final _backButton = Icon( |
| 82 | Icons.arrow_back_ios, |
| 83 | color: Theme.of(context).colorScheme.primary, |
| 84 | size: 16, |
| 85 | ); |
| 86 | final _closeButton = currentTheme.isDark ? closeButtonImageDarkTheme : closeButtonImage; |
| 87 | |
| 88 | bool isMobileView = responsiveLayoutUtil.shouldRenderMobileUI; |
| 89 | |
| 90 | return MergeSemantics( |
| 91 | child: SizedBox( |
| 92 | height: isMobileView ? 37 : 45, |
| 93 | width: isMobileView ? 37 : 45, |
| 94 | child: ButtonTheme( |
| 95 | minWidth: double.minPositive, |
| 96 | child: Semantics( |
| 97 | label: !isMobileView ? S.of(context).close : S.of(context).seed_alert_back, |
| 98 | child: TextButton( |
| 99 | style: ButtonStyle( |
| 100 | overlayColor: WidgetStateColor.resolveWith((states) => Colors.transparent), |
| 101 | ), |
| 102 | onPressed: () => onClose(context), |
| 103 | child: !isMobileView ? _closeButton : _backButton, |
| 104 | ), |
| 105 | ), |
| 106 | ), |
| 107 | ), |
| 108 | ); |
| 109 | } |
| 110 | |
| 111 | @override |
| 112 | Widget body(BuildContext context) { |
| 113 | WidgetsBinding.instance.addPostFrameCallback((_) => _setReactions(context, buySellViewModel)); |
| 114 | |
| 115 | return KeyboardActions( |
| 116 | disableScroll: true, |
| 117 | config: KeyboardActionsConfig( |
| 118 | keyboardActionsPlatform: KeyboardActionsPlatform.IOS, |
| 119 | keyboardBarColor: Theme.of(context).colorScheme.surface, |
| 120 | nextFocus: false, |
| 121 | actions: [ |
| 122 | KeyboardActionsItem( |
| 123 | focusNode: _fiatAmountFocus, toolbarButtons: [(_) => KeyboardDoneButton()]), |
| 124 | KeyboardActionsItem( |
| 125 | focusNode: _cryptoAmountFocus, toolbarButtons: [(_) => KeyboardDoneButton()]) |
| 126 | ]), |
| 127 | child: Container( |
| 128 | color: Theme.of(context).colorScheme.surface, |
| 129 | child: Form( |
| 130 | key: _formKey, |
| 131 | child: ScrollableWithBottomSection( |
| 132 | contentPadding: EdgeInsets.only(bottom: 24), |
| 133 | content: Observer( |
| 134 | builder: (_) => Column( |
| 135 | children: [ |
| 136 | _exchangeCardsSection(context), |
| 137 | Padding( |
| 138 | padding: EdgeInsets.symmetric(horizontal: 24), |
| 139 | child: Column( |
| 140 | children: [ |
| 141 | SizedBox(height: 12), |
| 142 | _buildPaymentMethodTile(context), |
| 143 | ], |
| 144 | ), |
| 145 | ), |
| 146 | ], |
| 147 | ), |
| 148 | ), |
| 149 | bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24), |
| 150 | bottomSection: Observer( |
| 151 | builder: (_) => Column( |
| 152 | children: [ |
| 153 | if (buySellViewModel.isBuySellQuoteFailed) |
| 154 | Padding( |
| 155 | padding: EdgeInsets.only(bottom: 15), |
| 156 | child: Row( |
| 157 | children: [ |
| 158 | Expanded( |
| 159 | child: Container( |
| 160 | alignment: Alignment.centerRight, |
| 161 | child: Icon( |
| 162 | Icons.warning_amber_rounded, |
| 163 | color: Theme.of(context).colorScheme.error, |
| 164 | size: 26, |
| 165 | ), |
| 166 | ), |
| 167 | ), |
| 168 | Expanded( |
| 169 | flex: 8, |
| 170 | child: Text( |
| 171 | buySellViewModel.buySellQuoteFailedError ?? |
| 172 | S.of(context).buy_sell_pair_is_not_supported_warning, |
| 173 | textAlign: TextAlign.center, |
| 174 | softWrap: true, |
| 175 | overflow: TextOverflow.ellipsis, |
| 176 | maxLines: 3, |
| 177 | style: Theme.of(context).textTheme.bodyMedium?.copyWith( |
| 178 | color: Theme.of(context).colorScheme.error, |
| 179 | fontWeight: FontWeight.w500, |
| 180 | ), |
| 181 | ), |
| 182 | ), |
| 183 | ], |
| 184 | ), |
| 185 | ), |
| 186 | LoadingPrimaryButton( |
| 187 | text: S.current.choose_a_provider, |
| 188 | onPressed: () async { |
| 189 | if (!_formKey.currentState!.validate()) return; |
| 190 | buySellViewModel.onTapChoseProvider(context); |
| 191 | }, |
| 192 | color: Theme.of(context).colorScheme.primary, |
| 193 | textColor: Theme.of(context).colorScheme.onPrimary, |
| 194 | isDisabled: buySellViewModel.isBuySellQuoteFailed, |
| 195 | isLoading: |
| 196 | !buySellViewModel.isReadyToTrade && !buySellViewModel.isBuySellQuoteFailed, |
| 197 | ), |
| 198 | ], |
| 199 | ), |
| 200 | ), |
| 201 | ), |
| 202 | ), |
| 203 | ), |
| 204 | ); |
| 205 | } |
| 206 | |
| 207 | Widget _buildPaymentMethodTile(BuildContext context) { |
| 208 | if (buySellViewModel.paymentMethodState is PaymentMethodLoading || |
| 209 | buySellViewModel.paymentMethodState is InitialPaymentMethod) { |
| 210 | return OptionTilePlaceholder( |
| 211 | withBadge: false, |
| 212 | withSubtitle: false, |
| 213 | borderRadius: 30, |
| 214 | padding: EdgeInsets.symmetric(horizontal: 24, vertical: 8), |
| 215 | leadingIcon: Icons.arrow_forward_ios, |
| 216 | isDarkTheme: currentTheme.isDark); |
| 217 | } |
| 218 | if (buySellViewModel.paymentMethodState is PaymentMethodFailed) { |
| 219 | return OptionTilePlaceholder(errorText: 'No payment methods available', borderRadius: 30); |
| 220 | } |
| 221 | if (buySellViewModel.paymentMethodState is PaymentMethodLoaded && |
| 222 | buySellViewModel.selectedPaymentMethod != null) { |
| 223 | final selectedPaymentMethod = buySellViewModel.selectedPaymentMethod!; |
| 224 | return ProviderOptionTile( |
| 225 | lightImagePath: selectedPaymentMethod.lightIconPath, |
| 226 | darkImagePath: selectedPaymentMethod.darkIconPath, |
| 227 | title: selectedPaymentMethod.title, |
| 228 | onPressed: () => _pickPaymentMethod(context), |
| 229 | leadingIcon: Icons.arrow_forward_ios, |
| 230 | isLightMode: !currentTheme.isDark, |
| 231 | borderRadius: 30, |
| 232 | padding: EdgeInsets.symmetric(horizontal: 24, vertical: 8), |
| 233 | titleTextStyle: Theme.of(context).textTheme.titleMedium?.copyWith( |
| 234 | color: Theme.of(context).colorScheme.onSurface, |
| 235 | fontWeight: FontWeight.w600, |
| 236 | ), |
| 237 | ); |
| 238 | } |
| 239 | return OptionTilePlaceholder(errorText: 'No payment methods available', borderRadius: 30); |
| 240 | } |
| 241 | |
| 242 | void _pickPaymentMethod(BuildContext context) async { |
| 243 | final currentOption = buySellViewModel.selectedPaymentMethod; |
| 244 | await Navigator.of(context).pushNamed( |
| 245 | Routes.paymentMethodOptionsPage, |
| 246 | arguments: [ |
| 247 | buySellViewModel.paymentMethods, |
| 248 | buySellViewModel.changeOption, |
| 249 | ], |
| 250 | ); |
| 251 | |
| 252 | buySellViewModel.selectedPaymentMethod; |
| 253 | if (currentOption != null && |
| 254 | currentOption.paymentMethodType != |
| 255 | buySellViewModel.selectedPaymentMethod?.paymentMethodType) { |
| 256 | await buySellViewModel.calculateBestRate(); |
| 257 | } |
| 258 | } |
| 259 | |
| 260 | void _setReactions(BuildContext context, BuySellViewModel buySellViewModel) { |
| 261 | if (_isReactionsSet) return; |
| 262 | |
| 263 | final fiatAmountController = fiatCurrencyKey.currentState!.amountController; |
| 264 | final cryptoAmountController = cryptoCurrencyKey.currentState!.amountController; |
| 265 | final cryptoAddressController = cryptoCurrencyKey.currentState!.addressController; |
| 266 | |
| 267 | cryptoAddressController.addListener(() { |
| 268 | buySellViewModel.changeCryptoCurrencyAddress(cryptoAddressController.text); |
| 269 | }); |
| 270 | |
| 271 | _onCurrencyChange(buySellViewModel.cryptoCurrency, buySellViewModel, cryptoCurrencyKey); |
| 272 | _onCurrencyChange(buySellViewModel.fiatCurrency, buySellViewModel, fiatCurrencyKey); |
| 273 | |
| 274 | reaction( |
| 275 | (_) => buySellViewModel.wallet.name, |
| 276 | (_) => _onWalletNameChange( |
| 277 | buySellViewModel, buySellViewModel.cryptoCurrency, cryptoCurrencyKey)); |
| 278 | |
| 279 | reaction((_) => buySellViewModel.cryptoCurrency, |
| 280 | (currency) => _onCurrencyChange(currency, buySellViewModel, cryptoCurrencyKey)); |
| 281 | |
| 282 | reaction((_) => buySellViewModel.fiatCurrency, |
| 283 | (currency) => _onCurrencyChange(currency, buySellViewModel, fiatCurrencyKey)); |
| 284 | |
| 285 | reaction((_) => buySellViewModel.fiatAmount, (amount) { |
| 286 | if (fiatCurrencyKey.currentState!.amountController.text != amount) { |
| 287 | fiatCurrencyKey.currentState!.amountController.text = amount; |
| 288 | } |
| 289 | }); |
| 290 | |
| 291 | reaction((_) => buySellViewModel.isCryptoCurrencyAddressEnabled, (isEnabled) { |
| 292 | cryptoCurrencyKey.currentState!.isAddressEditable(isEditable: isEnabled); |
| 293 | }); |
| 294 | |
| 295 | reaction((_) => buySellViewModel.cryptoAmount, (amount) { |
| 296 | if (cryptoCurrencyKey.currentState!.amountController.text != amount) { |
| 297 | cryptoCurrencyKey.currentState!.amountController.text = amount; |
| 298 | } |
| 299 | }); |
| 300 | |
| 301 | reaction((_) => buySellViewModel.cryptoCurrencyAddress, (String address) { |
| 302 | if (cryptoAddressController.text != address) { |
| 303 | cryptoCurrencyKey.currentState!.addressController.text = address; |
| 304 | } |
| 305 | }); |
| 306 | |
| 307 | fiatAmountController.addListener(() { |
| 308 | if (fiatAmountController.text != buySellViewModel.fiatAmount) { |
| 309 | buySellViewModel.changeFiatAmount(amount: fiatAmountController.text); |
| 310 | } |
| 311 | }); |
| 312 | |
| 313 | cryptoAmountController.addListener(() { |
| 314 | if (cryptoAmountController.text != buySellViewModel.cryptoAmount) { |
| 315 | buySellViewModel.changeCryptoAmount(amount: cryptoAmountController.text); |
| 316 | } |
| 317 | }); |
| 318 | |
| 319 | _cryptoAddressFocus.addListener(() async { |
| 320 | if (!_cryptoAddressFocus.hasFocus && cryptoAddressController.text.isNotEmpty) { |
| 321 | final domain = cryptoAddressController.text; |
| 322 | final parsed = await fetchParsedAddress(context, domain, buySellViewModel.cryptoCurrency); |
| 323 | if (parsed.isNotEmpty) { |
| 324 | buySellViewModel.cryptoCurrencyAddress = parsed; |
| 325 | } |
| 326 | } |
| 327 | }); |
| 328 | |
| 329 | reaction((_) => buySellViewModel.wallet.walletAddresses.addressForBuy, (String address) { |
| 330 | if (buySellViewModel.cryptoCurrency == CryptoCurrency.xmr) { |
| 331 | cryptoCurrencyKey.currentState!.changeAddress(address: address); |
| 332 | } |
| 333 | }); |
| 334 | |
| 335 | reaction((_) => buySellViewModel.isReadyToTrade, (bool isReady) { |
| 336 | if (isReady) { |
| 337 | if (buySellViewModel.skipIsReadyToTradeReaction) { |
| 338 | buySellViewModel.skipIsReadyToTradeReaction = false; |
| 339 | return; |
| 340 | } |
| 341 | if (cryptoAmountController.text.isNotEmpty && |
| 342 | cryptoAmountController.text != S.current.fetching) { |
| 343 | buySellViewModel.changeCryptoAmount(amount: cryptoAmountController.text); |
| 344 | } else if (fiatAmountController.text.isNotEmpty && |
| 345 | fiatAmountController.text != S.current.fetching) { |
| 346 | buySellViewModel.changeFiatAmount(amount: fiatAmountController.text); |
| 347 | } |
| 348 | } |
| 349 | }); |
| 350 | |
| 351 | _isReactionsSet = true; |
| 352 | } |
| 353 | |
| 354 | void _onCurrencyChange( |
| 355 | Currency currency, BuySellViewModel buySellViewModel, GlobalKey<ExchangeCardState> key) { |
| 356 | final isCurrentTypeWallet = currency == buySellViewModel.wallet.currency; |
| 357 | |
| 358 | key.currentState!.changeSelectedCurrency(currency); |
| 359 | key.currentState!.changeWalletName(isCurrentTypeWallet ? buySellViewModel.wallet.name : ''); |
| 360 | |
| 361 | key.currentState!.changeAddress( |
| 362 | address: isCurrentTypeWallet ? buySellViewModel.wallet.walletAddresses.addressForBuy : ''); |
| 363 | |
| 364 | key.currentState!.changeAmount(amount: ''); |
| 365 | } |
| 366 | |
| 367 | void _onWalletNameChange(BuySellViewModel buySellViewModel, CryptoCurrency currency, |
| 368 | GlobalKey<ExchangeCardState> key) { |
| 369 | final isCurrentTypeWallet = currency == buySellViewModel.wallet.currency; |
| 370 | |
| 371 | if (isCurrentTypeWallet) { |
| 372 | key.currentState!.changeWalletName(buySellViewModel.wallet.name); |
| 373 | key.currentState!.addressController.text = |
| 374 | buySellViewModel.wallet.walletAddresses.addressForBuy; |
| 375 | } else if (key.currentState!.addressController.text == |
| 376 | buySellViewModel.wallet.walletAddresses.addressForBuy) { |
| 377 | key.currentState!.changeWalletName(''); |
| 378 | key.currentState!.addressController.text = ''; |
| 379 | } |
| 380 | } |
| 381 | |
| 382 | void disposeBestRateSync() => {}; |
| 383 | |
| 384 | Widget _exchangeCardsSection(BuildContext context) { |
| 385 | final fiatExchangeCard = Observer( |
| 386 | builder: (_) => ExchangeCard( |
| 387 | cardInstanceName: 'fiat_currency_trade_card', |
| 388 | onDispose: disposeBestRateSync, |
| 389 | amountFocusNode: _fiatAmountFocus, |
| 390 | key: fiatCurrencyKey, |
| 391 | title: 'FIAT ${S.of(context).amount}', |
| 392 | initialCurrency: buySellViewModel.fiatCurrency, |
| 393 | initialWalletName: '', |
| 394 | initialAddress: '', |
| 395 | initialIsAmountEditable: true, |
| 396 | isAmountEstimated: false, |
| 397 | currencyRowPadding: EdgeInsets.zero, |
| 398 | addressRowPadding: EdgeInsets.zero, |
| 399 | isMoneroWallet: buySellViewModel.wallet == WalletType.monero, |
| 400 | showAddressField: false, |
| 401 | showLimitsField: false, |
| 402 | currencies: buySellViewModel.fiatCurrencies, |
| 403 | onCurrencySelected: (currency) => buySellViewModel.changeFiatCurrency(currency: currency), |
| 404 | onTapCurrencyPicker: _presentFiatPicker, |
| 405 | imageArrow: Image.asset( |
| 406 | 'assets/images/arrow_bottom_purple_icon.png', |
| 407 | color: Theme.of(context).colorScheme.primary, |
| 408 | height: 8, |
| 409 | ), |
| 410 | currencyButtonColor: Colors.transparent, |
| 411 | addressButtonsColor: Theme.of(context).colorScheme.surfaceContainerHighest, |
| 412 | borderColor: Theme.of(context).colorScheme.outlineVariant, |
| 413 | onPushPasteButton: (context) async { |
| 414 | final domain = cryptoCurrencyKey.currentState!.addressController.text; |
| 415 | final parsed = await fetchParsedAddress(context, domain, buySellViewModel.cryptoCurrency); |
| 416 | if (parsed.isNotEmpty) { |
| 417 | buySellViewModel.cryptoCurrencyAddress = parsed; |
| 418 | } |
| 419 | }, |
| 420 | onPushAddressBookButton: (context) async { |
| 421 | final domain = cryptoCurrencyKey.currentState!.addressController.text; |
| 422 | final parsed = await fetchParsedAddress(context, domain, buySellViewModel.cryptoCurrency); |
| 423 | if (parsed.isNotEmpty) { |
| 424 | buySellViewModel.cryptoCurrencyAddress = parsed; |
| 425 | } |
| 426 | }, |
| 427 | fillColor: buySellViewModel.isBuyAction |
| 428 | ? Theme.of(context).colorScheme.surfaceContainer |
| 429 | : Theme.of(context).colorScheme.surfaceContainerLow, |
| 430 | ), |
| 431 | ); |
| 432 | |
| 433 | final cryptoExchangeCard = Observer( |
| 434 | builder: (_) => ExchangeCard( |
| 435 | cardInstanceName: 'crypto_currency_trade_card', |
| 436 | onDispose: disposeBestRateSync, |
| 437 | amountFocusNode: _cryptoAmountFocus, |
| 438 | addressFocusNode: _cryptoAddressFocus, |
| 439 | key: cryptoCurrencyKey, |
| 440 | title: 'Crypto ${S.of(context).amount}', |
| 441 | initialCurrency: buySellViewModel.cryptoCurrency, |
| 442 | initialWalletName: '', |
| 443 | initialAddress: buySellViewModel.cryptoCurrency == buySellViewModel.wallet.currency |
| 444 | ? buySellViewModel.wallet.walletAddresses.addressForBuy |
| 445 | : buySellViewModel.cryptoCurrencyAddress, |
| 446 | initialIsAmountEditable: true, |
| 447 | isAmountEstimated: true, |
| 448 | showLimitsField: false, |
| 449 | currencyRowPadding: EdgeInsets.zero, |
| 450 | addressRowPadding: EdgeInsets.zero, |
| 451 | isMoneroWallet: buySellViewModel.wallet == WalletType.monero, |
| 452 | currencies: buySellViewModel.cryptoCurrencies, |
| 453 | onCurrencySelected: (currency) => buySellViewModel.changeCryptoCurrency(currency: currency), |
| 454 | onTapCurrencyPicker: _presentCryptoPicker, |
| 455 | imageArrow: Image.asset( |
| 456 | 'assets/images/arrow_bottom_cake_green.png', |
| 457 | color: Theme.of(context).colorScheme.primary, |
| 458 | height: 8, |
| 459 | ), |
| 460 | currencyButtonColor: Colors.transparent, |
| 461 | addressButtonsColor: Theme.of(context).colorScheme.surfaceContainerHighest, |
| 462 | borderColor: Theme.of(context).colorScheme.outlineVariant, |
| 463 | addressTextFieldValidator: AddressValidator(type: buySellViewModel.cryptoCurrency), |
| 464 | onPushPasteButton: (context) async {}, |
| 465 | onPushAddressBookButton: (context) async {}, |
| 466 | fillColor: buySellViewModel.isBuyAction |
| 467 | ? Theme.of(context).colorScheme.surfaceContainerLow |
| 468 | : Theme.of(context).colorScheme.surfaceContainer, |
| 469 | useSatoshis: buySellViewModel.useSatoshi, |
| 470 | ), |
| 471 | ); |
| 472 | |
| 473 | if (responsiveLayoutUtil.shouldRenderMobileUI) { |
| 474 | return Observer( |
| 475 | builder: (_) { |
| 476 | if (buySellViewModel.isBuyAction) { |
| 477 | return MobileExchangeCardsSection( |
| 478 | firstExchangeCard: fiatExchangeCard, |
| 479 | secondExchangeCard: cryptoExchangeCard, |
| 480 | onBuyTap: () => null, |
| 481 | onSellTap: () => |
| 482 | buySellViewModel.isBuyAction ? buySellViewModel.changeBuySellAction() : null, |
| 483 | isBuySellOption: true, |
| 484 | ); |
| 485 | } else { |
| 486 | return MobileExchangeCardsSection( |
| 487 | firstExchangeCard: cryptoExchangeCard, |
| 488 | secondExchangeCard: fiatExchangeCard, |
| 489 | onBuyTap: () => |
| 490 | !buySellViewModel.isBuyAction ? buySellViewModel.changeBuySellAction() : null, |
| 491 | onSellTap: () => null, |
| 492 | isBuySellOption: true, |
| 493 | ); |
| 494 | } |
| 495 | }, |
| 496 | ); |
| 497 | } |
| 498 | |
| 499 | return Observer( |
| 500 | builder: (_) { |
| 501 | if (buySellViewModel.isBuyAction) { |
| 502 | return DesktopExchangeCardsSection( |
| 503 | firstExchangeCard: fiatExchangeCard, |
| 504 | secondExchangeCard: cryptoExchangeCard, |
| 505 | onBuyTap: () => null, |
| 506 | onSellTap: () => |
| 507 | buySellViewModel.isBuyAction ? buySellViewModel.changeBuySellAction() : null, |
| 508 | isBuySellOption: true, |
| 509 | ); |
| 510 | } else { |
| 511 | return DesktopExchangeCardsSection( |
| 512 | firstExchangeCard: cryptoExchangeCard, |
| 513 | secondExchangeCard: fiatExchangeCard, |
| 514 | onBuyTap: () => |
| 515 | !buySellViewModel.isBuyAction ? buySellViewModel.changeBuySellAction() : null, |
| 516 | onSellTap: () => null, |
| 517 | isBuySellOption: true, |
| 518 | ); |
| 519 | } |
| 520 | }, |
| 521 | ); |
| 522 | } |
| 523 | |
| 524 | void _presentFiatPicker(BuildContext context) { |
| 525 | FiatCurrencyPickerSheet.show( |
| 526 | context: context, |
| 527 | selected: buySellViewModel.fiatCurrency, |
| 528 | onSelected: (currency) => buySellViewModel.changeFiatCurrency(currency: currency), |
| 529 | ); |
| 530 | } |
| 531 | |
| 532 | void _presentCryptoPicker(BuildContext context) { |
| 533 | final items = [...buySellViewModel.cryptoCurrencies]; |
| 534 | appendEvmDefaultTokens(items); |
| 535 | CurrencyPickerSheet.show( |
| 536 | context: context, |
| 537 | args: CurrencyPickerArgs( |
| 538 | items: items, |
| 539 | selected: buySellViewModel.cryptoCurrency, |
| 540 | recentsSource: RecentsSource.orders, |
| 541 | onSelected: (currency) => buySellViewModel.changeCryptoCurrency(currency: currency), |
| 542 | symbolResolver: buySellViewModel.amountParsingProxy.getCryptoSymbol, |
| 543 | ), |
| 544 | ); |
| 545 | } |
| 546 | |
| 547 | Future<String> fetchParsedAddress( |
| 548 | BuildContext context, |
| 549 | String domain, |
| 550 | CryptoCurrency currency, |
| 551 | ) async { |
| 552 | final parsedAddresses = |
| 553 | await _resolver.resolve(query: domain, wallet: buySellViewModel.wallet, currency: currency); |
| 554 | return parsedAddresses.isNotEmpty |
| 555 | ? parsedAddresses.first.parsedAddressByCurrencyMap[currency] ?? '' |
| 556 | : ''; |
| 557 | } |
| 558 | } |