Make fee rate options pretty (#1554)

This commit is contained in:
Andrew Camilleri
2020-05-07 22:34:39 +02:00
committed by GitHub
parent ba02372d13
commit aff91f49ef
4 changed files with 66 additions and 51 deletions

View File

@@ -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 recommendedFees =
var feeProvider = _feeRateProvider.CreateFeeProvider(network); new[]
var recommendedFees = new[] { {
TimeSpan.FromMinutes(10.0), TimeSpan.FromMinutes(10.0), TimeSpan.FromMinutes(60.0), TimeSpan.FromHours(6.0),
TimeSpan.FromMinutes(60.0), TimeSpan.FromHours(24.0),
TimeSpan.FromHours(6.0), }.Select(async time =>
TimeSpan.FromHours(24.0), {
} try
.Select(time => network.NBitcoinNetwork.Consensus.GetExpectedBlocksFor(time)) {
.Select(blockCount => feeProvider.GetFeeRateAsync((int)blockCount)) var result = await feeProvider.GetFeeRateAsync(
.ToArray(); (int)network.NBitcoinNetwork.Consensus.GetExpectedBlocksFor(time));
return new WalletSendModel.FeeRateOption() {Target = time, FeeRate = result.SatoshiPerByte};
}
catch (Exception)
{
return null;
}
})
.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++) await Task.WhenAll(recommendedFees);
{ model.RecommendedSatoshiPerByte =
decimal? feeRate = null; recommendedFees.Select(tuple => tuple.Result).Where(option => option != null).ToList();
try
{
feeRate = (await recommendedFees[i]).SatoshiPerByte;
}
catch
{
} model.FeeSatoshiPerByte = model.RecommendedSatoshiPerByte.LastOrDefault()?.FeeRate;
model.RecommendedSatoshiPerByte[i] = feeRate;
}
model.FeeSatoshiPerByte = model.RecommendedSatoshiPerByte.Reverse().Where(r => r is decimal).FirstOrDefault();
model.SupportRBF = network.SupportRBF; model.SupportRBF = network.SupportRBF;
using (CancellationTokenSource cts = new CancellationTokenSource()) using (CancellationTokenSource cts = new CancellationTokenSource())
{ {

View File

@@ -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
@@ -25,15 +31,8 @@ namespace BTCPayServer.Models.WalletViewModels
public decimal CurrentBalance { get; set; } public decimal CurrentBalance { get; set; }
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]

View File

@@ -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++) <div class="text-left mt-1" >
{ <span class="text-muted">
if (Model.RecommendedSatoshiPerByte[i] is null) Confirm in the next
continue; </span>
<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> <div class="btn-group btn-group-toggle feerate-options" data-toggle="buttons">
}
</p> @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>
}
</div> </div>
@if (Model.Outputs.Count == 1) @if (Model.Outputs.Count == 1)
{ {

View File

@@ -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");