Skip to content

Commit 857b983

Browse files
committed
feat: coin control to exchange/swap send flow
closes #404
1 parent 51db6c7 commit 857b983

2 files changed

Lines changed: 259 additions & 54 deletions

File tree

lib/pages/exchange_view/exchange_step_views/step_4_view.dart

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import 'package:tuple/tuple.dart';
1717

1818
import '../../../app_config.dart';
1919
import '../../../models/exchange/incomplete_exchange.dart';
20+
import '../../../models/input.dart';
21+
import '../../../models/isar/models/isar_models.dart';
2022
import '../../../providers/providers.dart';
2123
import '../../../route_generator.dart';
2224
import '../../../themes/stack_colors.dart';
@@ -33,8 +35,10 @@ import '../../../wallets/crypto_currency/crypto_currency.dart';
3335
import '../../../wallets/isar/providers/wallet_info_provider.dart';
3436
import '../../../wallets/models/tx_data.dart';
3537
import '../../../wallets/wallet/impl/firo_wallet.dart';
38+
import '../../../wallets/wallet/wallet_mixin_interfaces/coin_control_interface.dart';
3639
import '../../../widgets/background.dart';
3740
import '../../../widgets/custom_buttons/app_bar_icon_button.dart';
41+
import '../../../widgets/custom_buttons/blue_text_button.dart';
3842
import '../../../widgets/custom_buttons/simple_copy_button.dart';
3943
import '../../../widgets/desktop/primary_button.dart';
4044
import '../../../widgets/desktop/secondary_button.dart';
@@ -43,6 +47,7 @@ import '../../../widgets/qr.dart';
4347
import '../../../widgets/rounded_container.dart';
4448
import '../../../widgets/rounded_white_container.dart';
4549
import '../../../widgets/stack_dialog.dart';
50+
import '../../coin_control/coin_control_view.dart';
4651
import '../../home_view/home_view.dart';
4752
import '../../send_view/sub_widgets/building_transaction_dialog.dart';
4853
import '../../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

Comments
 (0)