diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 862ce9db5..9bdc0f3ac 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -291,4 +291,10 @@ class CWBitcoin extends Bitcoin { outputsCount, ); } + + @override + int getMaxCustomFeeRate(Object wallet) { + final bitcoinWallet = wallet as ElectrumWallet; + return (bitcoinWallet.feeRate(BitcoinTransactionPriority.fast) * 1.1).round(); + } } diff --git a/lib/src/screens/send/widgets/send_card.dart b/lib/src/screens/send/widgets/send_card.dart index 7c2bfedd0..c9ae5182a 100644 --- a/lib/src/screens/send/widgets/send_card.dart +++ b/lib/src/screens/send/widgets/send_card.dart @@ -675,6 +675,7 @@ class SendCardState extends State with AutomaticKeepAliveClientMixin( @@ -689,6 +690,7 @@ class SendCardState extends State with AutomaticKeepAliveClientMixin extends StandardListRow { this.isGridView = false, this.matchingCriteria, this.customValue, + this.maxValue, this.customItemIndex, this.onItemSelected}) : super( @@ -34,6 +35,7 @@ class SettingsPriorityPickerCell extends StandardListRow { displayItem: (ItemType item) => displayItem!(item, sliderValue.round()), selectedAtIndex: selectedAtIndex, customItemIndex: customItemIndex, + maxValue: maxValue, headerEnabled: false, closeOnItemSelected: false, mainAxisAlignment: MainAxisAlignment.center, @@ -61,6 +63,7 @@ class SettingsPriorityPickerCell extends StandardListRow { final bool isGridView; final bool Function(ItemType, String)? matchingCriteria; double? customValue; + double? maxValue; int? customItemIndex; @override diff --git a/lib/src/screens/transaction_details/rbf_details_list_fee_picker_item.dart b/lib/src/screens/transaction_details/rbf_details_list_fee_picker_item.dart index 8f722ee7e..7615065d7 100644 --- a/lib/src/screens/transaction_details/rbf_details_list_fee_picker_item.dart +++ b/lib/src/screens/transaction_details/rbf_details_list_fee_picker_item.dart @@ -10,6 +10,7 @@ class StandardPickerListItem extends TransactionDetailsListItem { required this.onItemSelected, required this.selectedIdx, required this.customItemIndex, + this.maxValue, required this.customValue}) : super(title: title, value: value); @@ -18,6 +19,7 @@ class StandardPickerListItem extends TransactionDetailsListItem { final Function(double) onSliderChanged; final Function(T) onItemSelected; final int selectedIdx; + final double? maxValue; final int customItemIndex; double customValue; } diff --git a/lib/src/screens/transaction_details/rbf_details_page.dart b/lib/src/screens/transaction_details/rbf_details_page.dart index 875e0a4ef..3faec48a8 100644 --- a/lib/src/screens/transaction_details/rbf_details_page.dart +++ b/lib/src/screens/transaction_details/rbf_details_page.dart @@ -74,6 +74,7 @@ class RBFDetailsPage extends BasePage { selectedIdx: item.selectedIdx, customItemIndex: item.customItemIndex, customValue: item.customValue, + maxValue: item.maxValue, ); } diff --git a/lib/src/widgets/picker.dart b/lib/src/widgets/picker.dart index d87b5721e..b744d1db0 100644 --- a/lib/src/widgets/picker.dart +++ b/lib/src/widgets/picker.dart @@ -27,14 +27,21 @@ class Picker extends StatefulWidget { this.headerEnabled = true, this.closeOnItemSelected = true, this.sliderValue, + this.minValue, + this.maxValue, this.customItemIndex, this.isWrapped = true, this.borderColor, this.onSliderChanged, this.matchingCriteria, - }) : assert(hintText == null || - matchingCriteria != - null); // make sure that if the search field is enabled then there is a searching criteria provided + }) : assert(hintText == null || matchingCriteria != null) { + // make sure that if the search field is enabled then there is a searching criteria provided + if (sliderValue != null && maxValue != null) { + if (sliderValue! > maxValue!) { + sliderValue = maxValue; + } + } + } final int selectedAtIndex; final List items; @@ -49,12 +56,14 @@ class Picker extends StatefulWidget { final String? hintText; final bool headerEnabled; final bool closeOnItemSelected; - final double? sliderValue; + double? sliderValue; + final double? minValue; final int? customItemIndex; final bool isWrapped; final Color? borderColor; final Function(double)? onSliderChanged; final bool Function(Item, String)? matchingCriteria; + final double? maxValue; @override _PickerState createState() => _PickerState(items, images, onItemSelected); @@ -138,7 +147,7 @@ class _PickerState extends State> { containerHeight = height * 0.75; } - final content = Column ( + final content = Column( children: [ if (widget.title?.isNotEmpty ?? false) Container( @@ -211,8 +220,9 @@ class _PickerState extends State> { fontWeight: FontWeight.w500, fontFamily: 'Lato', decoration: TextDecoration.none, - color: - Theme.of(context).extension()!.titleColor, + color: Theme.of(context) + .extension()! + .titleColor, ), ), ) @@ -491,8 +501,8 @@ class _PickerState extends State> { child: Slider( value: widget.sliderValue ?? 1, onChanged: isActivated ? widget.onSliderChanged : null, - min: 1, - max: 100, + min: widget.minValue ?? 1, + max: widget.maxValue ?? 100, divisions: 100, ), ), diff --git a/lib/src/widgets/standard_picker_list.dart b/lib/src/widgets/standard_picker_list.dart index eb1d16900..ea8b07097 100644 --- a/lib/src/widgets/standard_picker_list.dart +++ b/lib/src/widgets/standard_picker_list.dart @@ -15,6 +15,7 @@ class StandardPickerList extends StatefulWidget { required this.selectedIdx, required this.customItemIndex, required this.customValue, + this.maxValue, }) : super(key: key); final String title; @@ -26,6 +27,7 @@ class StandardPickerList extends StatefulWidget { final String value; final int selectedIdx; final double customValue; + final double? maxValue; @override _StandardPickerListState createState() => _StandardPickerListState(); @@ -59,6 +61,7 @@ class _StandardPickerListState extends State> { displayItem: adaptedDisplayItem, selectedAtIndex: selectedIdx, customItemIndex: widget.customItemIndex, + maxValue: widget.maxValue, headerEnabled: false, closeOnItemSelected: false, mainAxisAlignment: MainAxisAlignment.center, diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index 298cc3eed..2e00d1f0b 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -166,6 +166,13 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor return null; } + int? get maxCustomFeeRate { + if (wallet.type == WalletType.bitcoin) { + return bitcoin!.getMaxCustomFeeRate(wallet); + } + return null; + } + @computed int get customBitcoinFeeRate => _settingsStore.customBitcoinFeeRate; diff --git a/lib/view_model/settings/other_settings_view_model.dart b/lib/view_model/settings/other_settings_view_model.dart index cf410a1a9..0493acf81 100644 --- a/lib/view_model/settings/other_settings_view_model.dart +++ b/lib/view_model/settings/other_settings_view_model.dart @@ -140,6 +140,13 @@ abstract class OtherSettingsViewModelBase with Store { return customItem != null ? priorities.indexOf(customItem) : null; } + int? get maxCustomFeeRate { + if (_wallet.type == WalletType.bitcoin) { + return bitcoin!.getMaxCustomFeeRate(_wallet); + } + return null; + } + @action ProviderType onBuyProviderTypeSelected(ProviderType buyProviderType) => _settingsStore.defaultBuyProviders[walletType] = buyProviderType; diff --git a/lib/view_model/transaction_details_view_model.dart b/lib/view_model/transaction_details_view_model.dart index fd6d3ef6e..be2ebc545 100644 --- a/lib/view_model/transaction_details_view_model.dart +++ b/lib/view_model/transaction_details_view_model.dart @@ -348,12 +348,14 @@ abstract class TransactionDetailsViewModelBase with Store { final customItem = priorities.firstWhereOrNull( (element) => element == sendViewModel.bitcoinTransactionPriorityCustom); final customItemIndex = customItem != null ? priorities.indexOf(customItem) : null; + final maxCustomFeeRate = sendViewModel.maxCustomFeeRate?.toDouble(); RBFListItems.add(StandardPickerListItem( title: S.current.estimated_new_fee, value: bitcoin!.formatterBitcoinAmountToString(amount: newFee) + ' ${walletTypeToCryptoCurrency(wallet.type)}', items: priorityForWalletType(wallet.type), customValue: settingsStore.customBitcoinFeeRate.toDouble(), + maxValue: maxCustomFeeRate, selectedIdx: selectedItem, customItemIndex: customItemIndex ?? 0, displayItem: (dynamic priority, double sliderValue) => diff --git a/tool/configure.dart b/tool/configure.dart index d08724e42..34a39d28b 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -159,6 +159,7 @@ abstract class Bitcoin { Future isChangeSufficientForFee(Object wallet, String txId, String newFee); int getFeeAmountForPriority(Object wallet, TransactionPriority priority, int inputsCount, int outputsCount, {int? size}); int getFeeAmountWithFeeRate(Object wallet, int feeRate, int inputsCount, int outputsCount, {int? size}); + int getMaxCustomFeeRate(Object wallet); } """;