mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-23 06:35:13 +01:00
Checkout v2: Improve truncation of displayed addresses (#4924)
This commit is contained in:
parent
a0bb3ace61
commit
18e34b3cbe
7 changed files with 86 additions and 36 deletions
|
@ -64,9 +64,9 @@ namespace BTCPayServer.Tests
|
||||||
var qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
var qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
||||||
var address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
var address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
||||||
var payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
var payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
||||||
var copyAddress = s.Driver.FindElement(By.Id("Address_BTC")).GetAttribute("value");
|
var copyAddress = s.Driver.FindElement(By.CssSelector("#Address_BTC .truncate-center-start")).Text;
|
||||||
Assert.Equal($"bitcoin:{address}", payUrl);
|
Assert.Equal($"bitcoin:{address}", payUrl);
|
||||||
Assert.StartsWith("bcrt", s.Driver.FindElement(By.Id("Address_BTC")).GetAttribute("value"));
|
Assert.StartsWith("bcrt", s.Driver.FindElement(By.CssSelector("#Address_BTC .truncate-center-start")).Text);
|
||||||
Assert.DoesNotContain("lightning=", payUrl);
|
Assert.DoesNotContain("lightning=", payUrl);
|
||||||
Assert.Equal(address, copyAddress);
|
Assert.Equal(address, copyAddress);
|
||||||
Assert.Equal($"bitcoin:{address.ToUpperInvariant()}", qrValue);
|
Assert.Equal($"bitcoin:{address.ToUpperInvariant()}", qrValue);
|
||||||
|
@ -86,7 +86,7 @@ namespace BTCPayServer.Tests
|
||||||
{
|
{
|
||||||
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
||||||
Assert.StartsWith("lightning:lnurl", payUrl);
|
Assert.StartsWith("lightning:lnurl", payUrl);
|
||||||
Assert.StartsWith("lnurl", s.Driver.WaitForElement(By.Id("Lightning_BTC")).GetAttribute("value"));
|
Assert.StartsWith("lnurl", s.Driver.WaitForElement(By.CssSelector("#Lightning_BTC .truncate-center-start")).Text);
|
||||||
s.Driver.ElementDoesNotExist(By.Id("Address_BTC"));
|
s.Driver.ElementDoesNotExist(By.Id("Address_BTC"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ namespace BTCPayServer.Tests
|
||||||
qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
||||||
address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
||||||
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
||||||
copyAddress = s.Driver.FindElement(By.Id("Lightning_BTC_LightningLike")).GetAttribute("value");
|
copyAddress = s.Driver.FindElement(By.CssSelector("#Lightning_BTC_LightningLike .truncate-center-start")).Text;
|
||||||
Assert.Equal($"lightning:{address}", payUrl);
|
Assert.Equal($"lightning:{address}", payUrl);
|
||||||
Assert.Equal(address, copyAddress);
|
Assert.Equal(address, copyAddress);
|
||||||
Assert.Equal($"lightning:{address.ToUpperInvariant()}", qrValue);
|
Assert.Equal($"lightning:{address.ToUpperInvariant()}", qrValue);
|
||||||
|
@ -229,8 +229,8 @@ namespace BTCPayServer.Tests
|
||||||
qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
||||||
address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
||||||
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
||||||
var copyAddressOnchain = s.Driver.FindElement(By.Id("Address_BTC")).GetAttribute("value");
|
var copyAddressOnchain = s.Driver.FindElement(By.CssSelector("#Address_BTC .truncate-center-start")).Text;
|
||||||
var copyAddressLightning = s.Driver.FindElement(By.Id("Lightning_BTC")).GetAttribute("value");
|
var copyAddressLightning = s.Driver.FindElement(By.CssSelector("#Lightning_BTC .truncate-center-start")).Text;
|
||||||
Assert.StartsWith($"bitcoin:{address}?amount=", payUrl);
|
Assert.StartsWith($"bitcoin:{address}?amount=", payUrl);
|
||||||
Assert.Contains("?amount=", payUrl);
|
Assert.Contains("?amount=", payUrl);
|
||||||
Assert.Contains("&lightning=", payUrl);
|
Assert.Contains("&lightning=", payUrl);
|
||||||
|
@ -297,8 +297,8 @@ namespace BTCPayServer.Tests
|
||||||
qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
||||||
address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
||||||
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
||||||
copyAddressOnchain = s.Driver.FindElement(By.Id("Address_BTC")).GetAttribute("value");
|
copyAddressOnchain = s.Driver.FindElement(By.CssSelector("#Address_BTC .truncate-center-start")).Text;
|
||||||
copyAddressLightning = s.Driver.FindElement(By.Id("Lightning_BTC")).GetAttribute("value");
|
copyAddressLightning = s.Driver.FindElement(By.CssSelector("#Lightning_BTC .truncate-center-start")).Text;
|
||||||
Assert.StartsWith($"bitcoin:{address}", payUrl);
|
Assert.StartsWith($"bitcoin:{address}", payUrl);
|
||||||
Assert.Contains("?lightning=lnurl", payUrl);
|
Assert.Contains("?lightning=lnurl", payUrl);
|
||||||
Assert.DoesNotContain("amount=", payUrl);
|
Assert.DoesNotContain("amount=", payUrl);
|
||||||
|
|
|
@ -1,10 +1,29 @@
|
||||||
@model BTCPayServer.Components.TruncateCenter.TruncateCenterViewModel
|
@model BTCPayServer.Components.TruncateCenter.TruncateCenterViewModel
|
||||||
<span class="truncate-center @Model.Classes">
|
@{
|
||||||
<span class="truncate-center-truncated" @(Model.Truncated != Model.Text ? $"data-bs-toggle=tooltip title={Model.Text}" : "")>@Model.Truncated</span>
|
var classes = string.IsNullOrEmpty(Model.Classes) ? string.Empty : Model.Classes.Trim();
|
||||||
|
@if (Model.Copy) classes += " truncate-center--copy";
|
||||||
|
@if (Model.Elastic) classes += " truncate-center--elastic";
|
||||||
|
}
|
||||||
|
<span class="truncate-center @classes">
|
||||||
|
@if (Model.IsVue)
|
||||||
|
{
|
||||||
|
<span class="truncate-center-truncated" data-bs-toggle="tooltip" :title=@Safe.Json(Model.Text)>
|
||||||
|
<span class="truncate-center-start" v-text=@Safe.Json(Model.Text)></span>
|
||||||
|
<span class="truncate-center-end" v-text=@Safe.Json($"{Model.Text}.slice(-{Model.Padding})")></span>
|
||||||
|
</span>
|
||||||
|
<span class="truncate-center-text" v-text=@Safe.Json(Model.Text)></span>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span class="truncate-center-truncated" @(!string.IsNullOrEmpty(Model.Start) ? $"data-bs-toggle=tooltip title={Model.Text}" : "")>
|
||||||
|
<span class="truncate-center-start">@(Model.Elastic ? Model.Text : $"{Model.Start}…")</span>
|
||||||
|
<span class="truncate-center-end">@Model.End</span>
|
||||||
|
</span>
|
||||||
<span class="truncate-center-text">@Model.Text</span>
|
<span class="truncate-center-text">@Model.Text</span>
|
||||||
|
}
|
||||||
@if (Model.Copy)
|
@if (Model.Copy)
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-link p-0" data-clipboard="@Model.Text">
|
<button type="button" class="btn btn-link p-0" @(Model.IsVue ? ":" : string.Empty)data-clipboard=@Safe.Json(Model.Text)>
|
||||||
<vc:icon symbol="copy" />
|
<vc:icon symbol="copy" />
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace BTCPayServer.Components.TruncateCenter;
|
||||||
/// <returns>HTML with truncated string</returns>
|
/// <returns>HTML with truncated string</returns>
|
||||||
public class TruncateCenter : ViewComponent
|
public class TruncateCenter : ViewComponent
|
||||||
{
|
{
|
||||||
public IViewComponentResult Invoke(string text, string link = null, string classes = null, int padding = 7, bool copy = true)
|
public IViewComponentResult Invoke(string text, string link = null, string classes = null, int padding = 7, bool copy = true, bool elastic = false, bool isVue = false)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(text))
|
if (string.IsNullOrEmpty(text))
|
||||||
return new HtmlContentViewComponentResult(new StringHtmlContent(string.Empty));
|
return new HtmlContentViewComponentResult(new StringHtmlContent(string.Empty));
|
||||||
|
@ -23,11 +23,17 @@ public class TruncateCenter : ViewComponent
|
||||||
{
|
{
|
||||||
Classes = classes,
|
Classes = classes,
|
||||||
Padding = padding,
|
Padding = padding,
|
||||||
|
Elastic = elastic,
|
||||||
|
IsVue = isVue,
|
||||||
Copy = copy,
|
Copy = copy,
|
||||||
Text = text,
|
Text = text,
|
||||||
Link = link,
|
Link = link
|
||||||
Truncated = text.Length > 2 * padding ? $"{text[..padding]}…{text[^padding..]}" : text
|
|
||||||
};
|
};
|
||||||
|
if (!isVue && text.Length > 2 * padding)
|
||||||
|
{
|
||||||
|
vm.Start = text[..padding];
|
||||||
|
vm.End = text[^padding..];
|
||||||
|
}
|
||||||
return View(vm);
|
return View(vm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,13 @@ namespace BTCPayServer.Components.TruncateCenter
|
||||||
public class TruncateCenterViewModel
|
public class TruncateCenterViewModel
|
||||||
{
|
{
|
||||||
public string Text { get; set; }
|
public string Text { get; set; }
|
||||||
public string Truncated { get; set; }
|
public string Start { get; set; }
|
||||||
|
public string End { get; set; }
|
||||||
public string Classes { get; set; }
|
public string Classes { get; set; }
|
||||||
public string Link { get; set; }
|
public string Link { get; set; }
|
||||||
public int Padding { get; set; }
|
public int Padding { get; set; }
|
||||||
public bool Copy { get; set; }
|
public bool Copy { get; set; }
|
||||||
|
public bool Elastic { get; set; }
|
||||||
|
public bool IsVue { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
@using BTCPayServer.BIP78.Sender
|
@using BTCPayServer.BIP78.Sender
|
||||||
|
@using BTCPayServer.Components.TruncateCenter
|
||||||
|
@using BTCPayServer.Abstractions.TagHelpers
|
||||||
@model BTCPayServer.Models.InvoicingModels.PaymentModel
|
@model BTCPayServer.Models.InvoicingModels.PaymentModel
|
||||||
|
|
||||||
<template id="bitcoin-method-checkout-template">
|
<template id="bitcoin-method-checkout-template">
|
||||||
|
@ -11,22 +13,16 @@
|
||||||
<img class="qr-icon" :src="model.cryptoImage" :alt="model.paymentMethodName"/>
|
<img class="qr-icon" :src="model.cryptoImage" :alt="model.paymentMethodName"/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="model.btcAddress" class="input-group mt-3">
|
<div v-if="model.btcAddress" class="input-group mt-3">
|
||||||
<div class="form-floating">
|
<div class="form-floating" id="Address_@Model.PaymentMethodId">
|
||||||
<input id="Address_@Model.PaymentMethodId" class="form-control-plaintext" readonly="readonly" :value="model.btcAddress">
|
<vc:truncate-center text="model.btcAddress" is-vue="true" padding="15" elastic="true" classes="form-control-plaintext" />
|
||||||
<label for="Address_@Model.PaymentMethodId" v-t="{ path: 'address', args: { paymentMethod: model.paymentMethodName }}"></label>
|
<label v-t="{ path: 'address', args: { paymentMethod: model.paymentMethodName }}"></label>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-link" data-clipboard-target="#Address_@Model.PaymentMethodId">
|
|
||||||
<vc:icon symbol="copy" />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-if="lightning" class="input-group mt-3">
|
<div v-if="lightning" class="input-group mt-3">
|
||||||
<div class="form-floating">
|
<div class="form-floating" id="Lightning_@Model.PaymentMethodId">
|
||||||
<input id="Lightning_@Model.PaymentMethodId" class="form-control-plaintext" readonly="readonly" :value="lightning" />
|
<vc:truncate-center text="lightning" is-vue="true" padding="15" elastic="true" classes="form-control-plaintext" />
|
||||||
<label for="Lightning_@Model.PaymentMethodId" v-t="'lightning'"></label>
|
<label v-t="'lightning'"></label>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-link" data-clipboard-target="#Lightning_@Model.PaymentMethodId">
|
|
||||||
<vc:icon symbol="copy" />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<a v-if="model.invoiceBitcoinUrl && model.showPayInWalletButton" class="btn btn-primary rounded-pill w-100 mt-4" target="_top" id="PayInWallet"
|
<a v-if="model.invoiceBitcoinUrl && model.showPayInWalletButton" class="btn btn-primary rounded-pill w-100 mt-4" target="_top" id="PayInWallet"
|
||||||
:href="model.invoiceBitcoinUrl" :title="$t(hasPayjoin ? 'BIP21 payment link with PayJoin support' : 'BIP21 payment link')" v-t="'pay_in_wallet'"></a>
|
:href="model.invoiceBitcoinUrl" :title="$t(hasPayjoin ? 'BIP21 payment link with PayJoin support' : 'BIP21 payment link')" v-t="'pay_in_wallet'"></a>
|
||||||
|
|
|
@ -10,13 +10,10 @@
|
||||||
<img class="qr-icon" :src="model.cryptoImage" :alt="model.paymentMethodName"/>
|
<img class="qr-icon" :src="model.cryptoImage" :alt="model.paymentMethodName"/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="model.btcAddress" class="input-group mt-3">
|
<div v-if="model.btcAddress" class="input-group mt-3">
|
||||||
<div class="form-floating">
|
<div class="form-floating" id="Lightning_@Model.PaymentMethodId">
|
||||||
<input id="Lightning_@Model.PaymentMethodId" class="form-control-plaintext" readonly="readonly" :value="model.btcAddress">
|
<vc:truncate-center text="model.btcAddress" is-vue="true" padding="15" elastic="true" classes="form-control-plaintext" />
|
||||||
<label for="Lightning_@Model.PaymentMethodId" v-t="'lightning'"></label>
|
<label v-t="'lightning'"></label>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-link" data-clipboard-target="#Lightning_@Model.PaymentMethodId">
|
|
||||||
<vc:icon symbol="copy" />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<a v-if="model.invoiceBitcoinUrl && model.showPayInWalletButton" class="btn btn-primary rounded-pill w-100 mt-4" target="_top" id="PayInWallet"
|
<a v-if="model.invoiceBitcoinUrl && model.showPayInWalletButton" class="btn btn-primary rounded-pill w-100 mt-4" target="_top" id="PayInWallet"
|
||||||
:href="model.invoiceBitcoinUrl" v-t="'pay_in_wallet'"></a>
|
:href="model.invoiceBitcoinUrl" v-t="'pay_in_wallet'"></a>
|
||||||
|
|
|
@ -928,6 +928,14 @@ input.ts-wrapper.form-control:not(.ts-hidden-accessible,.ts-inline) {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--btcpay-space-s);
|
gap: var(--btcpay-space-s);
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.truncate-center--elastic .truncate-center-start {
|
||||||
|
flex: 0 1 auto;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.truncate-center-id {
|
.truncate-center-id {
|
||||||
|
@ -941,15 +949,36 @@ input.ts-wrapper.form-control:not(.ts-hidden-accessible,.ts-inline) {
|
||||||
}
|
}
|
||||||
|
|
||||||
.truncate-center a,
|
.truncate-center a,
|
||||||
.truncate-center button,
|
.truncate-center button {
|
||||||
.truncate-center-truncated {
|
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.truncate-center-truncated {
|
||||||
|
text-align: left;
|
||||||
|
display: inline-flex;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.truncate-center button.btn .icon {
|
.truncate-center button.btn .icon {
|
||||||
--btn-icon-size: 1em;
|
--btn-icon-size: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.truncate-center--elastic .truncate-center-truncated {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.truncate-center--elastic.truncate-center--copy .truncate-center-truncated {
|
||||||
|
max-width: calc(100% - 2em);
|
||||||
|
}
|
||||||
|
|
||||||
|
.truncate-center.form-control-plaintext {
|
||||||
|
padding-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.truncate-center.form-control-plaintext button.btn .icon {
|
||||||
|
--btn-icon-size: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
@media screen {
|
@media screen {
|
||||||
.truncate-center-id {
|
.truncate-center-id {
|
||||||
background: var(--btcpay-neutral-100);
|
background: var(--btcpay-neutral-100);
|
||||||
|
|
Loading…
Add table
Reference in a new issue