mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-23 22:46:49 +01:00
* Fix labels not showing multiple payouts payments * Improve label tooltip Makes it more readable * Cleanup code * Fix overflowing text in order ID field in invoices table (#3765) close #3714 Co-authored-by: Dennis Reimann <mail@dennisreimann.de> Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com> Co-authored-by: Umar Bolatov <bolatovumar@gmail.com>
187 lines
7.5 KiB
C#
187 lines
7.5 KiB
C#
#nullable enable
|
|
using System;
|
|
using System.Drawing;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.AspNetCore.Routing;
|
|
using BTCPayServer.Data;
|
|
using BTCPayServer.Client.Models;
|
|
using BTCPayServer.Abstractions.Extensions;
|
|
|
|
namespace BTCPayServer.Services.Labels
|
|
{
|
|
public class LabelFactory
|
|
{
|
|
private readonly LinkGenerator _linkGenerator;
|
|
private readonly WalletRepository _walletRepository;
|
|
|
|
public LabelFactory(
|
|
LinkGenerator linkGenerator,
|
|
WalletRepository walletRepository
|
|
)
|
|
{
|
|
_linkGenerator = linkGenerator;
|
|
_walletRepository = walletRepository;
|
|
}
|
|
|
|
public IEnumerable<ColoredLabel> ColorizeTransactionLabels(WalletBlobInfo walletBlobInfo, WalletTransactionInfo transactionInfo,
|
|
HttpRequest request)
|
|
{
|
|
foreach (var label in transactionInfo.Labels)
|
|
{
|
|
walletBlobInfo.LabelColors.TryGetValue(label.Value.Text, out var color);
|
|
yield return CreateLabel(label.Value, color, request);
|
|
}
|
|
}
|
|
|
|
public IEnumerable<ColoredLabel> GetWalletColoredLabels(WalletBlobInfo walletBlobInfo, HttpRequest request)
|
|
{
|
|
foreach (var kv in walletBlobInfo.LabelColors)
|
|
{
|
|
yield return CreateLabel(new RawLabel() { Text = kv.Key }, kv.Value, request);
|
|
}
|
|
}
|
|
|
|
const string DefaultColor = "#000";
|
|
private ColoredLabel CreateLabel(LabelData uncoloredLabel, string? color, HttpRequest request)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(uncoloredLabel);
|
|
color ??= DefaultColor;
|
|
|
|
ColoredLabel coloredLabel = new ColoredLabel
|
|
{
|
|
Text = uncoloredLabel.Text,
|
|
Color = color,
|
|
TextColor = TextColor(color)
|
|
};
|
|
|
|
string PayoutLabelText(KeyValuePair<string, List<string>> pair)
|
|
{
|
|
if (pair.Value.Count == 1)
|
|
return $"Paid a payout of a pull payment ({pair.Key})";
|
|
else
|
|
return $"Paid payouts of a pull payment ({pair.Key})";
|
|
}
|
|
|
|
if (uncoloredLabel is ReferenceLabel refLabel)
|
|
{
|
|
var refInLabel = string.IsNullOrEmpty(refLabel.Reference) ? string.Empty : $"({refLabel.Reference})";
|
|
switch (uncoloredLabel.Type)
|
|
{
|
|
case "invoice":
|
|
coloredLabel.Tooltip = $"Received through an invoice {refInLabel}";
|
|
coloredLabel.Link = string.IsNullOrEmpty(refLabel.Reference)
|
|
? null
|
|
: _linkGenerator.InvoiceLink(refLabel.Reference, request.Scheme, request.Host, request.PathBase);
|
|
break;
|
|
case "payment-request":
|
|
coloredLabel.Tooltip = $"Received through a payment request {refInLabel}";
|
|
coloredLabel.Link = string.IsNullOrEmpty(refLabel.Reference)
|
|
? null
|
|
: _linkGenerator.PaymentRequestLink(refLabel.Reference, request.Scheme, request.Host, request.PathBase);
|
|
break;
|
|
case "app":
|
|
coloredLabel.Tooltip = $"Received through an app {refInLabel}";
|
|
coloredLabel.Link = string.IsNullOrEmpty(refLabel.Reference)
|
|
? null
|
|
: _linkGenerator.AppLink(refLabel.Reference, request.Scheme, request.Host, request.PathBase);
|
|
break;
|
|
case "pj-exposed":
|
|
coloredLabel.Tooltip = $"This UTXO was exposed through a PayJoin proposal for an invoice {refInLabel}";
|
|
coloredLabel.Link = string.IsNullOrEmpty(refLabel.Reference)
|
|
? null
|
|
: _linkGenerator.InvoiceLink(refLabel.Reference, request.Scheme, request.Host, request.PathBase);
|
|
break;
|
|
}
|
|
}
|
|
else if (uncoloredLabel is PayoutLabel payoutLabel)
|
|
{
|
|
coloredLabel.Tooltip = payoutLabel.PullPaymentPayouts.Count > 1
|
|
? $"<ul>{string.Join(string.Empty, payoutLabel.PullPaymentPayouts.Select(pair => $"<li>{PayoutLabelText(pair)}</li>"))}</ul>"
|
|
: payoutLabel.PullPaymentPayouts.Select(PayoutLabelText).ToString();
|
|
|
|
coloredLabel.Link = string.IsNullOrEmpty(payoutLabel.WalletId)
|
|
? null
|
|
: _linkGenerator.PayoutLink(payoutLabel.WalletId, null, PayoutState.Completed, request.Scheme, request.Host,
|
|
request.PathBase);
|
|
|
|
}
|
|
return coloredLabel;
|
|
}
|
|
|
|
// Borrowed from https://github.com/ManageIQ/guides/blob/master/labels.md
|
|
readonly string[] LabelColorScheme =
|
|
{
|
|
"#fbca04",
|
|
"#0e8a16",
|
|
"#ff7619",
|
|
"#84b6eb",
|
|
"#5319e7",
|
|
"#cdcdcd",
|
|
"#cc317c",
|
|
};
|
|
|
|
readonly int MaxLabelSize = 20;
|
|
|
|
async public Task<RawLabel> BuildLabel(
|
|
WalletBlobInfo walletBlobInfo,
|
|
HttpRequest request,
|
|
WalletTransactionInfo walletTransactionInfo,
|
|
WalletId walletId,
|
|
string transactionId,
|
|
string label
|
|
)
|
|
{
|
|
label = label.Trim().TrimStart('{').ToLowerInvariant().Replace(',', ' ').Truncate(MaxLabelSize);
|
|
var labels = GetWalletColoredLabels(walletBlobInfo, request);
|
|
|
|
if (!labels.Any(l => l.Text.Equals(label, StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
var chosenColor = ChooseBackgroundColor(walletBlobInfo, request);
|
|
walletBlobInfo.LabelColors.Add(label, chosenColor);
|
|
await _walletRepository.SetWalletInfo(walletId, walletBlobInfo);
|
|
}
|
|
|
|
return new RawLabel(label);
|
|
}
|
|
|
|
private string ChooseBackgroundColor(
|
|
WalletBlobInfo walletBlobInfo,
|
|
HttpRequest request
|
|
)
|
|
{
|
|
var labels = GetWalletColoredLabels(walletBlobInfo, request);
|
|
|
|
List<string> allColors = new List<string>();
|
|
allColors.AddRange(LabelColorScheme);
|
|
allColors.AddRange(labels.Select(l => l.Color));
|
|
var chosenColor =
|
|
allColors
|
|
.GroupBy(k => k)
|
|
.OrderBy(k => k.Count())
|
|
.ThenBy(k =>
|
|
{
|
|
var indexInColorScheme = Array.IndexOf(LabelColorScheme, k.Key);
|
|
|
|
// Ensures that any label color which may not be in our label color scheme is given the least priority
|
|
return indexInColorScheme == -1 ? double.PositiveInfinity : indexInColorScheme;
|
|
})
|
|
.First().Key;
|
|
|
|
return chosenColor;
|
|
}
|
|
|
|
private string TextColor(string bgColor)
|
|
{
|
|
int nThreshold = 105;
|
|
var bg = ColorTranslator.FromHtml(bgColor);
|
|
int bgDelta = Convert.ToInt32((bg.R * 0.299) + (bg.G * 0.587) + (bg.B * 0.114));
|
|
Color color = (255 - bgDelta < nThreshold) ? Color.Black : Color.White;
|
|
return ColorTranslator.ToHtml(color);
|
|
}
|
|
}
|
|
}
|