mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-12-23 03:49:22 +00:00
feat: WIP desktop custom fee selection for supported coins
This commit is contained in:
parent
04e0446aaf
commit
fae0c778ef
3 changed files with 629 additions and 82 deletions
|
@ -931,6 +931,43 @@ class _ConfirmTransactionViewState
|
|||
),
|
||||
),
|
||||
),
|
||||
if (isDesktop &&
|
||||
!widget.isPaynymTransaction &&
|
||||
transactionInfo["fee"] is int &&
|
||||
transactionInfo["vSize"] is int)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 32,
|
||||
),
|
||||
child: Text(
|
||||
"sats/vByte",
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context),
|
||||
),
|
||||
),
|
||||
if (isDesktop &&
|
||||
!widget.isPaynymTransaction &&
|
||||
transactionInfo["fee"] is int &&
|
||||
transactionInfo["vSize"] is int)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 10,
|
||||
left: 32,
|
||||
right: 32,
|
||||
),
|
||||
child: RoundedContainer(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 18,
|
||||
),
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultBG,
|
||||
child: Text(
|
||||
"~${(transactionInfo["fee"] / transactionInfo["vSize"]).toInt()}",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!isDesktop) const Spacer(),
|
||||
SizedBox(
|
||||
height: isDesktop ? 23 : 12,
|
||||
|
|
|
@ -12,6 +12,7 @@ import 'dart:async';
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:bip47/bip47.dart';
|
||||
import 'package:cw_core/monero_transaction_priority.dart';
|
||||
import 'package:decimal/decimal.dart';
|
||||
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -45,16 +46,20 @@ import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
|
|||
import 'package:stackwallet/utilities/clipboard_interface.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/prefs.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/animated_text.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_fee_dialog.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
import 'package:stackwallet/widgets/fee_slider.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/addressbook_icon.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
||||
|
@ -115,6 +120,17 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
|
||||
bool get isPaynymSend => widget.accountLite != null;
|
||||
|
||||
bool isCustomFee = false;
|
||||
int customFeeRate = 1;
|
||||
(FeeRateType, String?, String?)? feeSelectionResult;
|
||||
|
||||
final stringsToLoopThrough = [
|
||||
"Calculating",
|
||||
"Calculating.",
|
||||
"Calculating..",
|
||||
"Calculating...",
|
||||
];
|
||||
|
||||
Future<void> previewSend() async {
|
||||
final manager =
|
||||
ref.read(walletsChangeNotifierProvider).getManager(walletId);
|
||||
|
@ -283,6 +299,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
isSegwit: widget.accountLite!.segwit,
|
||||
amount: amount,
|
||||
args: {
|
||||
"satsPerVByte": isCustomFee ? customFeeRate : null,
|
||||
"feeRate": feeRate,
|
||||
"UTXOs": (manager.hasCoinControlSupport &&
|
||||
coinControlEnabled &&
|
||||
|
@ -299,6 +316,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
amount: amount,
|
||||
args: {
|
||||
"feeRate": ref.read(feeRateTypeStateProvider),
|
||||
"satsPerVByte": isCustomFee ? customFeeRate : null,
|
||||
"UTXOs": (manager.hasCoinControlSupport &&
|
||||
coinControlEnabled &&
|
||||
ref.read(desktopUseUTXOs).isNotEmpty)
|
||||
|
@ -312,6 +330,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
amount: amount,
|
||||
args: {
|
||||
"feeRate": ref.read(feeRateTypeStateProvider),
|
||||
"satsPerVByte": isCustomFee ? customFeeRate : null,
|
||||
"UTXOs": (manager.hasCoinControlSupport &&
|
||||
coinControlEnabled &&
|
||||
ref.read(desktopUseUTXOs).isNotEmpty)
|
||||
|
@ -561,12 +580,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
);
|
||||
} else {
|
||||
return AnimatedText(
|
||||
stringsToLoopThrough: const [
|
||||
"Loading balance",
|
||||
"Loading balance.",
|
||||
"Loading balance..",
|
||||
"Loading balance...",
|
||||
],
|
||||
stringsToLoopThrough: stringsToLoopThrough,
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
);
|
||||
}
|
||||
|
@ -1359,80 +1373,50 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
}
|
||||
},
|
||||
),
|
||||
// const SizedBox(
|
||||
// height: 20,
|
||||
// ),
|
||||
// Text(
|
||||
// "Note (optional)",
|
||||
// style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
// color: Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .textFieldActiveSearchIconRight,
|
||||
// ),
|
||||
// textAlign: TextAlign.left,
|
||||
// ),
|
||||
// const SizedBox(
|
||||
// height: 10,
|
||||
// ),
|
||||
// ClipRRect(
|
||||
// borderRadius: BorderRadius.circular(
|
||||
// Constants.size.circularBorderRadius,
|
||||
// ),
|
||||
// child: TextField(
|
||||
// minLines: 1,
|
||||
// maxLines: 5,
|
||||
// autocorrect: Util.isDesktop ? false : true,
|
||||
// enableSuggestions: Util.isDesktop ? false : true,
|
||||
// controller: noteController,
|
||||
// focusNode: _noteFocusNode,
|
||||
// style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
// color: Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .textFieldActiveText,
|
||||
// height: 1.8,
|
||||
// ),
|
||||
// onChanged: (_) => setState(() {}),
|
||||
// decoration: standardInputDecoration(
|
||||
// "Type something...",
|
||||
// _noteFocusNode,
|
||||
// context,
|
||||
// desktopMed: true,
|
||||
// ).copyWith(
|
||||
// contentPadding: const EdgeInsets.only(
|
||||
// left: 16,
|
||||
// top: 11,
|
||||
// bottom: 12,
|
||||
// right: 5,
|
||||
// ),
|
||||
// suffixIcon: noteController.text.isNotEmpty
|
||||
// ? Padding(
|
||||
// padding: const EdgeInsets.only(right: 0),
|
||||
// child: UnconstrainedBox(
|
||||
// child: Row(
|
||||
// children: [
|
||||
// TextFieldIconButton(
|
||||
// child: const XIcon(),
|
||||
// onTap: () async {
|
||||
// setState(() {
|
||||
// noteController.text = "";
|
||||
// });
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
// : null,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
if (!isPaynymSend)
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
if (!([Coin.nano, Coin.banano, Coin.epicCash].contains(coin)))
|
||||
Text(
|
||||
"Transaction fee (${coin == Coin.ethereum ? "max" : "estimated"})",
|
||||
ConditionalParent(
|
||||
condition: coin.isElectrumXCoin,
|
||||
builder: (child) => Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
child,
|
||||
CustomTextButton(
|
||||
text: "Edit",
|
||||
onTap: () async {
|
||||
feeSelectionResult = await showDialog<
|
||||
(
|
||||
FeeRateType,
|
||||
String?,
|
||||
String?,
|
||||
)?>(
|
||||
context: context,
|
||||
builder: (_) => DesktopFeeDialog(
|
||||
walletId: walletId,
|
||||
),
|
||||
);
|
||||
|
||||
if (feeSelectionResult != null) {
|
||||
if (isCustomFee &&
|
||||
feeSelectionResult!.$1 != FeeRateType.custom) {
|
||||
isCustomFee = false;
|
||||
} else if (!isCustomFee &&
|
||||
feeSelectionResult!.$1 == FeeRateType.custom) {
|
||||
isCustomFee = true;
|
||||
}
|
||||
}
|
||||
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Text(
|
||||
"Transaction fee"
|
||||
"${isCustomFee ? "" : " (${coin == Coin.ethereum ? "max" : "estimated"})"}",
|
||||
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
|
@ -1440,13 +1424,127 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
),
|
||||
if (!([Coin.nano, Coin.banano, Coin.epicCash].contains(coin)))
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
if (!([Coin.nano, Coin.banano, Coin.epicCash].contains(coin)))
|
||||
DesktopFeeDropDown(
|
||||
if (!isCustomFee)
|
||||
(feeSelectionResult?.$2 == null)
|
||||
? FutureBuilder(
|
||||
future: ref.watch(
|
||||
walletsChangeNotifierProvider.select(
|
||||
(value) => value.getManager(walletId).fees,
|
||||
),
|
||||
),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done &&
|
||||
snapshot.hasData) {
|
||||
return DesktopFeeItem(
|
||||
feeObject: snapshot.data,
|
||||
feeRateType: FeeRateType.average,
|
||||
walletId: walletId,
|
||||
feeFor: ({
|
||||
required Amount amount,
|
||||
required FeeRateType feeRateType,
|
||||
required int feeRate,
|
||||
required Coin coin,
|
||||
}) async {
|
||||
if (ref
|
||||
.read(feeSheetSessionCacheProvider)
|
||||
.average[amount] ==
|
||||
null) {
|
||||
final manager = ref
|
||||
.read(walletsChangeNotifierProvider)
|
||||
.getManager(walletId);
|
||||
|
||||
if (coin == Coin.monero || coin == Coin.wownero) {
|
||||
final fee = await manager.estimateFeeFor(amount,
|
||||
MoneroTransactionPriority.regular.raw!);
|
||||
ref
|
||||
.read(feeSheetSessionCacheProvider)
|
||||
.average[amount] = fee;
|
||||
} else if ((coin == Coin.firo ||
|
||||
coin == Coin.firoTestNet) &&
|
||||
ref
|
||||
.read(
|
||||
publicPrivateBalanceStateProvider
|
||||
.state)
|
||||
.state !=
|
||||
"Private") {
|
||||
ref
|
||||
.read(feeSheetSessionCacheProvider)
|
||||
.average[amount] =
|
||||
await (manager.wallet as FiroWallet)
|
||||
.estimateFeeForPublic(amount, feeRate);
|
||||
} else {
|
||||
ref
|
||||
.read(feeSheetSessionCacheProvider)
|
||||
.average[amount] =
|
||||
await manager.estimateFeeFor(
|
||||
amount, feeRate);
|
||||
}
|
||||
}
|
||||
return ref
|
||||
.read(feeSheetSessionCacheProvider)
|
||||
.average[amount]!;
|
||||
},
|
||||
isSelected: true,
|
||||
);
|
||||
} else {
|
||||
return Row(
|
||||
children: [
|
||||
AnimatedText(
|
||||
stringsToLoopThrough: stringsToLoopThrough,
|
||||
style: STextStyles.desktopTextExtraExtraSmall(
|
||||
context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveText,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
},
|
||||
)
|
||||
: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
feeSelectionResult?.$2 ?? "",
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveText,
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
Text(
|
||||
feeSelectionResult?.$3 ?? "",
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveSearchIconRight,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (isCustomFee)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
bottom: 12,
|
||||
top: 16,
|
||||
),
|
||||
child: FeeSlider(
|
||||
onSatVByteChanged: (rate) {
|
||||
customFeeRate = rate;
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 36,
|
||||
|
|
412
lib/widgets/desktop/desktop_fee_dialog.dart
Normal file
412
lib/widgets/desktop/desktop_fee_dialog.dart
Normal file
|
@ -0,0 +1,412 @@
|
|||
import 'package:cw_core/monero_transaction_priority.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/models/models.dart';
|
||||
import 'package:stackwallet/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart';
|
||||
import 'package:stackwallet/pages/token_view/token_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart';
|
||||
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
||||
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
|
||||
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
|
||||
import 'package:stackwallet/themes/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/widgets/animated_text.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
||||
|
||||
class DesktopFeeDialog extends ConsumerStatefulWidget {
|
||||
const DesktopFeeDialog({
|
||||
Key? key,
|
||||
required this.walletId,
|
||||
this.isToken = false,
|
||||
}) : super(key: key);
|
||||
|
||||
final String walletId;
|
||||
final bool isToken;
|
||||
|
||||
@override
|
||||
ConsumerState<DesktopFeeDialog> createState() => _DesktopFeeDialogState();
|
||||
}
|
||||
|
||||
class _DesktopFeeDialogState extends ConsumerState<DesktopFeeDialog> {
|
||||
late final String walletId;
|
||||
|
||||
FeeObject? feeObject;
|
||||
FeeRateType feeRateType = FeeRateType.average;
|
||||
|
||||
Future<Amount> feeFor({
|
||||
required Amount amount,
|
||||
required FeeRateType feeRateType,
|
||||
required int feeRate,
|
||||
required Coin coin,
|
||||
}) async {
|
||||
switch (feeRateType) {
|
||||
case FeeRateType.fast:
|
||||
if (ref
|
||||
.read(widget.isToken
|
||||
? tokenFeeSessionCacheProvider
|
||||
: feeSheetSessionCacheProvider)
|
||||
.fast[amount] ==
|
||||
null) {
|
||||
if (widget.isToken == false) {
|
||||
final manager =
|
||||
ref.read(walletsChangeNotifierProvider).getManager(walletId);
|
||||
|
||||
if (coin == Coin.monero || coin == Coin.wownero) {
|
||||
final fee = await manager.estimateFeeFor(
|
||||
amount, MoneroTransactionPriority.fast.raw!);
|
||||
ref.read(feeSheetSessionCacheProvider).fast[amount] = fee;
|
||||
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
||||
ref.read(publicPrivateBalanceStateProvider.state).state !=
|
||||
"Private") {
|
||||
ref.read(feeSheetSessionCacheProvider).fast[amount] =
|
||||
await (manager.wallet as FiroWallet)
|
||||
.estimateFeeForPublic(amount, feeRate);
|
||||
} else {
|
||||
ref.read(feeSheetSessionCacheProvider).fast[amount] =
|
||||
await manager.estimateFeeFor(amount, feeRate);
|
||||
}
|
||||
} else {
|
||||
final tokenWallet = ref.read(tokenServiceProvider)!;
|
||||
final fee = tokenWallet.estimateFeeFor(feeRate);
|
||||
ref.read(tokenFeeSessionCacheProvider).fast[amount] = fee;
|
||||
}
|
||||
}
|
||||
return ref
|
||||
.read(widget.isToken
|
||||
? tokenFeeSessionCacheProvider
|
||||
: feeSheetSessionCacheProvider)
|
||||
.fast[amount]!;
|
||||
|
||||
case FeeRateType.average:
|
||||
if (ref
|
||||
.read(widget.isToken
|
||||
? tokenFeeSessionCacheProvider
|
||||
: feeSheetSessionCacheProvider)
|
||||
.average[amount] ==
|
||||
null) {
|
||||
if (widget.isToken == false) {
|
||||
final manager =
|
||||
ref.read(walletsChangeNotifierProvider).getManager(walletId);
|
||||
|
||||
if (coin == Coin.monero || coin == Coin.wownero) {
|
||||
final fee = await manager.estimateFeeFor(
|
||||
amount, MoneroTransactionPriority.regular.raw!);
|
||||
ref.read(feeSheetSessionCacheProvider).average[amount] = fee;
|
||||
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
||||
ref.read(publicPrivateBalanceStateProvider.state).state !=
|
||||
"Private") {
|
||||
ref.read(feeSheetSessionCacheProvider).average[amount] =
|
||||
await (manager.wallet as FiroWallet)
|
||||
.estimateFeeForPublic(amount, feeRate);
|
||||
} else {
|
||||
ref.read(feeSheetSessionCacheProvider).average[amount] =
|
||||
await manager.estimateFeeFor(amount, feeRate);
|
||||
}
|
||||
} else {
|
||||
final tokenWallet = ref.read(tokenServiceProvider)!;
|
||||
final fee = tokenWallet.estimateFeeFor(feeRate);
|
||||
ref.read(tokenFeeSessionCacheProvider).average[amount] = fee;
|
||||
}
|
||||
}
|
||||
return ref
|
||||
.read(widget.isToken
|
||||
? tokenFeeSessionCacheProvider
|
||||
: feeSheetSessionCacheProvider)
|
||||
.average[amount]!;
|
||||
|
||||
case FeeRateType.slow:
|
||||
if (ref
|
||||
.read(widget.isToken
|
||||
? tokenFeeSessionCacheProvider
|
||||
: feeSheetSessionCacheProvider)
|
||||
.slow[amount] ==
|
||||
null) {
|
||||
if (widget.isToken == false) {
|
||||
final manager =
|
||||
ref.read(walletsChangeNotifierProvider).getManager(walletId);
|
||||
|
||||
if (coin == Coin.monero || coin == Coin.wownero) {
|
||||
final fee = await manager.estimateFeeFor(
|
||||
amount, MoneroTransactionPriority.slow.raw!);
|
||||
ref.read(feeSheetSessionCacheProvider).slow[amount] = fee;
|
||||
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
||||
ref.read(publicPrivateBalanceStateProvider.state).state !=
|
||||
"Private") {
|
||||
ref.read(feeSheetSessionCacheProvider).slow[amount] =
|
||||
await (manager.wallet as FiroWallet)
|
||||
.estimateFeeForPublic(amount, feeRate);
|
||||
} else {
|
||||
ref.read(feeSheetSessionCacheProvider).slow[amount] =
|
||||
await manager.estimateFeeFor(amount, feeRate);
|
||||
}
|
||||
} else {
|
||||
final tokenWallet = ref.read(tokenServiceProvider)!;
|
||||
final fee = tokenWallet.estimateFeeFor(feeRate);
|
||||
ref.read(tokenFeeSessionCacheProvider).slow[amount] = fee;
|
||||
}
|
||||
}
|
||||
return ref
|
||||
.read(widget.isToken
|
||||
? tokenFeeSessionCacheProvider
|
||||
: feeSheetSessionCacheProvider)
|
||||
.slow[amount]!;
|
||||
default:
|
||||
return Amount.zero;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
walletId = widget.walletId;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DesktopDialog(
|
||||
maxWidth: 450,
|
||||
maxHeight: double.infinity,
|
||||
child: FutureBuilder(
|
||||
future: ref.watch(
|
||||
walletsChangeNotifierProvider.select(
|
||||
(value) => value.getManager(walletId).fees,
|
||||
),
|
||||
),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done &&
|
||||
snapshot.hasData) {
|
||||
feeObject = snapshot.data!;
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 32),
|
||||
child: Text(
|
||||
"Choose fee",
|
||||
style: STextStyles.desktopH3(context),
|
||||
),
|
||||
),
|
||||
const DesktopDialogCloseButton(),
|
||||
],
|
||||
),
|
||||
...FeeRateType.values.map(
|
||||
(e) => Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 32,
|
||||
right: 32,
|
||||
bottom: 16,
|
||||
),
|
||||
child: DesktopFeeItem(
|
||||
feeObject: feeObject,
|
||||
feeRateType: e,
|
||||
walletId: walletId,
|
||||
feeFor: feeFor,
|
||||
isSelected: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DesktopFeeItem extends ConsumerStatefulWidget {
|
||||
const DesktopFeeItem({
|
||||
Key? key,
|
||||
required this.feeObject,
|
||||
required this.feeRateType,
|
||||
required this.walletId,
|
||||
required this.feeFor,
|
||||
required this.isSelected,
|
||||
}) : super(key: key);
|
||||
|
||||
final FeeObject? feeObject;
|
||||
final FeeRateType feeRateType;
|
||||
final String walletId;
|
||||
final Future<Amount> Function({
|
||||
required Amount amount,
|
||||
required FeeRateType feeRateType,
|
||||
required int feeRate,
|
||||
required Coin coin,
|
||||
}) feeFor;
|
||||
final bool isSelected;
|
||||
|
||||
@override
|
||||
ConsumerState<DesktopFeeItem> createState() => _DesktopFeeItemState();
|
||||
}
|
||||
|
||||
class _DesktopFeeItemState extends ConsumerState<DesktopFeeItem> {
|
||||
String? feeString;
|
||||
String? timeString;
|
||||
|
||||
static const stringsToLoopThrough = [
|
||||
"Calculating",
|
||||
"Calculating.",
|
||||
"Calculating..",
|
||||
"Calculating...",
|
||||
];
|
||||
|
||||
String estimatedTimeToBeIncludedInNextBlock(
|
||||
int targetBlockTime, int estimatedNumberOfBlocks) {
|
||||
int time = targetBlockTime * estimatedNumberOfBlocks;
|
||||
|
||||
int hours = (time / 3600).floor();
|
||||
if (hours > 1) {
|
||||
return "~$hours hours";
|
||||
} else if (hours == 1) {
|
||||
return "~$hours hour";
|
||||
}
|
||||
|
||||
// less than an hour
|
||||
|
||||
final string = (time / 60).toStringAsFixed(1);
|
||||
|
||||
if (string == "1.0") {
|
||||
return "~1 minute";
|
||||
} else {
|
||||
if (string.endsWith(".0")) {
|
||||
return "~${(time / 60).floor()} minutes";
|
||||
}
|
||||
return "~$string minutes";
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
debugPrint("BUILD: $runtimeType : ${widget.feeRateType}");
|
||||
|
||||
return MaterialButton(
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(
|
||||
(
|
||||
widget.feeRateType,
|
||||
feeString,
|
||||
timeString,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Builder(
|
||||
builder: (_) {
|
||||
if (widget.feeRateType == FeeRateType.custom) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
widget.feeRateType.prettyName,
|
||||
style:
|
||||
STextStyles.desktopTextExtraExtraSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveText,
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
final manager = ref.watch(walletsChangeNotifierProvider
|
||||
.select((value) => value.getManager(widget.walletId)));
|
||||
|
||||
if (widget.feeObject == null) {
|
||||
return AnimatedText(
|
||||
stringsToLoopThrough: stringsToLoopThrough,
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveText,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return FutureBuilder(
|
||||
future: widget.feeFor(
|
||||
coin: manager.coin,
|
||||
feeRateType: widget.feeRateType,
|
||||
feeRate: widget.feeRateType == FeeRateType.fast
|
||||
? widget.feeObject!.fast
|
||||
: widget.feeRateType == FeeRateType.slow
|
||||
? widget.feeObject!.slow
|
||||
: widget.feeObject!.medium,
|
||||
amount: ref.watch(sendAmountProvider.state).state,
|
||||
),
|
||||
builder: (_, AsyncSnapshot<Amount> snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done &&
|
||||
snapshot.hasData) {
|
||||
feeString = "${widget.feeRateType.prettyName} "
|
||||
"(~${ref.watch(pAmountFormatter(manager.coin)).format(
|
||||
snapshot.data!,
|
||||
indicatePrecisionLoss: false,
|
||||
)})";
|
||||
|
||||
timeString = manager.coin == Coin.ethereum
|
||||
? ""
|
||||
: estimatedTimeToBeIncludedInNextBlock(
|
||||
Constants.targetBlockTimeInSeconds(manager.coin),
|
||||
widget.feeRateType == FeeRateType.fast
|
||||
? widget.feeObject!.numberOfBlocksFast
|
||||
: widget.feeRateType == FeeRateType.slow
|
||||
? widget.feeObject!.numberOfBlocksSlow
|
||||
: widget.feeObject!.numberOfBlocksAverage,
|
||||
);
|
||||
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
feeString!,
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveText,
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
if (widget.feeObject != null)
|
||||
Text(
|
||||
timeString!,
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveSearchIconRight,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return AnimatedText(
|
||||
stringsToLoopThrough: stringsToLoopThrough,
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveText,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue