mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 22:14:26 +01:00
@@ -26,11 +26,4 @@ namespace BTCPayServer.Data
|
|||||||
.WithMany(w => w.WalletTransactions).OnDelete(DeleteBehavior.Cascade);
|
.WithMany(w => w.WalletTransactions).OnDelete(DeleteBehavior.Cascade);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class WalletTransactionInfo
|
|
||||||
{
|
|
||||||
public string Comment { get; set; } = string.Empty;
|
|
||||||
[JsonIgnore]
|
|
||||||
public HashSet<string> Labels { get; set; } = new HashSet<string>();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ using BTCPayServer.Security.Bitpay;
|
|||||||
using BTCPayServer.Services;
|
using BTCPayServer.Services;
|
||||||
using BTCPayServer.Services.Apps;
|
using BTCPayServer.Services.Apps;
|
||||||
using BTCPayServer.Services.Invoices;
|
using BTCPayServer.Services.Invoices;
|
||||||
|
using BTCPayServer.Services.Labels;
|
||||||
using BTCPayServer.Services.Mails;
|
using BTCPayServer.Services.Mails;
|
||||||
using BTCPayServer.Services.Rates;
|
using BTCPayServer.Services.Rates;
|
||||||
using BTCPayServer.Tests.Logging;
|
using BTCPayServer.Tests.Logging;
|
||||||
@@ -585,6 +586,95 @@ namespace BTCPayServer.Tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Fast", "Fast")]
|
||||||
|
public void CanParseLegacyLabels()
|
||||||
|
{
|
||||||
|
static void AssertContainsRawLabel(WalletTransactionInfo info)
|
||||||
|
{
|
||||||
|
foreach (var item in new[] { "blah", "lol", "hello" })
|
||||||
|
{
|
||||||
|
Assert.True(info.Labels.ContainsKey(item));
|
||||||
|
var rawLabel = Assert.IsType<RawLabel>(info.Labels[item]);
|
||||||
|
Assert.Equal("raw", rawLabel.Type);
|
||||||
|
Assert.Equal(item, rawLabel.Text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var data = new WalletTransactionData();
|
||||||
|
data.Labels = "blah,lol,hello,lol";
|
||||||
|
var info = data.GetBlobInfo();
|
||||||
|
Assert.Equal(3, info.Labels.Count);
|
||||||
|
AssertContainsRawLabel(info);
|
||||||
|
data.SetBlobInfo(info);
|
||||||
|
Assert.Contains("raw", data.Labels);
|
||||||
|
Assert.Contains("{", data.Labels);
|
||||||
|
Assert.Contains("[", data.Labels);
|
||||||
|
info = data.GetBlobInfo();
|
||||||
|
AssertContainsRawLabel(info);
|
||||||
|
|
||||||
|
|
||||||
|
data = new WalletTransactionData()
|
||||||
|
{
|
||||||
|
Labels = "pos",
|
||||||
|
Blob = Encoders.Hex.DecodeData("1f8b08000000000000037abf7b7fb592737e6e6e6a5e89929592522d000000ffff030036bc6ad911000000")
|
||||||
|
};
|
||||||
|
info = data.GetBlobInfo();
|
||||||
|
var label = Assert.Single(info.Labels);
|
||||||
|
Assert.Equal("raw", label.Value.Type);
|
||||||
|
Assert.Equal("pos", label.Value.Text);
|
||||||
|
Assert.Equal("pos", label.Key);
|
||||||
|
|
||||||
|
|
||||||
|
static void AssertContainsLabel(WalletTransactionInfo info)
|
||||||
|
{
|
||||||
|
Assert.Equal(2, info.Labels.Count);
|
||||||
|
var invoiceLabel = Assert.IsType<ReferenceLabel>(info.Labels["invoice"]);
|
||||||
|
Assert.Equal("BFm1MCJPBCDeRoWXvPcwnM", invoiceLabel.Reference);
|
||||||
|
Assert.Equal("invoice", invoiceLabel.Text);
|
||||||
|
Assert.Equal("invoice", invoiceLabel.Type);
|
||||||
|
|
||||||
|
var appLabel = Assert.IsType<ReferenceLabel>(info.Labels["app"]);
|
||||||
|
Assert.Equal("87kj5yKay8mB4UUZcJhZH5TqDKMD3CznjwLjiu1oYZXe", appLabel.Reference);
|
||||||
|
Assert.Equal("app", appLabel.Text);
|
||||||
|
Assert.Equal("app", appLabel.Type);
|
||||||
|
}
|
||||||
|
data = new WalletTransactionData()
|
||||||
|
{
|
||||||
|
Labels = "[\"{\\n \\\"value\\\": \\\"invoice\\\",\\n \\\"id\\\": \\\"BFm1MCJPBCDeRoWXvPcwnM\\\"\\n}\",\"{\\n \\\"value\\\": \\\"app\\\",\\n \\\"id\\\": \\\"87kj5yKay8mB4UUZcJhZH5TqDKMD3CznjwLjiu1oYZXe\\\"\\n}\"]",
|
||||||
|
};
|
||||||
|
info = data.GetBlobInfo();
|
||||||
|
AssertContainsLabel(info);
|
||||||
|
data.SetBlobInfo(info);
|
||||||
|
info = data.GetBlobInfo();
|
||||||
|
AssertContainsLabel(info);
|
||||||
|
|
||||||
|
static void AssertPayoutLabel(WalletTransactionInfo info)
|
||||||
|
{
|
||||||
|
Assert.Single(info.Labels);
|
||||||
|
var l = Assert.IsType<PayoutLabel>(info.Labels["payout"]);
|
||||||
|
Assert.Equal("pullPaymentId", l.PullPaymentId);
|
||||||
|
Assert.Equal("walletId", l.WalletId);
|
||||||
|
Assert.Equal("payoutId", l.PayoutId);
|
||||||
|
}
|
||||||
|
|
||||||
|
var payoutId = "payoutId";
|
||||||
|
var pullPaymentId = "pullPaymentId";
|
||||||
|
var walletId = "walletId";
|
||||||
|
// How it was serialized before
|
||||||
|
|
||||||
|
data = new WalletTransactionData()
|
||||||
|
{
|
||||||
|
Labels = new JArray(JObject.FromObject(new { value = "payout", id = payoutId, pullPaymentId, walletId })).ToString()
|
||||||
|
};
|
||||||
|
info = data.GetBlobInfo();
|
||||||
|
AssertPayoutLabel(info);
|
||||||
|
data.SetBlobInfo(info);
|
||||||
|
info = data.GetBlobInfo();
|
||||||
|
AssertPayoutLabel(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[Trait("Fast", "Fast")]
|
[Trait("Fast", "Fast")]
|
||||||
public void DeterministicUTXOSorter()
|
public void DeterministicUTXOSorter()
|
||||||
@@ -1234,8 +1324,8 @@ namespace BTCPayServer.Tests
|
|||||||
tx = Assert.Single(transactions.Transactions);
|
tx = Assert.Single(transactions.Transactions);
|
||||||
|
|
||||||
Assert.Equal("hello", tx.Comment);
|
Assert.Equal("hello", tx.Comment);
|
||||||
Assert.Contains("test", tx.Labels.Select(l => l.Value));
|
Assert.Contains("test", tx.Labels.Select(l => l.Text));
|
||||||
Assert.Contains("test2", tx.Labels.Select(l => l.Value));
|
Assert.Contains("test2", tx.Labels.Select(l => l.Text));
|
||||||
Assert.Equal(2, tx.Labels.GroupBy(l => l.Color).Count());
|
Assert.Equal(2, tx.Labels.GroupBy(l => l.Color).Count());
|
||||||
|
|
||||||
Assert.IsType<RedirectToActionResult>(
|
Assert.IsType<RedirectToActionResult>(
|
||||||
@@ -1246,8 +1336,8 @@ namespace BTCPayServer.Tests
|
|||||||
tx = Assert.Single(transactions.Transactions);
|
tx = Assert.Single(transactions.Transactions);
|
||||||
|
|
||||||
Assert.Equal("hello", tx.Comment);
|
Assert.Equal("hello", tx.Comment);
|
||||||
Assert.Contains("test", tx.Labels.Select(l => l.Value));
|
Assert.Contains("test", tx.Labels.Select(l => l.Text));
|
||||||
Assert.DoesNotContain("test2", tx.Labels.Select(l => l.Value));
|
Assert.DoesNotContain("test2", tx.Labels.Select(l => l.Text));
|
||||||
Assert.Single(tx.Labels.GroupBy(l => l.Color));
|
Assert.Single(tx.Labels.GroupBy(l => l.Color));
|
||||||
|
|
||||||
var walletInfo = await tester.PayTester.GetService<WalletRepository>().GetWalletInfo(walletId);
|
var walletInfo = await tester.PayTester.GetService<WalletRepository>().GetWalletInfo(walletId);
|
||||||
|
|||||||
@@ -159,12 +159,12 @@ namespace BTCPayServer.Controllers
|
|||||||
if (addlabel != null)
|
if (addlabel != null)
|
||||||
{
|
{
|
||||||
addlabel = addlabel.Trim().TrimStart('{').ToLowerInvariant().Replace(',', ' ').Truncate(MaxLabelSize);
|
addlabel = addlabel.Trim().TrimStart('{').ToLowerInvariant().Replace(',', ' ').Truncate(MaxLabelSize);
|
||||||
var labels = _labelFactory.GetLabels(walletBlobInfo, Request);
|
var labels = _labelFactory.GetWalletColoredLabels(walletBlobInfo, Request);
|
||||||
if (!walletTransactionsInfo.TryGetValue(transactionId, out var walletTransactionInfo))
|
if (!walletTransactionsInfo.TryGetValue(transactionId, out var walletTransactionInfo))
|
||||||
{
|
{
|
||||||
walletTransactionInfo = new WalletTransactionInfo();
|
walletTransactionInfo = new WalletTransactionInfo();
|
||||||
}
|
}
|
||||||
if (!labels.Any(l => l.Value.Equals(addlabel, StringComparison.OrdinalIgnoreCase)))
|
if (!labels.Any(l => l.Text.Equals(addlabel, StringComparison.OrdinalIgnoreCase)))
|
||||||
{
|
{
|
||||||
List<string> allColors = new List<string>();
|
List<string> allColors = new List<string>();
|
||||||
allColors.AddRange(LabelColorScheme);
|
allColors.AddRange(LabelColorScheme);
|
||||||
@@ -183,7 +183,8 @@ namespace BTCPayServer.Controllers
|
|||||||
walletBlobInfo.LabelColors.Add(addlabel, chosenColor);
|
walletBlobInfo.LabelColors.Add(addlabel, chosenColor);
|
||||||
await WalletRepository.SetWalletInfo(walletId, walletBlobInfo);
|
await WalletRepository.SetWalletInfo(walletId, walletBlobInfo);
|
||||||
}
|
}
|
||||||
if (walletTransactionInfo.Labels.Add(addlabel))
|
var rawLabel = new RawLabel(addlabel);
|
||||||
|
if (walletTransactionInfo.Labels.TryAdd(rawLabel.Text, rawLabel))
|
||||||
{
|
{
|
||||||
await WalletRepository.SetWalletTransactionInfo(walletId, transactionId, walletTransactionInfo);
|
await WalletRepository.SetWalletTransactionInfo(walletId, transactionId, walletTransactionInfo);
|
||||||
}
|
}
|
||||||
@@ -195,8 +196,8 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
if (walletTransactionInfo.Labels.Remove(removelabel))
|
if (walletTransactionInfo.Labels.Remove(removelabel))
|
||||||
{
|
{
|
||||||
var canDelete = !walletTransactionsInfo.SelectMany(txi => txi.Value.Labels).Any(l => l == removelabel);
|
var canDeleteColor = !walletTransactionsInfo.Any(txi => txi.Value.Labels.ContainsKey(removelabel));
|
||||||
if (canDelete)
|
if (canDeleteColor)
|
||||||
{
|
{
|
||||||
walletBlobInfo.LabelColors.Remove(removelabel);
|
walletBlobInfo.LabelColors.Remove(removelabel);
|
||||||
await WalletRepository.SetWalletInfo(walletId, walletBlobInfo);
|
await WalletRepository.SetWalletInfo(walletId, walletBlobInfo);
|
||||||
@@ -315,14 +316,14 @@ namespace BTCPayServer.Controllers
|
|||||||
|
|
||||||
if (walletTransactionsInfo.TryGetValue(tx.TransactionId.ToString(), out var transactionInfo))
|
if (walletTransactionsInfo.TryGetValue(tx.TransactionId.ToString(), out var transactionInfo))
|
||||||
{
|
{
|
||||||
var labels = _labelFactory.GetLabels(walletBlob, transactionInfo, Request);
|
var labels = _labelFactory.ColorizeTransactionLabels(walletBlob, transactionInfo, Request);
|
||||||
vm.Labels.AddRange(labels);
|
vm.Labels.AddRange(labels);
|
||||||
model.Labels.AddRange(labels);
|
model.Labels.AddRange(labels);
|
||||||
vm.Comment = transactionInfo.Comment;
|
vm.Comment = transactionInfo.Comment;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (labelFilter == null ||
|
if (labelFilter == null ||
|
||||||
vm.Labels.Any(l => l.Value.Equals(labelFilter, StringComparison.OrdinalIgnoreCase)))
|
vm.Labels.Any(l => l.Text.Equals(labelFilter, StringComparison.OrdinalIgnoreCase)))
|
||||||
model.Transactions.Add(vm);
|
model.Transactions.Add(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,7 +548,7 @@ namespace BTCPayServer.Controllers
|
|||||||
Outpoint = coin.OutPoint.ToString(),
|
Outpoint = coin.OutPoint.ToString(),
|
||||||
Amount = coin.Value.GetValue(network),
|
Amount = coin.Value.GetValue(network),
|
||||||
Comment = info?.Comment,
|
Comment = info?.Comment,
|
||||||
Labels = info == null ? null : _labelFactory.GetLabels(walletBlobAsync, info, Request),
|
Labels = info == null ? null : _labelFactory.ColorizeTransactionLabels(walletBlobAsync, info, Request),
|
||||||
Link = string.Format(CultureInfo.InvariantCulture, network.BlockExplorerLink, coin.OutPoint.Hash.ToString())
|
Link = string.Format(CultureInfo.InvariantCulture, network.BlockExplorerLink, coin.OutPoint.Hash.ToString())
|
||||||
};
|
};
|
||||||
}).ToArray();
|
}).ToArray();
|
||||||
|
|||||||
@@ -1,32 +1,57 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using BTCPayServer.Services.Labels;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
||||||
namespace BTCPayServer.Data
|
namespace BTCPayServer.Data
|
||||||
{
|
{
|
||||||
|
public class WalletTransactionInfo
|
||||||
|
{
|
||||||
|
public string Comment { get; set; } = string.Empty;
|
||||||
|
[JsonIgnore]
|
||||||
|
public Dictionary<string, Label> Labels { get; set; } = new Dictionary<string, Label>();
|
||||||
|
}
|
||||||
public static class WalletTransactionDataExtensions
|
public static class WalletTransactionDataExtensions
|
||||||
{
|
{
|
||||||
public static WalletTransactionInfo GetBlobInfo(this WalletTransactionData walletTransactionData)
|
public static WalletTransactionInfo GetBlobInfo(this WalletTransactionData walletTransactionData)
|
||||||
{
|
{
|
||||||
|
WalletTransactionInfo blobInfo;
|
||||||
if (walletTransactionData.Blob == null || walletTransactionData.Blob.Length == 0)
|
if (walletTransactionData.Blob == null || walletTransactionData.Blob.Length == 0)
|
||||||
{
|
blobInfo = new WalletTransactionInfo();
|
||||||
return new WalletTransactionInfo();
|
else
|
||||||
}
|
blobInfo = JsonConvert.DeserializeObject<WalletTransactionInfo>(ZipUtils.Unzip(walletTransactionData.Blob));
|
||||||
var blobInfo = JsonConvert.DeserializeObject<WalletTransactionInfo>(ZipUtils.Unzip(walletTransactionData.Blob));
|
|
||||||
if (!string.IsNullOrEmpty(walletTransactionData.Labels))
|
if (!string.IsNullOrEmpty(walletTransactionData.Labels))
|
||||||
{
|
{
|
||||||
if (walletTransactionData.Labels.StartsWith('['))
|
if (walletTransactionData.Labels.StartsWith('['))
|
||||||
{
|
{
|
||||||
blobInfo.Labels.AddRange(JArray.Parse(walletTransactionData.Labels).Values<string>());
|
foreach (var jtoken in JArray.Parse(walletTransactionData.Labels))
|
||||||
|
{
|
||||||
|
var l = jtoken.Type == JTokenType.String ? Label.Parse(jtoken.Value<string>())
|
||||||
|
: Label.Parse(jtoken.ToString());
|
||||||
|
blobInfo.Labels.TryAdd(l.Text, l);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
blobInfo.Labels.AddRange(walletTransactionData.Labels.Split(',',
|
// Legacy path
|
||||||
StringSplitOptions.RemoveEmptyEntries));
|
foreach (var token in walletTransactionData.Labels.Split(',',
|
||||||
|
StringSplitOptions.RemoveEmptyEntries))
|
||||||
|
{
|
||||||
|
var l = Label.Parse(token);
|
||||||
|
blobInfo.Labels.TryAdd(l.Text, l);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return blobInfo;
|
return blobInfo;
|
||||||
}
|
}
|
||||||
|
static JsonSerializerSettings LabelSerializerSettings = new JsonSerializerSettings()
|
||||||
|
{
|
||||||
|
ContractResolver = new CamelCasePropertyNamesContractResolver(),
|
||||||
|
Formatting = Formatting.None
|
||||||
|
};
|
||||||
public static void SetBlobInfo(this WalletTransactionData walletTransactionData, WalletTransactionInfo blobInfo)
|
public static void SetBlobInfo(this WalletTransactionData walletTransactionData, WalletTransactionInfo blobInfo)
|
||||||
{
|
{
|
||||||
if (blobInfo == null)
|
if (blobInfo == null)
|
||||||
@@ -35,8 +60,11 @@ namespace BTCPayServer.Data
|
|||||||
walletTransactionData.Blob = Array.Empty<byte>();
|
walletTransactionData.Blob = Array.Empty<byte>();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
walletTransactionData.Labels = new JArray(
|
||||||
walletTransactionData.Labels = JArray.FromObject(blobInfo.Labels).ToString();
|
blobInfo.Labels.Select(l => JsonConvert.SerializeObject(l.Value, LabelSerializerSettings))
|
||||||
|
.Select(l => JObject.Parse(l))
|
||||||
|
.OfType<JToken>()
|
||||||
|
.ToArray()).ToString();
|
||||||
walletTransactionData.Blob = ZipUtils.Zip(JsonConvert.SerializeObject(blobInfo));
|
walletTransactionData.Blob = ZipUtils.Zip(JsonConvert.SerializeObject(blobInfo));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using BTCPayServer.Payments;
|
|||||||
using BTCPayServer.Payments.Bitcoin;
|
using BTCPayServer.Payments.Bitcoin;
|
||||||
using BTCPayServer.Services;
|
using BTCPayServer.Services;
|
||||||
using BTCPayServer.Services.Apps;
|
using BTCPayServer.Services.Apps;
|
||||||
|
using BTCPayServer.Services.Labels;
|
||||||
using BTCPayServer.Services.PaymentRequests;
|
using BTCPayServer.Services.PaymentRequests;
|
||||||
using NBitcoin;
|
using NBitcoin;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
@@ -40,7 +41,7 @@ namespace BTCPayServer.HostedServices
|
|||||||
{
|
{
|
||||||
var walletId = new WalletId(invoiceEvent.Invoice.StoreId, invoiceEvent.Payment.GetCryptoCode());
|
var walletId = new WalletId(invoiceEvent.Invoice.StoreId, invoiceEvent.Payment.GetCryptoCode());
|
||||||
var transactionId = bitcoinLikePaymentData.Outpoint.Hash;
|
var transactionId = bitcoinLikePaymentData.Outpoint.Hash;
|
||||||
var labels = new List<(string color, string label)>
|
var labels = new List<(string color, Label label)>
|
||||||
{
|
{
|
||||||
UpdateTransactionLabel.InvoiceLabelTemplate(invoiceEvent.Invoice.Id)
|
UpdateTransactionLabel.InvoiceLabelTemplate(invoiceEvent.Invoice.Id)
|
||||||
};
|
};
|
||||||
@@ -79,14 +80,14 @@ namespace BTCPayServer.HostedServices
|
|||||||
|
|
||||||
foreach (var label in pair.Value)
|
foreach (var label in pair.Value)
|
||||||
{
|
{
|
||||||
walletBlobInfo.LabelColors.TryAdd(label.label, label.color);
|
walletBlobInfo.LabelColors.TryAdd(label.label.Text, label.color);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _walletRepository.SetWalletInfo(updateTransactionLabel.WalletId, walletBlobInfo);
|
await _walletRepository.SetWalletInfo(updateTransactionLabel.WalletId, walletBlobInfo);
|
||||||
var update = false;
|
var update = false;
|
||||||
foreach (var label in pair.Value)
|
foreach (var label in pair.Value)
|
||||||
{
|
{
|
||||||
if (walletTransactionInfo.Labels.Add(label.label))
|
if (walletTransactionInfo.Labels.TryAdd(label.label.Text, label.label))
|
||||||
{
|
{
|
||||||
update = true;
|
update = true;
|
||||||
}
|
}
|
||||||
@@ -108,47 +109,52 @@ namespace BTCPayServer.HostedServices
|
|||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
public UpdateTransactionLabel(WalletId walletId, uint256 txId, (string color, string label) colorLabel)
|
public UpdateTransactionLabel(WalletId walletId, uint256 txId, (string color, Label label) colorLabel)
|
||||||
{
|
{
|
||||||
WalletId = walletId;
|
WalletId = walletId;
|
||||||
TransactionLabels = new Dictionary<uint256, List<(string color, string label)>>();
|
TransactionLabels = new Dictionary<uint256, List<(string color, Label label)>>();
|
||||||
TransactionLabels.Add(txId, new List<(string color, string label)>() { colorLabel });
|
TransactionLabels.Add(txId, new List<(string color, Label label)>() { colorLabel });
|
||||||
}
|
}
|
||||||
public UpdateTransactionLabel(WalletId walletId, uint256 txId, List<(string color, string label)> colorLabels)
|
public UpdateTransactionLabel(WalletId walletId, uint256 txId, List<(string color, Label label)> colorLabels)
|
||||||
{
|
{
|
||||||
WalletId = walletId;
|
WalletId = walletId;
|
||||||
TransactionLabels = new Dictionary<uint256, List<(string color, string label)>>();
|
TransactionLabels = new Dictionary<uint256, List<(string color, Label label)>>();
|
||||||
TransactionLabels.Add(txId, colorLabels);
|
TransactionLabels.Add(txId, colorLabels);
|
||||||
}
|
}
|
||||||
public static (string color, string label) PayjoinLabelTemplate()
|
public static (string color, Label label) PayjoinLabelTemplate()
|
||||||
{
|
{
|
||||||
return ("#51b13e", "payjoin");
|
return ("#51b13e", new RawLabel("payjoin"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static (string color, string label) InvoiceLabelTemplate(string invoice)
|
public static (string color, Label label) InvoiceLabelTemplate(string invoice)
|
||||||
{
|
{
|
||||||
return ("#cedc21", JObject.FromObject(new { value = "invoice", id = invoice }).ToString());
|
return ("#cedc21", new ReferenceLabel("invoice", invoice));
|
||||||
}
|
}
|
||||||
public static (string color, string label) PaymentRequestLabelTemplate(string paymentRequestId)
|
public static (string color, Label label) PaymentRequestLabelTemplate(string paymentRequestId)
|
||||||
{
|
{
|
||||||
return ("#489D77", JObject.FromObject(new { value = "payment-request", id = paymentRequestId }).ToString());
|
return ("#489D77", new ReferenceLabel("payment-request", paymentRequestId));
|
||||||
}
|
}
|
||||||
public static (string color, string label) AppLabelTemplate(string appId)
|
public static (string color, Label label) AppLabelTemplate(string appId)
|
||||||
{
|
{
|
||||||
return ("#5093B6", JObject.FromObject(new { value = "app", id = appId }).ToString());
|
return ("#5093B6", new ReferenceLabel("app", appId));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static (string color, string label) PayjoinExposedLabelTemplate(string invoice)
|
public static (string color, Label label) PayjoinExposedLabelTemplate(string invoice)
|
||||||
{
|
{
|
||||||
return ("#51b13e", JObject.FromObject(new { value = "pj-exposed", id = invoice }).ToString());
|
return ("#51b13e", new ReferenceLabel("pj-exposed", invoice));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static (string color, string label) PayoutTemplate(string payoutId, string pullPaymentId, string walletId)
|
public static (string color, Label label) PayoutTemplate(string payoutId, string pullPaymentId, string walletId)
|
||||||
{
|
{
|
||||||
return ("#3F88AF", JObject.FromObject(new { value = "payout", id = payoutId, pullPaymentId, walletId }).ToString());
|
return ("#3F88AF", new PayoutLabel()
|
||||||
|
{
|
||||||
|
PayoutId = payoutId,
|
||||||
|
PullPaymentId = pullPaymentId,
|
||||||
|
WalletId = walletId
|
||||||
|
});
|
||||||
}
|
}
|
||||||
public WalletId WalletId { get; set; }
|
public WalletId WalletId { get; set; }
|
||||||
public Dictionary<uint256, List<(string color, string label)>> TransactionLabels { get; set; }
|
public Dictionary<uint256, List<(string color, Label label)>> TransactionLabels { get; set; }
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
var result = new StringBuilder();
|
var result = new StringBuilder();
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ namespace BTCPayServer.Models.WalletViewModels
|
|||||||
public string Link { get; set; }
|
public string Link { get; set; }
|
||||||
public bool Positive { get; set; }
|
public bool Positive { get; set; }
|
||||||
public string Balance { get; set; }
|
public string Balance { get; set; }
|
||||||
public HashSet<Label> Labels { get; set; } = new HashSet<Label>();
|
public HashSet<ColoredLabel> Labels { get; set; } = new HashSet<ColoredLabel>();
|
||||||
}
|
}
|
||||||
public HashSet<Label> Labels { get; set; } = new HashSet<Label>();
|
public HashSet<ColoredLabel> Labels { get; set; } = new HashSet<ColoredLabel>();
|
||||||
public List<TransactionViewModel> Transactions { get; set; } = new List<TransactionViewModel>();
|
public List<TransactionViewModel> Transactions { get; set; } = new List<TransactionViewModel>();
|
||||||
public int Skip { get; set; }
|
public int Skip { get; set; }
|
||||||
public int Count { get; set; }
|
public int Count { get; set; }
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ namespace BTCPayServer.Models.WalletViewModels
|
|||||||
|
|
||||||
public class InputSelectionOption
|
public class InputSelectionOption
|
||||||
{
|
{
|
||||||
public IEnumerable<Label> Labels { get; set; }
|
public IEnumerable<ColoredLabel> Labels { get; set; }
|
||||||
public string Comment { get; set; }
|
public string Comment { get; set; }
|
||||||
public decimal Amount { get; set; }
|
public decimal Amount { get; set; }
|
||||||
public string Outpoint { get; set; }
|
public string Outpoint { get; set; }
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ using BTCPayServer.HostedServices;
|
|||||||
using BTCPayServer.Payments.Bitcoin;
|
using BTCPayServer.Payments.Bitcoin;
|
||||||
using BTCPayServer.Services;
|
using BTCPayServer.Services;
|
||||||
using BTCPayServer.Services.Invoices;
|
using BTCPayServer.Services.Invoices;
|
||||||
|
using BTCPayServer.Services.Labels;
|
||||||
using BTCPayServer.Services.Stores;
|
using BTCPayServer.Services.Stores;
|
||||||
using BTCPayServer.Services.Wallets;
|
using BTCPayServer.Services.Wallets;
|
||||||
using Microsoft.AspNetCore.Cors;
|
using Microsoft.AspNetCore.Cors;
|
||||||
@@ -462,8 +463,8 @@ namespace BTCPayServer.Payments.PayJoin
|
|||||||
{
|
{
|
||||||
WalletId = new WalletId(invoice.StoreId, network.CryptoCode),
|
WalletId = new WalletId(invoice.StoreId, network.CryptoCode),
|
||||||
TransactionLabels = selectedUTXOs.GroupBy(pair => pair.Key.Hash).Select(utxo =>
|
TransactionLabels = selectedUTXOs.GroupBy(pair => pair.Key.Hash).Select(utxo =>
|
||||||
new KeyValuePair<uint256, List<(string color, string label)>>(utxo.Key,
|
new KeyValuePair<uint256, List<(string color, Label label)>>(utxo.Key,
|
||||||
new List<(string color, string label)>()
|
new List<(string color, Label label)>()
|
||||||
{
|
{
|
||||||
UpdateTransactionLabel.PayjoinExposedLabelTemplate(invoice.Id)
|
UpdateTransactionLabel.PayjoinExposedLabelTemplate(invoice.Id)
|
||||||
}))
|
}))
|
||||||
|
|||||||
43
BTCPayServer/Services/Labels/ColoredLabel.cs
Normal file
43
BTCPayServer/Services/Labels/ColoredLabel.cs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Services.Labels
|
||||||
|
{
|
||||||
|
public class ColoredLabel
|
||||||
|
{
|
||||||
|
internal ColoredLabel()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Text { get; internal set; }
|
||||||
|
public string Color { get; internal set; }
|
||||||
|
public string Link { get; internal set; }
|
||||||
|
public string Tooltip { get; internal set; }
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
ColoredLabel item = obj as ColoredLabel;
|
||||||
|
if (item == null)
|
||||||
|
return false;
|
||||||
|
return Text.Equals(item.Text, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(ColoredLabel a, ColoredLabel b)
|
||||||
|
{
|
||||||
|
if (System.Object.ReferenceEquals(a, b))
|
||||||
|
return true;
|
||||||
|
if (((object)a == null) || ((object)b == null))
|
||||||
|
return false;
|
||||||
|
return a.Text == b.Text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(ColoredLabel a, ColoredLabel b)
|
||||||
|
{
|
||||||
|
return !(a == b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return Text.GetHashCode(StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,44 +1,124 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace BTCPayServer.Services.Labels
|
namespace BTCPayServer.Services.Labels
|
||||||
{
|
{
|
||||||
public class Label
|
public abstract class Label
|
||||||
{
|
{
|
||||||
internal Label()
|
public string Type { get; set; }
|
||||||
|
public string Text { get; set; }
|
||||||
|
static void FixLegacy(JObject jObj, ReferenceLabel refLabel)
|
||||||
{
|
{
|
||||||
|
if (refLabel.Reference is null)
|
||||||
|
refLabel.Reference = jObj["id"].Value<string>();
|
||||||
|
FixLegacy(jObj, (Label)refLabel);
|
||||||
}
|
}
|
||||||
|
static void FixLegacy(JObject jObj, PayoutLabel payoutLabel)
|
||||||
public string Value { get; internal set; }
|
|
||||||
public string RawValue { get; internal set; }
|
|
||||||
public string Color { get; internal set; }
|
|
||||||
public string Link { get; internal set; }
|
|
||||||
public string Tooltip { get; internal set; }
|
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
{
|
||||||
Label item = obj as Label;
|
if (payoutLabel.PayoutId is null)
|
||||||
if (item == null)
|
payoutLabel.PayoutId = jObj["id"].Value<string>();
|
||||||
return false;
|
FixLegacy(jObj, (Label)payoutLabel);
|
||||||
return Value.Equals(item.Value, StringComparison.OrdinalIgnoreCase);
|
|
||||||
}
|
}
|
||||||
|
static void FixLegacy(JObject jObj, Label label)
|
||||||
public static bool operator ==(Label a, Label b)
|
|
||||||
{
|
{
|
||||||
if (System.Object.ReferenceEquals(a, b))
|
if (label.Type is null)
|
||||||
return true;
|
label.Type = jObj["value"].Value<string>();
|
||||||
if (((object)a == null) || ((object)b == null))
|
if (label.Text is null)
|
||||||
return false;
|
label.Text = label.Type;
|
||||||
return a.Value == b.Value;
|
|
||||||
}
|
}
|
||||||
|
static void FixLegacy(JObject jObj, RawLabel rawLabel)
|
||||||
public static bool operator !=(Label a, Label b)
|
|
||||||
{
|
{
|
||||||
return !(a == b);
|
rawLabel.Type = "raw";
|
||||||
|
FixLegacy(jObj, (Label)rawLabel);
|
||||||
}
|
}
|
||||||
|
public static Label Parse(string str)
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
{
|
||||||
return Value.GetHashCode(StringComparison.OrdinalIgnoreCase);
|
if (str == null)
|
||||||
|
throw new ArgumentNullException(nameof(str));
|
||||||
|
if (str.StartsWith("{", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
var jObj = JObject.Parse(str);
|
||||||
|
string type = null;
|
||||||
|
// Legacy label
|
||||||
|
if (!jObj.ContainsKey("type"))
|
||||||
|
{
|
||||||
|
type = jObj["value"].Value<string>();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
type = jObj["type"].Value<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case "raw":
|
||||||
|
var rawLabel = JsonConvert.DeserializeObject<RawLabel>(str);
|
||||||
|
FixLegacy(jObj, rawLabel);
|
||||||
|
return rawLabel;
|
||||||
|
case "invoice":
|
||||||
|
case "payment-request":
|
||||||
|
case "app":
|
||||||
|
case "pj-exposed":
|
||||||
|
var refLabel = JsonConvert.DeserializeObject<ReferenceLabel>(str);
|
||||||
|
FixLegacy(jObj, refLabel);
|
||||||
|
return refLabel;
|
||||||
|
case "payout":
|
||||||
|
var payoutLabel = JsonConvert.DeserializeObject<PayoutLabel>(str);
|
||||||
|
FixLegacy(jObj, payoutLabel);
|
||||||
|
return payoutLabel;
|
||||||
|
default:
|
||||||
|
// Legacy
|
||||||
|
return new RawLabel(jObj["value"].Value<string>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new RawLabel(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[JsonExtensionData]
|
||||||
|
public IDictionary<string, JToken> AdditionalData { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RawLabel : Label
|
||||||
|
{
|
||||||
|
public RawLabel()
|
||||||
|
{
|
||||||
|
Type = "raw";
|
||||||
|
}
|
||||||
|
public RawLabel(string text) : this()
|
||||||
|
{
|
||||||
|
Text = text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public class ReferenceLabel : Label
|
||||||
|
{
|
||||||
|
public ReferenceLabel()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
public ReferenceLabel(string type, string reference)
|
||||||
|
{
|
||||||
|
Text = type;
|
||||||
|
Reference = reference;
|
||||||
|
Type = type;
|
||||||
|
}
|
||||||
|
[JsonProperty("ref")]
|
||||||
|
public string Reference { get; set; }
|
||||||
|
}
|
||||||
|
public class PayoutLabel : Label
|
||||||
|
{
|
||||||
|
public PayoutLabel()
|
||||||
|
{
|
||||||
|
Type = "payout";
|
||||||
|
Text = "payout";
|
||||||
|
}
|
||||||
|
public string PayoutId { get; set; }
|
||||||
|
public string WalletId { get; set; }
|
||||||
|
public string PullPaymentId { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Amazon.Util.Internal.PlatformServices;
|
||||||
using BTCPayServer.Data;
|
using BTCPayServer.Data;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
@@ -17,104 +18,79 @@ namespace BTCPayServer.Services.Labels
|
|||||||
_linkGenerator = linkGenerator;
|
_linkGenerator = linkGenerator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<Label> GetLabels(WalletBlobInfo walletBlobInfo, WalletTransactionInfo transactionInfo,
|
public IEnumerable<ColoredLabel> ColorizeTransactionLabels(WalletBlobInfo walletBlobInfo, WalletTransactionInfo transactionInfo,
|
||||||
HttpRequest request)
|
HttpRequest request)
|
||||||
{
|
{
|
||||||
foreach (var label in transactionInfo.Labels)
|
foreach (var label in transactionInfo.Labels)
|
||||||
{
|
{
|
||||||
if (walletBlobInfo.LabelColors.TryGetValue(label, out var color))
|
if (walletBlobInfo.LabelColors.TryGetValue(label.Value.Text, out var color))
|
||||||
{
|
{
|
||||||
yield return CreateLabel(label, color, request);
|
yield return CreateLabel(label.Value, color, request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<Label> GetLabels(WalletBlobInfo walletBlobInfo, HttpRequest request)
|
public IEnumerable<ColoredLabel> GetWalletColoredLabels(WalletBlobInfo walletBlobInfo, HttpRequest request)
|
||||||
{
|
{
|
||||||
foreach (var kv in walletBlobInfo.LabelColors)
|
foreach (var kv in walletBlobInfo.LabelColors)
|
||||||
{
|
{
|
||||||
yield return CreateLabel(kv.Key, kv.Value, request);
|
yield return CreateLabel(new RawLabel() { Text = kv.Key }, kv.Value, request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Label CreateLabel(string value, string color, HttpRequest request)
|
private ColoredLabel CreateLabel(Label uncoloredLabel, string color, HttpRequest request)
|
||||||
{
|
{
|
||||||
if (value == null)
|
if (uncoloredLabel == null)
|
||||||
throw new ArgumentNullException(nameof(value));
|
throw new ArgumentNullException(nameof(uncoloredLabel));
|
||||||
if (color == null)
|
if (color == null)
|
||||||
throw new ArgumentNullException(nameof(color));
|
throw new ArgumentNullException(nameof(color));
|
||||||
if (value.StartsWith("{", StringComparison.InvariantCultureIgnoreCase))
|
|
||||||
|
ColoredLabel coloredLabel = new ColoredLabel()
|
||||||
{
|
{
|
||||||
var jObj = JObject.Parse(value);
|
Text = uncoloredLabel.Text,
|
||||||
if (jObj.ContainsKey("value"))
|
Color = color
|
||||||
|
};
|
||||||
|
if (uncoloredLabel is ReferenceLabel refLabel)
|
||||||
|
{
|
||||||
|
var refInLabel = string.IsNullOrEmpty(refLabel.Reference) ? string.Empty : $"({refLabel.Reference})";
|
||||||
|
switch (uncoloredLabel.Type)
|
||||||
{
|
{
|
||||||
var id = jObj.ContainsKey("id") ? jObj["id"].Value<string>() : string.Empty;
|
case "invoice":
|
||||||
var idInLabel = string.IsNullOrEmpty(id) ? string.Empty : $"({id})";
|
coloredLabel.Tooltip = $"Received through an invoice {refInLabel}";
|
||||||
|
coloredLabel.Link = string.IsNullOrEmpty(refLabel.Reference)
|
||||||
switch (jObj["value"].Value<string>())
|
? null
|
||||||
{
|
: _linkGenerator.InvoiceLink(refLabel.Reference, request.Scheme, request.Host, request.PathBase);
|
||||||
case "invoice":
|
break;
|
||||||
return new Label()
|
case "payment-request":
|
||||||
{
|
coloredLabel.Tooltip = $"Received through a payment request {refInLabel}";
|
||||||
RawValue = value,
|
coloredLabel.Link = string.IsNullOrEmpty(refLabel.Reference)
|
||||||
Value = "invoice",
|
? null
|
||||||
Color = color,
|
: _linkGenerator.PaymentRequestLink(refLabel.Reference, request.Scheme, request.Host, request.PathBase);
|
||||||
Tooltip = $"Received through an invoice {idInLabel}",
|
break;
|
||||||
Link = string.IsNullOrEmpty(id)
|
case "app":
|
||||||
? null
|
coloredLabel.Tooltip = $"Received through an app {refInLabel}";
|
||||||
: _linkGenerator.InvoiceLink(id, request.Scheme, request.Host, request.PathBase)
|
coloredLabel.Link = string.IsNullOrEmpty(refLabel.Reference)
|
||||||
};
|
? null
|
||||||
case "payment-request":
|
: _linkGenerator.AppLink(refLabel.Reference, request.Scheme, request.Host, request.PathBase);
|
||||||
return new Label()
|
break;
|
||||||
{
|
case "pj-exposed":
|
||||||
RawValue = value,
|
coloredLabel.Tooltip = $"This utxo was exposed through a payjoin proposal for an invoice {refInLabel}";
|
||||||
Value = "payment-request",
|
coloredLabel.Link = string.IsNullOrEmpty(refLabel.Reference)
|
||||||
Color = color,
|
? null
|
||||||
Tooltip = $"Received through a payment request {idInLabel}",
|
: _linkGenerator.InvoiceLink(refLabel.Reference, request.Scheme, request.Host, request.PathBase);
|
||||||
Link = string.IsNullOrEmpty(id)
|
break;
|
||||||
? null
|
|
||||||
: _linkGenerator.PaymentRequestLink(id, request.Scheme, request.Host, request.PathBase)
|
|
||||||
};
|
|
||||||
case "app":
|
|
||||||
return new Label()
|
|
||||||
{
|
|
||||||
RawValue = value,
|
|
||||||
Value = "app",
|
|
||||||
Color = color,
|
|
||||||
Tooltip = $"Received through an app {idInLabel}",
|
|
||||||
Link = string.IsNullOrEmpty(id)
|
|
||||||
? null
|
|
||||||
: _linkGenerator.AppLink(id, request.Scheme, request.Host, request.PathBase)
|
|
||||||
};
|
|
||||||
case "pj-exposed":
|
|
||||||
return new Label()
|
|
||||||
{
|
|
||||||
RawValue = value,
|
|
||||||
Value = "payjoin-exposed",
|
|
||||||
Color = color,
|
|
||||||
Tooltip = $"This utxo was exposed through a payjoin proposal for an invoice {idInLabel}",
|
|
||||||
Link = string.IsNullOrEmpty(id)
|
|
||||||
? null
|
|
||||||
: _linkGenerator.InvoiceLink(id, request.Scheme, request.Host, request.PathBase)
|
|
||||||
};
|
|
||||||
case "payout":
|
|
||||||
return new Label()
|
|
||||||
{
|
|
||||||
RawValue = value,
|
|
||||||
Value = "payout",
|
|
||||||
Color = color,
|
|
||||||
Tooltip = $"Paid a payout of a pull payment ({jObj["pullPaymentId"].Value<string>()})",
|
|
||||||
Link = string.IsNullOrEmpty(id)
|
|
||||||
? null
|
|
||||||
: _linkGenerator.PayoutLink(jObj["walletId"].Value<string>(),
|
|
||||||
jObj["pullPaymentId"].Value<string>(), request.Scheme, request.Host,
|
|
||||||
request.PathBase)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (uncoloredLabel is PayoutLabel payoutLabel)
|
||||||
return new Label() { RawValue = value, Value = value, Color = color };
|
{
|
||||||
|
coloredLabel.Tooltip = $"Paid a payout of a pull payment ({payoutLabel.PullPaymentId})";
|
||||||
|
coloredLabel.Link = string.IsNullOrEmpty(payoutLabel.PullPaymentId) || string.IsNullOrEmpty(payoutLabel.WalletId)
|
||||||
|
? null
|
||||||
|
: _linkGenerator.PayoutLink(payoutLabel.WalletId,
|
||||||
|
payoutLabel.PullPaymentId, request.Scheme, request.Host,
|
||||||
|
request.PathBase);
|
||||||
|
}
|
||||||
|
return coloredLabel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,14 +48,14 @@
|
|||||||
class="badge badge-primary badge-pill ml-2"
|
class="badge badge-primary badge-pill ml-2"
|
||||||
v-for="label of item.labels"
|
v-for="label of item.labels"
|
||||||
v-bind:style="{ 'background-color': label.color}"
|
v-bind:style="{ 'background-color': label.color}"
|
||||||
key="label.value">
|
key="label.text">
|
||||||
|
|
||||||
<span v-if="!label.link" data-toggle="tooltip" v-tooltip="label.tooltip">
|
<span v-if="!label.link" data-toggle="tooltip" v-tooltip="label.tooltip">
|
||||||
{{label.value}}
|
{{label.text}}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<a :href="label.link" target="_blank"v-if="label.link" data-toggle="tooltip" v-tooltip="label.tooltip">
|
<a :href="label.link" target="_blank" v-if="label.link" data-toggle="tooltip" v-tooltip="label.tooltip">
|
||||||
{{label.value}}
|
{{label.text}}
|
||||||
<i class="fa fa-info-circle"></i>
|
<i class="fa fa-info-circle"></i>
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -81,8 +81,8 @@
|
|||||||
<span class="mr-2">Filter by label:</span>
|
<span class="mr-2">Filter by label:</span>
|
||||||
@foreach (var label in Model.Labels)
|
@foreach (var label in Model.Labels)
|
||||||
{
|
{
|
||||||
<a asp-route-labelFilter="@label.Value" class="badge mr-2 my-1 position-relative text-white d-block"
|
<a asp-route-labelFilter="@label.Text" class="badge mr-2 my-1 position-relative text-white d-block"
|
||||||
style="background-color: @label.Color;"><span class="text-white">@label.Value</span></a>
|
style="background-color: @label.Color;"><span class="text-white">@label.Text</span></a>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -123,7 +123,7 @@
|
|||||||
style="background-color: @label.Color; padding-right: 16px; z-index: 1;"
|
style="background-color: @label.Color; padding-right: 16px; z-index: 1;"
|
||||||
data-toggle="tooltip"
|
data-toggle="tooltip"
|
||||||
title="@label.Tooltip">
|
title="@label.Tooltip">
|
||||||
<a asp-route-labelFilter="@label.Value" class="text-white">@label.Value</a>
|
<a asp-route-labelFilter="@label.Text" class="text-white">@label.Text</a>
|
||||||
|
|
||||||
<form
|
<form
|
||||||
asp-route-walletId="@this.Context.GetRouteValue("walletId")"
|
asp-route-walletId="@this.Context.GetRouteValue("walletId")"
|
||||||
@@ -135,7 +135,7 @@
|
|||||||
name="removelabel"
|
name="removelabel"
|
||||||
style="color: @label.Color; filter: brightness(0.5);"
|
style="color: @label.Color; filter: brightness(0.5);"
|
||||||
type="submit"
|
type="submit"
|
||||||
value="@label.RawValue">
|
value="@label.Text">
|
||||||
<span class="fa fa-close"></span>
|
<span class="fa fa-close"></span>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -184,11 +184,11 @@
|
|||||||
{
|
{
|
||||||
@if (transaction.Labels.Contains(label))
|
@if (transaction.Labels.Contains(label))
|
||||||
{
|
{
|
||||||
<button name="removelabel" class="btn btn-sm" type="submit" value="@label.Value"><span class="badge" style="display:block;background-color:@label.Color;color:white;text-align:left;"><span class="fa fa-check"></span> @label.Value</span></button>
|
<button name="removelabel" class="btn btn-sm" type="submit" value="@label.Text"><span class="badge" style="display:block;background-color:@label.Color;color:white;text-align:left;"><span class="fa fa-check"></span> @label.Text</span></button>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<button name="addlabelclick" class="btn btn-sm" type="submit" value="@label.Value"><span class="badge" style="display:block;background-color:@label.Color;color:white;text-align:left;">@label.Value</span></button>
|
<button name="addlabelclick" class="btn btn-sm" type="submit" value="@label.Text"><span class="badge" style="display:block;background-color:@label.Color;color:white;text-align:left;">@label.Text</span></button>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user