@@ -17,6 +17,8 @@ import 'package:tuple/tuple.dart';
1717
1818import '../../../app_config.dart' ;
1919import '../../../models/exchange/incomplete_exchange.dart' ;
20+ import '../../../models/input.dart' ;
21+ import '../../../models/isar/models/isar_models.dart' ;
2022import '../../../providers/providers.dart' ;
2123import '../../../route_generator.dart' ;
2224import '../../../themes/stack_colors.dart' ;
@@ -33,8 +35,10 @@ import '../../../wallets/crypto_currency/crypto_currency.dart';
3335import '../../../wallets/isar/providers/wallet_info_provider.dart' ;
3436import '../../../wallets/models/tx_data.dart' ;
3537import '../../../wallets/wallet/impl/firo_wallet.dart' ;
38+ import '../../../wallets/wallet/wallet_mixin_interfaces/coin_control_interface.dart' ;
3639import '../../../widgets/background.dart' ;
3740import '../../../widgets/custom_buttons/app_bar_icon_button.dart' ;
41+ import '../../../widgets/custom_buttons/blue_text_button.dart' ;
3842import '../../../widgets/custom_buttons/simple_copy_button.dart' ;
3943import '../../../widgets/desktop/primary_button.dart' ;
4044import '../../../widgets/desktop/secondary_button.dart' ;
@@ -43,6 +47,7 @@ import '../../../widgets/qr.dart';
4347import '../../../widgets/rounded_container.dart' ;
4448import '../../../widgets/rounded_white_container.dart' ;
4549import '../../../widgets/stack_dialog.dart' ;
50+ import '../../coin_control/coin_control_view.dart' ;
4651import '../../home_view/home_view.dart' ;
4752import '../../send_view/sub_widgets/building_transaction_dialog.dart' ;
4853import '../../wallet_view/wallet_view.dart' ;
@@ -71,6 +76,8 @@ class _Step4ViewState extends ConsumerState<Step4View> {
7176 late final IncompleteExchangeModel model;
7277 late final ClipboardInterface clipboard;
7378
79+ Set <UTXO > _selectedUTXOs = {};
80+
7481 String _statusString = "New" ;
7582
7683 Timer ? _statusTimer;
@@ -287,6 +294,11 @@ class _Step4ViewState extends ConsumerState<Step4View> {
287294 addressType: wallet.cryptoCurrency.getAddressType (address)! ,
288295 );
289296
297+ // Build utxos set from selected UTXOs if any
298+ final selectedInputs = _selectedUTXOs.isNotEmpty
299+ ? _selectedUTXOs.map ((e) => StandardInput (e)).toSet ()
300+ : null ;
301+
290302 if (wallet is FiroWallet && ! firoPublicSend) {
291303 txDataFuture = wallet.prepareSendSpark (
292304 txData: TxData (
@@ -307,6 +319,7 @@ class _Step4ViewState extends ConsumerState<Step4View> {
307319 recipients: [recipient],
308320 memo: memo,
309321 feeRateType: FeeRateType .average,
322+ utxos: selectedInputs,
310323 note:
311324 "${model .trade !.payInCurrency .toUpperCase ()}/"
312325 "${model .trade !.payOutCurrency .toUpperCase ()} exchange" ,
@@ -525,6 +538,116 @@ class _Step4ViewState extends ConsumerState<Step4View> {
525538 ),
526539 if (isWalletCoinAndCanSend)
527540 const SizedBox (height: 12 ),
541+ if (isWalletCoinAndCanSend)
542+ Builder (
543+ builder: (context) {
544+ final tuple = ref.watch (
545+ exchangeSendFromWalletIdStateProvider
546+ .state,
547+ ).state;
548+ if (tuple == null ||
549+ model.sendTicker.toLowerCase () !=
550+ tuple.item2.ticker.toLowerCase ()) {
551+ return const SizedBox .shrink ();
552+ }
553+
554+ final wallet = ref
555+ .watch (pWallets)
556+ .getWallet (tuple.item1);
557+ final coinControlEnabled = ref.watch (
558+ prefsChangeNotifierProvider.select (
559+ (value) => value.enableCoinControl,
560+ ),
561+ );
562+
563+ // Firo sends from the exchange flow may
564+ // spend from the Spark (private) balance,
565+ // which selects coins via the native Spark
566+ // library and cannot honor a manual UTXO
567+ // selection. Hide coin control for Firo so
568+ // the UI does not promise control it would
569+ // silently ignore on a Spark send.
570+ if (wallet is ! CoinControlInterface ||
571+ wallet.info.coin is Firo ||
572+ ! coinControlEnabled) {
573+ return const SizedBox .shrink ();
574+ }
575+
576+ return Padding (
577+ padding: const EdgeInsets .only (
578+ bottom: 12 ,
579+ ),
580+ child: RoundedWhiteContainer (
581+ child: Row (
582+ mainAxisAlignment:
583+ MainAxisAlignment .spaceBetween,
584+ children: [
585+ Text (
586+ "Coin control" ,
587+ style: STextStyles .w500_14 (
588+ context,
589+ ).copyWith (
590+ color: Theme .of (context)
591+ .extension < StackColors > ()!
592+ .textSubtitle1,
593+ ),
594+ ),
595+ CustomTextButton (
596+ text: _selectedUTXOs.isEmpty
597+ ? "Select coins"
598+ : "Selected coins"
599+ " (${_selectedUTXOs .length })" ,
600+ onTap: () async {
601+ if (FocusScope .of (context)
602+ .hasFocus) {
603+ FocusScope .of (context)
604+ .unfocus ();
605+ await Future <void >.delayed (
606+ const Duration (
607+ milliseconds: 100 ,
608+ ),
609+ );
610+ }
611+
612+ if (context.mounted) {
613+ final sendAmount =
614+ model.sendAmount
615+ .toAmount (
616+ fractionDigits: wallet
617+ .info
618+ .coin
619+ .fractionDigits,
620+ );
621+
622+ final result =
623+ await Navigator .of (
624+ context,
625+ ).pushNamed (
626+ CoinControlView
627+ .routeName,
628+ arguments: Tuple4 (
629+ tuple.item1,
630+ CoinControlViewType
631+ .use,
632+ sendAmount,
633+ _selectedUTXOs,
634+ ),
635+ );
636+
637+ if (result is Set <UTXO >) {
638+ setState (() {
639+ _selectedUTXOs = result;
640+ });
641+ }
642+ }
643+ },
644+ ),
645+ ],
646+ ),
647+ ),
648+ );
649+ },
650+ ),
528651 if (isWalletCoinAndCanSend)
529652 _SendFromButton (
530653 model: model,
0 commit comments