mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 22:44:29 +01:00
Make fee rate options pretty (#1554)
This commit is contained in:
@@ -2,7 +2,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -417,38 +416,37 @@ namespace BTCPayServer.Controllers
|
|||||||
},
|
},
|
||||||
CryptoCode = walletId.CryptoCode
|
CryptoCode = walletId.CryptoCode
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
var feeProvider = _feeRateProvider.CreateFeeProvider(network);
|
var feeProvider = _feeRateProvider.CreateFeeProvider(network);
|
||||||
var recommendedFees = new[] {
|
var recommendedFees =
|
||||||
TimeSpan.FromMinutes(10.0),
|
new[]
|
||||||
TimeSpan.FromMinutes(60.0),
|
{
|
||||||
TimeSpan.FromHours(6.0),
|
TimeSpan.FromMinutes(10.0), TimeSpan.FromMinutes(60.0), TimeSpan.FromHours(6.0),
|
||||||
TimeSpan.FromHours(24.0),
|
TimeSpan.FromHours(24.0),
|
||||||
|
}.Select(async time =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await feeProvider.GetFeeRateAsync(
|
||||||
|
(int)network.NBitcoinNetwork.Consensus.GetExpectedBlocksFor(time));
|
||||||
|
return new WalletSendModel.FeeRateOption() {Target = time, FeeRate = result.SatoshiPerByte};
|
||||||
}
|
}
|
||||||
.Select(time => network.NBitcoinNetwork.Consensus.GetExpectedBlocksFor(time))
|
catch (Exception)
|
||||||
.Select(blockCount => feeProvider.GetFeeRateAsync((int)blockCount))
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})
|
||||||
.ToArray();
|
.ToArray();
|
||||||
var balance = _walletProvider.GetWallet(network).GetBalance(paymentMethod.AccountDerivation);
|
var balance = _walletProvider.GetWallet(network).GetBalance(paymentMethod.AccountDerivation);
|
||||||
model.NBXSeedAvailable = await CanUseHotWallet() && !string.IsNullOrEmpty(await ExplorerClientProvider.GetExplorerClient(network)
|
model.NBXSeedAvailable = await CanUseHotWallet() && !string.IsNullOrEmpty(await ExplorerClientProvider.GetExplorerClient(network)
|
||||||
.GetMetadataAsync<string>(GetDerivationSchemeSettings(walletId).AccountDerivation,
|
.GetMetadataAsync<string>(GetDerivationSchemeSettings(walletId).AccountDerivation,
|
||||||
WellknownMetadataKeys.MasterHDKey));
|
WellknownMetadataKeys.MasterHDKey));
|
||||||
model.CurrentBalance = await balance;
|
model.CurrentBalance = await balance;
|
||||||
model.RecommendedSatoshiPerByte = new decimal?[recommendedFees.Length];
|
|
||||||
for (int i = 0; i < model.RecommendedSatoshiPerByte.Length; i++)
|
|
||||||
{
|
|
||||||
decimal? feeRate = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
feeRate = (await recommendedFees[i]).SatoshiPerByte;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
await Task.WhenAll(recommendedFees);
|
||||||
model.RecommendedSatoshiPerByte[i] = feeRate;
|
model.RecommendedSatoshiPerByte =
|
||||||
}
|
recommendedFees.Select(tuple => tuple.Result).Where(option => option != null).ToList();
|
||||||
model.FeeSatoshiPerByte = model.RecommendedSatoshiPerByte.Reverse().Where(r => r is decimal).FirstOrDefault();
|
|
||||||
|
model.FeeSatoshiPerByte = model.RecommendedSatoshiPerByte.LastOrDefault()?.FeeRate;
|
||||||
model.SupportRBF = network.SupportRBF;
|
model.SupportRBF = network.SupportRBF;
|
||||||
using (CancellationTokenSource cts = new CancellationTokenSource())
|
using (CancellationTokenSource cts = new CancellationTokenSource())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using BTCPayServer.Services.Labels;
|
using BTCPayServer.Services.Labels;
|
||||||
|
|
||||||
@@ -6,6 +7,11 @@ namespace BTCPayServer.Models.WalletViewModels
|
|||||||
{
|
{
|
||||||
public class WalletSendModel
|
public class WalletSendModel
|
||||||
{
|
{
|
||||||
|
public class FeeRateOption
|
||||||
|
{
|
||||||
|
public TimeSpan Target { get; set; }
|
||||||
|
public decimal FeeRate { get; set; }
|
||||||
|
}
|
||||||
public List<TransactionOutput> Outputs { get; set; } = new List<TransactionOutput>();
|
public List<TransactionOutput> Outputs { get; set; } = new List<TransactionOutput>();
|
||||||
|
|
||||||
public class TransactionOutput
|
public class TransactionOutput
|
||||||
@@ -26,14 +32,7 @@ namespace BTCPayServer.Models.WalletViewModels
|
|||||||
|
|
||||||
public string CryptoCode { get; set; }
|
public string CryptoCode { get; set; }
|
||||||
|
|
||||||
public string[] RecommendedSatoshiLabels = new string[]
|
public List<FeeRateOption> RecommendedSatoshiPerByte { get; set; }
|
||||||
{
|
|
||||||
"10 minutes",
|
|
||||||
"1 hour",
|
|
||||||
"6 hours",
|
|
||||||
"1 day"
|
|
||||||
};
|
|
||||||
public decimal?[] RecommendedSatoshiPerByte { get; set; }
|
|
||||||
|
|
||||||
[Display(Name = "Fee rate (satoshi per byte)")]
|
[Display(Name = "Fee rate (satoshi per byte)")]
|
||||||
[Required]
|
[Required]
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
@addTagHelper *, BundlerMinifier.TagHelpers
|
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||||
@using Microsoft.AspNetCore.Mvc.ModelBinding
|
@using Microsoft.AspNetCore.Mvc.ModelBinding
|
||||||
|
@using BTCPayServer.Views
|
||||||
@model WalletSendModel
|
@model WalletSendModel
|
||||||
@{
|
@{
|
||||||
Layout = "../Shared/_NavLayout.cshtml";
|
Layout = "../Shared/_NavLayout.cshtml";
|
||||||
@@ -25,10 +26,6 @@
|
|||||||
<input type="hidden" asp-for="Fiat" />
|
<input type="hidden" asp-for="Fiat" />
|
||||||
<input type="hidden" asp-for="Rate" />
|
<input type="hidden" asp-for="Rate" />
|
||||||
<input type="hidden" asp-for="CurrentBalance" />
|
<input type="hidden" asp-for="CurrentBalance" />
|
||||||
@for (int i = 0; i < Model.RecommendedSatoshiPerByte.Length; i++)
|
|
||||||
{
|
|
||||||
<input type="hidden" asp-for="RecommendedSatoshiPerByte[i]" />
|
|
||||||
}
|
|
||||||
<input type="hidden" asp-for="CryptoCode" />
|
<input type="hidden" asp-for="CryptoCode" />
|
||||||
<input type="hidden" name="BIP21" id="BIP21" />
|
<input type="hidden" name="BIP21" id="BIP21" />
|
||||||
<ul class="text-danger">
|
<ul class="text-danger">
|
||||||
@@ -135,15 +132,27 @@
|
|||||||
<input asp-for="FeeSatoshiPerByte" type="number" step="any" class="form-control" />
|
<input asp-for="FeeSatoshiPerByte" type="number" step="any" class="form-control" />
|
||||||
<span asp-validation-for="FeeSatoshiPerByte" class="text-danger"></span>
|
<span asp-validation-for="FeeSatoshiPerByte" class="text-danger"></span>
|
||||||
<span id="FeeRate-Error" class="text-danger"></span>
|
<span id="FeeRate-Error" class="text-danger"></span>
|
||||||
<p class="form-text text-muted crypto-info">
|
@if (Model.RecommendedSatoshiPerByte.Any())
|
||||||
Target confirmation in:
|
|
||||||
@for (int i = 0; i < Model.RecommendedSatoshiPerByte.Length; i++)
|
|
||||||
{
|
{
|
||||||
if (Model.RecommendedSatoshiPerByte[i] is null)
|
<div class="text-left mt-1" >
|
||||||
continue;
|
<span class="text-muted">
|
||||||
<span><button type="button" class="btn btn-link p-0 align-baseline crypto-fee-link" data-feerate="@Model.RecommendedSatoshiPerByte[i]">@Model.RecommendedSatoshiLabels[i]</button>.</span>
|
Confirm in the next
|
||||||
|
</span>
|
||||||
|
<div class="btn-group btn-group-toggle feerate-options" data-toggle="buttons">
|
||||||
|
|
||||||
|
@for (var index = 0; index < Model.RecommendedSatoshiPerByte.Count; index++)
|
||||||
|
{
|
||||||
|
var feeRateOption = Model.RecommendedSatoshiPerByte[index];
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-primary crypto-fee-link" value="@feeRateOption.FeeRate" data-toggle="tooltip" title="@feeRateOption.FeeRate sat/b">
|
||||||
|
<input type="hidden" asp-for="RecommendedSatoshiPerByte[index].Target" />
|
||||||
|
<input type="hidden" asp-for="RecommendedSatoshiPerByte[index].FeeRate" />
|
||||||
|
@feeRateOption.Target.TimeString()
|
||||||
|
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
@if (Model.Outputs.Count == 1)
|
@if (Model.Outputs.Count == 1)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,15 +24,24 @@ function updateFiatValueWithCurrentElement() {
|
|||||||
updateFiatValue($(this))
|
updateFiatValue($(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function selectCorrectFeeOption(){
|
||||||
|
var val = $("#FeeSatoshiPerByte").val();
|
||||||
|
$(".feerate-options").children(".crypto-fee-link").removeClass("active");
|
||||||
|
$(".feerate-options").find("[value='"+val+"']").first().addClass("active");
|
||||||
|
}
|
||||||
|
|
||||||
$(function () {
|
$(function () {
|
||||||
$(".output-amount").on("input", updateFiatValueWithCurrentElement).each(updateFiatValueWithCurrentElement);
|
$(".output-amount").on("input", updateFiatValueWithCurrentElement).each(updateFiatValueWithCurrentElement);
|
||||||
|
|
||||||
$(".crypto-fee-link").on("click", function (elem) {
|
$(".crypto-fee-link").on("click", function (elem) {
|
||||||
var val = $(this).attr("data-feerate").valueOf();
|
$(this).parent().children().removeClass("active");
|
||||||
|
var val = $(this).addClass("active").val();
|
||||||
$("#FeeSatoshiPerByte").val(val);
|
$("#FeeSatoshiPerByte").val(val);
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
$("#FeeSatoshiPerByte").on("change input", selectCorrectFeeOption);
|
||||||
|
|
||||||
|
selectCorrectFeeOption();
|
||||||
$(".crypto-balance-link").on("click", function (elem) {
|
$(".crypto-balance-link").on("click", function (elem) {
|
||||||
var val = $(this).text();
|
var val = $(this).text();
|
||||||
var parentContainer = $(this).parents(".form-group");
|
var parentContainer = $(this).parents(".form-group");
|
||||||
|
|||||||
Reference in New Issue
Block a user