Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions lib/pages/exchange_view/exchange_step_views/step_4_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import 'package:tuple/tuple.dart';

import '../../../app_config.dart';
import '../../../models/exchange/incomplete_exchange.dart';
import '../../../models/isar/models/isar_models.dart';
import '../../../providers/providers.dart';
import '../../../route_generator.dart';
import '../../../themes/stack_colors.dart';
Expand Down Expand Up @@ -46,6 +47,7 @@ import '../../../widgets/stack_dialog.dart';
import '../../home_view/home_view.dart';
import '../../send_view/sub_widgets/building_transaction_dialog.dart';
import '../../wallet_view/wallet_view.dart';
import '../sub_widgets/exchange_coin_control.dart';
import '../confirm_change_now_send.dart';
import '../send_from_view.dart';
import '../sub_widgets/step_row.dart';
Expand All @@ -71,6 +73,12 @@ class _Step4ViewState extends ConsumerState<Step4View> {
late final IncompleteExchangeModel model;
late final ClipboardInterface clipboard;

Set<UTXO> _selectedUTXOs = {};
String? _selectedUtxoWalletId;

Set<UTXO> _selectionFor(String walletId) =>
_selectedUtxoWalletId == walletId ? _selectedUTXOs : const {};

String _statusString = "New";

Timer? _statusTimer;
Expand Down Expand Up @@ -270,6 +278,7 @@ class _Step4ViewState extends ConsumerState<Step4View> {
isSpark: wallet is FiroWallet && !firoPublicSend,
onCancel: () {
wasCancelled = true;
Navigator.of(context).pop();
},
);
},
Expand All @@ -287,6 +296,17 @@ class _Step4ViewState extends ConsumerState<Step4View> {
addressType: wallet.cryptoCurrency.getAddressType(address)!,
);

final selected = _selectionFor(tuple.item1);
final selectedInputs = selected.isEmpty
? null
: (await prepareExchangeCoinSelection(
walletId: tuple.item1,
wallet: wallet,
currentChainHeight: ref.read(pWalletChainHeight(tuple.item1)),
amount: amount,
selected: selected,
)).inputs;

if (wallet is FiroWallet && !firoPublicSend) {
throw Exception(
"Sending private Firo funds to an exchange address is temporarily "
Expand All @@ -303,6 +323,7 @@ class _Step4ViewState extends ConsumerState<Step4View> {
recipients: [recipient],
memo: memo,
feeRateType: FeeRateType.average,
utxos: selectedInputs,
note:
"${model.trade!.payInCurrency.toUpperCase()}/"
"${model.trade!.payOutCurrency.toUpperCase()} exchange",
Expand Down Expand Up @@ -343,6 +364,12 @@ class _Step4ViewState extends ConsumerState<Step4View> {
}
} catch (e, s) {
Logging.instance.e("$e\n$s", error: e, stackTrace: s);
if (e is ExchangeCoinSelectionException && e.clearSelection && mounted) {
setState(() {
_selectedUtxoWalletId = null;
_selectedUTXOs = {};
});
}
if (mounted && !wasCancelled) {
// pop building dialog
Navigator.of(context).pop();
Expand Down Expand Up @@ -521,6 +548,41 @@ class _Step4ViewState extends ConsumerState<Step4View> {
),
if (isWalletCoinAndCanSend)
const SizedBox(height: 12),
if (isWalletCoinAndCanSend)
Builder(
builder: (context) {
final tuple = ref
.watch(
exchangeSendFromWalletIdStateProvider
.state,
)
.state;
if (tuple == null ||
model.sendTicker.toLowerCase() !=
tuple.item2.ticker.toLowerCase()) {
return const SizedBox.shrink();
}
return ExchangeCoinControlSelector(
key: ValueKey(tuple.item1),
walletId: tuple.item1,
amount: model.sendAmount.toAmount(
fractionDigits:
tuple.item2.fractionDigits,
),
selected: _selectionFor(tuple.item1),
onChanged: (selected) {
setState(() {
_selectedUtxoWalletId = tuple.item1;
_selectedUTXOs = selected;
});
},
padding: const EdgeInsets.only(
bottom: 12,
),
rounded: true,
);
},
),
if (isWalletCoinAndCanSend)
_SendFromButton(
model: model,
Expand Down
151 changes: 94 additions & 57 deletions lib/pages/exchange_view/send_from_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import 'package:flutter_svg/svg.dart';

import '../../app_config.dart';
import '../../models/exchange/response_objects/trade.dart';
import '../../models/isar/models/isar_models.dart';
import '../../pages_desktop_specific/desktop_exchange/desktop_exchange_view.dart';
import '../../providers/providers.dart';
import '../../route_generator.dart';
Expand Down Expand Up @@ -48,6 +49,7 @@ import '../../widgets/stack_dialog.dart';
import '../home_view/home_view.dart';
import '../send_view/sub_widgets/building_transaction_dialog.dart';
import 'confirm_change_now_send.dart';
import 'sub_widgets/exchange_coin_control.dart';

class SendFromView extends ConsumerStatefulWidget {
const SendFromView({
Expand Down Expand Up @@ -222,12 +224,13 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
late final String address;
late final Trade trade;

Set<UTXO> _selectedUTXOs = {};

Future<void> _send({bool? shouldSendPublicFiroFunds}) async {
final coin = ref.read(pWalletCoin(walletId));
var wasCancelled = false;

try {
bool wasCancelled = false;

final wallet = ref.read(pWallets).getWallet(walletId);

unawaited(
Expand Down Expand Up @@ -278,6 +281,16 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
addressType: wallet.cryptoCurrency.getAddressType(address)!,
);

final selectedInputs = _selectedUTXOs.isEmpty
? null
: (await prepareExchangeCoinSelection(
walletId: walletId,
wallet: wallet,
currentChainHeight: ref.read(pWalletChainHeight(walletId)),
amount: amount,
selected: _selectedUTXOs,
)).inputs;

// if not firo then do normal send
if (shouldSendPublicFiroFunds == null) {
final memo = coin is Stellar || coin is Solana
Expand All @@ -290,6 +303,7 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
recipients: [recipient],
memo: memo,
feeRateType: FeeRateType.average,
utxos: selectedInputs,
),
);
} else {
Expand All @@ -299,6 +313,7 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
txData: TxData(
recipients: [recipient],
feeRateType: FeeRateType.average,
utxos: selectedInputs,
),
);
} else {
Expand Down Expand Up @@ -349,7 +364,10 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
}
} catch (e, s) {
Logging.instance.e("$e\n$s", error: e, stackTrace: s);
if (mounted) {
if (e is ExchangeCoinSelectionException && e.clearSelection && mounted) {
setState(() => _selectedUTXOs = {});
}
if (mounted && !wasCancelled) {
// pop building dialog
Navigator.of(context, rootNavigator: Util.isDesktop).pop();

Expand Down Expand Up @@ -543,68 +561,87 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
],
),
),
child: ConditionalParent(
condition: !isFiro,
builder: (child) => MaterialButton(
splashColor: Theme.of(context).extension<StackColors>()!.highlight,
key: Key("walletsSheetItemButtonKey_$walletId"),
padding: const EdgeInsets.all(8),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
onPressed: () async {
if (mounted) {
unawaited(_send());
}
},
child: child,
),
child: Row(
children: [
Container(
decoration: BoxDecoration(
color: ref.watch(pCoinColor(coin)).withOpacity(0.5),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ConditionalParent(
condition: !isFiro,
builder: (child) => MaterialButton(
splashColor: Theme.of(
context,
).extension<StackColors>()!.highlight,
key: Key("walletsSheetItemButtonKey_$walletId"),
padding: const EdgeInsets.all(8),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
child: Padding(
padding: const EdgeInsets.all(6),
child: SvgPicture.file(
File(ref.watch(coinIconProvider(coin))),
width: 24,
height: 24,
),
),
onPressed: () async {
if (mounted) {
unawaited(_send());
}
},
child: child,
),
const SizedBox(width: 12),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
ref.watch(pWalletName(walletId)),
style: STextStyles.titleBold12(context),
child: Row(
children: [
Container(
decoration: BoxDecoration(
color: ref.watch(pCoinColor(coin)).withOpacity(0.5),
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
if (!isFiro) const SizedBox(height: 2),
if (!isFiro)
Text(
ref
.watch(pAmountFormatter(coin))
.format(
ref.watch(pWalletBalance(walletId)).spendable,
),
style: STextStyles.itemSubtitle(context),
child: Padding(
padding: const EdgeInsets.all(6),
child: SvgPicture.file(
File(ref.watch(coinIconProvider(coin))),
width: 24,
height: 24,
),
],
),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
ref.watch(pWalletName(walletId)),
style: STextStyles.titleBold12(context),
),
if (!isFiro) const SizedBox(height: 2),
if (!isFiro)
Text(
ref
.watch(pAmountFormatter(coin))
.format(
ref.watch(pWalletBalance(walletId)).spendable,
),
style: STextStyles.itemSubtitle(context),
),
],
),
),
],
),
],
),
),
ExchangeCoinControlSelector(
walletId: walletId,
amount: amount,
selected: _selectedUTXOs,
onChanged: (selected) {
setState(() => _selectedUTXOs = selected);
},
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
),
],
),
),
);
Expand Down
Loading
Loading