Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
3192db6fec |
@ -32,9 +32,9 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="HtmlSanitizer" Version="5.0.372" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.9" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.9" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.7" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.5" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -14,12 +14,6 @@ namespace BTCPayServer.Abstractions.Extensions
|
||||
private const string ACTIVE_ID_KEY = "ActiveId";
|
||||
private const string ActivePageClass = "active";
|
||||
|
||||
public enum DateDisplayFormat
|
||||
{
|
||||
Localized,
|
||||
Relative
|
||||
}
|
||||
|
||||
public static void SetActivePage<T>(this ViewDataDictionary viewData, T activePage, string title = null, string activeId = null)
|
||||
where T : IConvertible
|
||||
{
|
||||
@ -92,29 +86,26 @@ namespace BTCPayServer.Abstractions.Extensions
|
||||
return categoryAndPageMatch && idMatch ? ActivePageClass : null;
|
||||
}
|
||||
|
||||
public static HtmlString ToBrowserDate(this DateTimeOffset date, DateDisplayFormat format = DateDisplayFormat.Localized)
|
||||
public static HtmlString ToBrowserDate(this DateTimeOffset date)
|
||||
{
|
||||
var relative = date.ToTimeAgo();
|
||||
var initial = format.ToString().ToLower();
|
||||
var dateTime = date.ToString("s", CultureInfo.InvariantCulture);
|
||||
var displayDate = format == DateDisplayFormat.Relative ? relative : date.ToString("g", CultureInfo.InvariantCulture);
|
||||
return new HtmlString($"<time datetime=\"{dateTime}\" data-relative=\"{relative}\" data-initial=\"{initial}\">{displayDate}</time>");
|
||||
var displayDate = date.ToString("o", CultureInfo.InvariantCulture);
|
||||
return new HtmlString($"<span class='localizeDate'>{displayDate}</span>");
|
||||
}
|
||||
|
||||
public static HtmlString ToBrowserDate(this DateTime date, DateDisplayFormat format = DateDisplayFormat.Localized)
|
||||
public static HtmlString ToBrowserDate(this DateTime date)
|
||||
{
|
||||
var relative = date.ToTimeAgo();
|
||||
var initial = format.ToString().ToLower();
|
||||
var dateTime = date.ToString("s", CultureInfo.InvariantCulture);
|
||||
var displayDate = format == DateDisplayFormat.Relative ? relative : date.ToString("g", CultureInfo.InvariantCulture);
|
||||
return new HtmlString($"<time datetime=\"{dateTime}\" data-relative=\"{relative}\" data-initial=\"{initial}\">{displayDate}</time>");
|
||||
var displayDate = date.ToString("o", CultureInfo.InvariantCulture);
|
||||
return new HtmlString($"<span class='localizeDate'>{displayDate}</span>");
|
||||
}
|
||||
|
||||
public static string ToTimeAgo(this DateTimeOffset date) => (DateTimeOffset.UtcNow - date).ToTimeAgo();
|
||||
|
||||
public static string ToTimeAgo(this DateTime date) => (DateTimeOffset.UtcNow - date).ToTimeAgo();
|
||||
|
||||
public static string ToTimeAgo(this TimeSpan diff) => diff.TotalSeconds > 0 ? $"{diff.TimeString()} ago" : $"in {diff.Negate().TimeString()}";
|
||||
public static string ToTimeAgo(this DateTimeOffset date)
|
||||
{
|
||||
var diff = DateTimeOffset.UtcNow - date;
|
||||
var formatted = diff.TotalSeconds > 0
|
||||
? $"{diff.TimeString()} ago"
|
||||
: $"in {diff.Negate().TimeString()}";
|
||||
return formatted;
|
||||
}
|
||||
|
||||
public static string TimeString(this TimeSpan timeSpan)
|
||||
{
|
||||
@ -126,14 +117,16 @@ namespace BTCPayServer.Abstractions.Extensions
|
||||
{
|
||||
return $"{(int)timeSpan.TotalMinutes} minute{Plural((int)timeSpan.TotalMinutes)}";
|
||||
}
|
||||
return timeSpan.Days < 1
|
||||
? $"{(int)timeSpan.TotalHours} hour{Plural((int)timeSpan.TotalHours)}"
|
||||
: $"{(int)timeSpan.TotalDays} day{Plural((int)timeSpan.TotalDays)}";
|
||||
if (timeSpan.Days < 1)
|
||||
{
|
||||
return $"{(int)timeSpan.TotalHours} hour{Plural((int)timeSpan.TotalHours)}";
|
||||
}
|
||||
return $"{(int)timeSpan.TotalDays} day{Plural((int)timeSpan.TotalDays)}";
|
||||
}
|
||||
|
||||
private static string Plural(int value)
|
||||
{
|
||||
return value == 1 ? string.Empty : "s";
|
||||
return value > 1 ? "s" : string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,8 +28,8 @@
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.3.13" />
|
||||
<PackageReference Include="NBitcoin" Version="7.0.11" />
|
||||
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.3.12" />
|
||||
<PackageReference Include="NBitcoin" Version="7.0.10" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -9,13 +9,6 @@ namespace BTCPayServer.Client.Models
|
||||
[JsonProperty("nodeURIs", ItemConverterType = typeof(NodeUriJsonConverter))]
|
||||
public NodeInfo[] NodeURIs { get; set; }
|
||||
public int BlockHeight { get; set; }
|
||||
public string Alias { get; set; }
|
||||
public string Color { get; set; }
|
||||
public string Version { get; set; }
|
||||
public long? PeersCount { get; set; }
|
||||
public long? ActiveChannelsCount { get; set; }
|
||||
public long? InactiveChannelsCount { get; set; }
|
||||
public long? PendingChannelsCount { get; set; }
|
||||
}
|
||||
|
||||
public class LightningChannelData
|
||||
|
@ -3,11 +3,11 @@
|
||||
<Import Project="../Build/Common.csproj" />
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.9">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.7">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.9" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.7" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BTCPayServer.Abstractions\BTCPayServer.Abstractions.csproj" />
|
||||
|
@ -13,7 +13,7 @@
|
||||
<EmbeddedResource Include="Resources\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.9">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.7">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
@ -4,9 +4,9 @@
|
||||
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.1" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.9" />
|
||||
<PackageReference Include="NBitcoin" Version="7.0.11" />
|
||||
<PackageReference Include="NBitcoin" Version="7.0.10" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="DigitalRuby.ExchangeSharp" Version="1.0.2" />
|
||||
</ItemGroup>
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using BTCPayServer.Services.Rates;
|
||||
|
||||
namespace BTCPayServer.Rating
|
||||
@ -57,13 +56,6 @@ namespace BTCPayServer.Rating
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (splitted.Length > 2)
|
||||
{
|
||||
// Some shitcoin have _ their own ticker name... Since we don't care about those, let's
|
||||
// parse it anyway assuming the first part is one currency.
|
||||
value = new CurrencyPair(splitted[0], string.Join("_", splitted.Skip(1).ToArray()));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Import Project="../Build/Common.csproj" />
|
||||
<PropertyGroup>
|
||||
<IsPackable>false</IsPackable>
|
||||
@ -19,12 +19,12 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
|
||||
<PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.14" />
|
||||
<PackageReference Include="Selenium.Support" Version="4.1.1" />
|
||||
<PackageReference Include="Selenium.WebDriver" Version="4.1.1" />
|
||||
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="105.0.5195.5200" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="104.0.5112.7900" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0.401-bullseye-slim AS builder
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0.101-bullseye-slim AS builder
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends chromium-driver \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
@ -1294,9 +1294,6 @@ namespace BTCPayServer.Tests
|
||||
[Fact]
|
||||
public void CanParseRateRules()
|
||||
{
|
||||
var pair = CurrencyPair.Parse("USD_EMAT_IC");
|
||||
Assert.Equal("USD", pair.Left);
|
||||
Assert.Equal("EMAT_IC", pair.Right);
|
||||
// Check happy path
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.AppendLine("// Some cool comments");
|
||||
@ -1392,7 +1389,7 @@ namespace BTCPayServer.Tests
|
||||
rule2.Reevaluate();
|
||||
Assert.False(rule2.HasError);
|
||||
Assert.Equal("5000 * 2000.4 * 1.1", rule2.ToString(true));
|
||||
Assert.Equal(5000m * 2000.4m * 1.1m, rule2.BidAsk.Bid);
|
||||
Assert.Equal(rule2.BidAsk.Bid, 5000m * 2000.4m * 1.1m);
|
||||
////////
|
||||
|
||||
// Make sure parenthesis are correctly calculated
|
||||
|
@ -88,7 +88,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal("missing-permission", e.APIError.Code);
|
||||
Assert.NotNull(e.APIError.Message);
|
||||
GreenfieldPermissionAPIError permissionError = Assert.IsType<GreenfieldPermissionAPIError>(e.APIError);
|
||||
Assert.Equal(Policies.CanModifyStoreSettings, permissionError.MissingPermission);
|
||||
Assert.Equal(permissionError.MissingPermission, Policies.CanModifyStoreSettings);
|
||||
}
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
@ -1653,11 +1653,11 @@ namespace BTCPayServer.Tests
|
||||
await tester.StartAsync();
|
||||
await tester.EnsureChannelsSetup();
|
||||
var user = tester.NewAccount();
|
||||
await user.GrantAccessAsync(true);
|
||||
user.GrantAccess(true);
|
||||
user.RegisterLightningNode("BTC", LightningConnectionType.CLightning, false);
|
||||
|
||||
var merchant = tester.NewAccount();
|
||||
await merchant.GrantAccessAsync(true);
|
||||
merchant.GrantAccess(true);
|
||||
merchant.RegisterLightningNode("BTC", LightningConnectionType.LndREST);
|
||||
var merchantClient = await merchant.CreateClient($"{Policies.CanUseLightningNodeInStore}:{merchant.StoreId}");
|
||||
var merchantInvoice = await merchantClient.CreateLightningInvoice(merchant.StoreId, "BTC", new CreateLightningInvoiceRequest(LightMoney.Satoshis(1_000), "hey", TimeSpan.FromSeconds(60)));
|
||||
@ -1667,13 +1667,6 @@ namespace BTCPayServer.Tests
|
||||
var info = await client.GetLightningNodeInfo("BTC");
|
||||
Assert.Single(info.NodeURIs);
|
||||
Assert.NotEqual(0, info.BlockHeight);
|
||||
Assert.NotNull(info.Alias);
|
||||
Assert.NotNull(info.Color);
|
||||
Assert.NotNull(info.Version);
|
||||
Assert.NotNull(info.PeersCount);
|
||||
Assert.NotNull(info.ActiveChannelsCount);
|
||||
Assert.NotNull(info.InactiveChannelsCount);
|
||||
Assert.NotNull(info.PendingChannelsCount);
|
||||
|
||||
await AssertAPIError("lightning-node-unavailable", () => client.GetLightningNodeChannels("BTC"));
|
||||
// Not permission for the store!
|
||||
|
@ -75,11 +75,11 @@ namespace BTCPayServer.Tests
|
||||
public async Task CanQueryDirectProviders()
|
||||
{
|
||||
// TODO: Check once in a while whether or not they are working again
|
||||
string[] brokenShitcoinCasinos = {};
|
||||
var skipped = 0;
|
||||
string[] brokenShitcoinCasinos = { };
|
||||
var factory = FastTests.CreateBTCPayRateFactory();
|
||||
var directlySupported = factory.GetSupportedExchanges().Where(s => s.Source == RateSource.Direct)
|
||||
.Select(s => s.Id).ToHashSet();
|
||||
var all = string.Join("\r\n", factory.GetSupportedExchanges().Select(e => e.Id).ToArray());
|
||||
foreach (var result in factory
|
||||
.Providers
|
||||
.Where(p => p.Value is BackgroundFetcherRateProvider bf &&
|
||||
@ -91,26 +91,14 @@ namespace BTCPayServer.Tests
|
||||
var name = result.ExpectedName;
|
||||
if (brokenShitcoinCasinos.Contains(name))
|
||||
{
|
||||
TestLogs.LogInformation($"Skipping {name}: Broken shitcoin casino");
|
||||
skipped++;
|
||||
TestLogs.LogInformation($"Skipping {name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
TestLogs.LogInformation($"Testing {name}");
|
||||
|
||||
result.Fetcher.InvalidateCache();
|
||||
|
||||
ExchangeRates exchangeRates = null;
|
||||
try
|
||||
{
|
||||
exchangeRates = new ExchangeRates(name, result.ResultAsync.Result);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
TestLogs.LogInformation($"Skipping {name}: {exception.Message}");
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
var exchangeRates = new ExchangeRates(name, result.ResultAsync.Result);
|
||||
result.Fetcher.InvalidateCache();
|
||||
Assert.NotNull(exchangeRates);
|
||||
Assert.NotEmpty(exchangeRates);
|
||||
@ -172,12 +160,11 @@ namespace BTCPayServer.Tests
|
||||
// Kraken emit one request only after first GetRates
|
||||
factory.Providers["kraken"].GetRatesAsync(default).GetAwaiter().GetResult();
|
||||
|
||||
|
||||
var p = new KrakenExchangeRateProvider();
|
||||
var rates = await p.GetRatesAsync(default);
|
||||
Assert.Contains(rates, e => e.CurrencyPair == new CurrencyPair("XMR", "BTC") && e.BidAsk.Bid < 1.0m);
|
||||
|
||||
// Check we didn't skip too many exchanges
|
||||
Assert.InRange(skipped, 0, 3);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -652,7 +652,7 @@ namespace BTCPayServer.Tests
|
||||
(string)store2.TempData[WellKnownTempData.ErrorMessage], StringComparison.CurrentCultureIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact(Timeout = LongRunningTestTimeout * 2)]
|
||||
[Fact(Timeout = LongRunningTestTimeout)]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanUseTorClient()
|
||||
{
|
||||
@ -1753,7 +1753,7 @@ namespace BTCPayServer.Tests
|
||||
await user.GrantAccessAsync();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
await user.SetNetworkFeeMode(NetworkFeeMode.Always);
|
||||
var invoice = await user.BitPay.CreateInvoiceAsync(
|
||||
var invoice = user.BitPay.CreateInvoice(
|
||||
new Invoice
|
||||
{
|
||||
Price = 10,
|
||||
@ -1768,7 +1768,7 @@ namespace BTCPayServer.Tests
|
||||
var jsonResult = user.GetController<UIInvoiceController>().Export("json").GetAwaiter().GetResult();
|
||||
var result = Assert.IsType<ContentResult>(jsonResult);
|
||||
Assert.Equal("application/json", result.ContentType);
|
||||
Assert.Single(JArray.Parse(result.Content));
|
||||
Assert.Equal(1, JArray.Parse(result.Content).Count);
|
||||
|
||||
var cashCow = tester.ExplorerNode;
|
||||
var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, cashCow.Network);
|
||||
|
@ -48,9 +48,9 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BIP78.Sender" Version="0.2.2" />
|
||||
<PackageReference Include="BTCPayServer.Hwi" Version="2.0.2" />
|
||||
<PackageReference Include="BTCPayServer.Lightning.All" Version="1.4.5" />
|
||||
<PackageReference Include="BTCPayServer.Lightning.All" Version="1.4.4" />
|
||||
<PackageReference Include="BuildBundlerMinifier" Version="3.2.449" />
|
||||
<PackageReference Include="BundlerMinifier.Core" Version="3.2.449" />
|
||||
<PackageReference Include="BundlerMinifier.Core" Version="3.2.435" />
|
||||
<PackageReference Include="BundlerMinifier.TagHelpers" Version="3.2.435" />
|
||||
<PackageReference Include="CsvHelper" Version="15.0.5" />
|
||||
<PackageReference Include="Dapper" Version="2.0.123" />
|
||||
@ -90,8 +90,8 @@
|
||||
<PackageReference Include="TwentyTwenty.Storage.Google" Version="2.12.1" />
|
||||
<PackageReference Include="TwentyTwenty.Storage.Local" Version="2.12.1" />
|
||||
<PackageReference Include="YamlDotNet" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="6.0.9" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.9" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="6.0.7" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.7" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -46,14 +46,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
return Ok(new LightningNodeInformationData
|
||||
{
|
||||
BlockHeight = info.BlockHeight,
|
||||
NodeURIs = info.NodeInfoList.Select(nodeInfo => nodeInfo).ToArray(),
|
||||
Alias = info.Alias,
|
||||
Color = info.Color,
|
||||
Version = info.Version,
|
||||
PeersCount = info.PeersCount,
|
||||
ActiveChannelsCount = info.ActiveChannelsCount,
|
||||
InactiveChannelsCount = info.InactiveChannelsCount,
|
||||
PendingChannelsCount = info.PendingChannelsCount
|
||||
NodeURIs = info.NodeInfoList.Select(nodeInfo => nodeInfo).ToArray()
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,6 @@ namespace BTCPayServer.Controllers
|
||||
var details = InvoicePopulatePayments(invoice);
|
||||
model.CryptoPayments = details.CryptoPayments;
|
||||
model.Payments = details.Payments;
|
||||
model.Overpaid = details.Overpaid;
|
||||
|
||||
return View(model);
|
||||
}
|
||||
@ -177,7 +176,7 @@ namespace BTCPayServer.Controllers
|
||||
});
|
||||
}
|
||||
JToken? receiptData = null;
|
||||
i.Metadata?.AdditionalData?.TryGetValue("receiptData", out receiptData);
|
||||
i.Metadata?.AdditionalData.TryGetValue("receiptData", out receiptData);
|
||||
|
||||
return View(new InvoiceReceiptViewModel
|
||||
{
|
||||
@ -468,25 +467,15 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
private InvoiceDetailsModel InvoicePopulatePayments(InvoiceEntity invoice)
|
||||
{
|
||||
|
||||
var overpaid = false;
|
||||
var model = new InvoiceDetailsModel
|
||||
return new InvoiceDetailsModel
|
||||
{
|
||||
Archived = invoice.Archived,
|
||||
Payments = invoice.GetPayments(false),
|
||||
Overpaid = true,
|
||||
CryptoPayments = invoice.GetPaymentMethods().Select(
|
||||
data =>
|
||||
{
|
||||
var accounting = data.Calculate();
|
||||
var paymentMethodId = data.GetId();
|
||||
var overpaidAmount = accounting.OverpaidHelper.ToDecimal(MoneyUnit.BTC);
|
||||
|
||||
if (overpaidAmount > 0)
|
||||
{
|
||||
overpaid = true;
|
||||
}
|
||||
|
||||
return new InvoiceDetailsModel.CryptoPayment
|
||||
{
|
||||
PaymentMethodId = paymentMethodId,
|
||||
@ -497,16 +486,13 @@ namespace BTCPayServer.Controllers
|
||||
accounting.CryptoPaid.ToDecimal(MoneyUnit.BTC),
|
||||
paymentMethodId.CryptoCode),
|
||||
Overpaid = _CurrencyNameTable.DisplayFormatCurrency(
|
||||
overpaidAmount, paymentMethodId.CryptoCode),
|
||||
accounting.OverpaidHelper.ToDecimal(MoneyUnit.BTC), paymentMethodId.CryptoCode),
|
||||
Address = data.GetPaymentMethodDetails().GetPaymentDestination(),
|
||||
Rate = ExchangeRate(data),
|
||||
PaymentMethodRaw = data
|
||||
};
|
||||
}).ToList()
|
||||
};
|
||||
model.Overpaid = overpaid;
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
[HttpPost("invoices/{invoiceId}/archive")]
|
||||
|
@ -320,16 +320,14 @@ namespace BTCPayServer.Controllers
|
||||
// if we couldn't filter at the db level, we need to apply skip and count
|
||||
if (!preFiltering)
|
||||
{
|
||||
model.Transactions = model.Transactions.Skip(skip).Take(count).ToList();
|
||||
model.Transactions = model.Transactions.Skip(skip).Take(count)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
model.CryptoCode = walletId.CryptoCode;
|
||||
|
||||
//If ajax call then load the partial view
|
||||
return Request.Headers["X-Requested-With"] == "XMLHttpRequest"
|
||||
? PartialView("_WalletTransactionsList", model)
|
||||
: View(model);
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[HttpGet("{walletId}/histogram/{type}")]
|
||||
|
@ -301,9 +301,8 @@ namespace BTCPayServer.Data.Payouts.LightningLike
|
||||
var payment = await lightningClient.GetPayment(bolt11PaymentRequest.PaymentHash.ToString());
|
||||
proofBlob.Preimage = payment.Preimage;
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception e)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,9 +50,7 @@ namespace BTCPayServer.Data
|
||||
var result = storeData.StoreBlob == null ? new StoreBlob() : new Serializer(null).ToObject<StoreBlob>(Encoding.UTF8.GetString(storeData.StoreBlob));
|
||||
if (result.PreferredExchange == null)
|
||||
result.PreferredExchange = CoinGeckoRateProvider.CoinGeckoName;
|
||||
if (result.PaymentMethodCriteria is null)
|
||||
result.PaymentMethodCriteria = new List<PaymentMethodCriteria>();
|
||||
result.PaymentMethodCriteria.RemoveAll(criteria => criteria?.PaymentMethod is null);
|
||||
result.PaymentMethodCriteria.RemoveAll(criteria => criteria.PaymentMethod is null);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -27,12 +27,6 @@ namespace BTCPayServer.HostedServices
|
||||
public virtual Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_Tasks = InitializeTasks();
|
||||
foreach (var t in _Tasks)
|
||||
t.ContinueWith(t =>
|
||||
{
|
||||
if (t.IsFaulted)
|
||||
Logs.PayServer.LogWarning(t.Exception, $"Unhanded exception in {this.GetType().Name}");
|
||||
}, TaskScheduler.Default);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,6 @@ using BTCPayServer.Services.Mails;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using InvoiceData = BTCPayServer.Client.Models.InvoiceData;
|
||||
|
||||
namespace BTCPayServer.HostedServices;
|
||||
@ -93,7 +92,6 @@ public class StoreEmailRuleProcessorSender : EventHostedServiceBase
|
||||
.Replace("{Invoice.Price}", i.Amount.ToString(CultureInfo.InvariantCulture))
|
||||
.Replace("{Invoice.Currency}", i.Currency)
|
||||
.Replace("{Invoice.Status}", i.Status.ToString())
|
||||
.Replace("{Invoice.AdditionalStatus}", i.AdditionalStatus.ToString())
|
||||
.Replace("{Invoice.OrderId}", i.Metadata.ToObject<InvoiceMetadata>().OrderId);
|
||||
.Replace("{Invoice.AdditionalStatus}", i.AdditionalStatus.ToString());
|
||||
}
|
||||
}
|
||||
|
@ -134,6 +134,5 @@ namespace BTCPayServer.Models.InvoicingModels
|
||||
public bool CanMarkStatus => CanMarkSettled || CanMarkInvalid;
|
||||
public List<RefundData> Refunds { get; set; }
|
||||
public bool ShowReceipt { get; set; }
|
||||
public bool Overpaid { get; set; } = false;
|
||||
}
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
||||
[DomainMappingConstraint(AppType.PointOfSale)]
|
||||
[RateLimitsFilter(ZoneLimits.PublicInvoices, Scope = RateLimitsScope.RemoteAddress)]
|
||||
public async Task<IActionResult> ViewPointOfSale(string appId,
|
||||
PosViewType? viewType,
|
||||
PosViewType viewType,
|
||||
[ModelBinder(typeof(InvariantDecimalModelBinder))] decimal? amount,
|
||||
string email,
|
||||
string orderId,
|
||||
@ -129,14 +129,12 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
||||
return NotFound();
|
||||
var settings = app.GetSettings<PointOfSaleSettings>();
|
||||
settings.DefaultView = settings.EnableShoppingCart ? PosViewType.Cart : settings.DefaultView;
|
||||
var currentView = viewType ?? settings.DefaultView;
|
||||
if (string.IsNullOrEmpty(choiceKey) && !settings.ShowCustomAmount &&
|
||||
currentView != PosViewType.Cart && currentView != PosViewType.Light)
|
||||
if (string.IsNullOrEmpty(choiceKey) && !settings.ShowCustomAmount && settings.DefaultView != PosViewType.Cart)
|
||||
{
|
||||
return RedirectToAction(nameof(ViewPointOfSale), new { appId, viewType });
|
||||
}
|
||||
string title;
|
||||
decimal? price;
|
||||
string title = null;
|
||||
decimal? price = null;
|
||||
Dictionary<string, InvoiceSupportedTransactionCurrency> paymentMethods = null;
|
||||
ViewPointOfSaleViewModel.Item choice = null;
|
||||
if (!string.IsNullOrEmpty(choiceKey))
|
||||
@ -157,9 +155,12 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
||||
price = amount;
|
||||
}
|
||||
|
||||
if (choice.Inventory is <= 0)
|
||||
if (choice.Inventory.HasValue)
|
||||
{
|
||||
return RedirectToAction(nameof(ViewPointOfSale), new { appId });
|
||||
if (choice.Inventory <= 0)
|
||||
{
|
||||
return RedirectToAction(nameof(ViewPointOfSale), new { appId = appId });
|
||||
}
|
||||
}
|
||||
|
||||
if (choice?.PaymentMethods?.Any() is true)
|
||||
@ -170,16 +171,17 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!settings.ShowCustomAmount && currentView != PosViewType.Cart && currentView != PosViewType.Light)
|
||||
if (!settings.ShowCustomAmount && settings.DefaultView != PosViewType.Cart)
|
||||
return NotFound();
|
||||
|
||||
price = amount;
|
||||
title = settings.Title;
|
||||
|
||||
//if cart IS enabled and we detect posdata that matches the cart system's, check inventory for the items
|
||||
if (!string.IsNullOrEmpty(posData) && currentView == PosViewType.Cart &&
|
||||
if (!string.IsNullOrEmpty(posData) &&
|
||||
settings.DefaultView == PosViewType.Cart &&
|
||||
AppService.TryParsePosCartItems(posData, out var cartItems))
|
||||
{
|
||||
|
||||
var choices = _appService.GetPOSItems(settings.Template, settings.Currency);
|
||||
foreach (var cartItem in cartItems)
|
||||
{
|
||||
@ -203,7 +205,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
||||
var store = await _appService.GetStore(app);
|
||||
try
|
||||
{
|
||||
var invoice = await _invoiceController.CreateInvoiceCore(new BitpayCreateInvoiceRequest
|
||||
var invoice = await _invoiceController.CreateInvoiceCore(new BitpayCreateInvoiceRequest()
|
||||
{
|
||||
ItemCode = choice?.Id,
|
||||
ItemDesc = title,
|
||||
@ -225,7 +227,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
|
||||
? store.GetStoreBlob().RequiresRefundEmail
|
||||
: requiresRefundEmail == RequiresRefundEmail.On,
|
||||
}, store, HttpContext.Request.GetAbsoluteRoot(),
|
||||
new List<string> { AppService.GetAppInternalTag(appId) },
|
||||
new List<string>() { AppService.GetAppInternalTag(appId) },
|
||||
cancellationToken, (entity) =>
|
||||
{
|
||||
entity.Metadata.OrderUrl = Request.GetDisplayUrl();
|
||||
|
@ -15,7 +15,7 @@ namespace BTCPayServer.Services.Labels
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
static void FixLegacy(JObject jObj, ReferenceLabel refLabel)
|
||||
{
|
||||
if (refLabel.Reference is null && jObj.ContainsKey("id"))
|
||||
@ -29,12 +29,6 @@ namespace BTCPayServer.Services.Labels
|
||||
var pullPaymentId = jObj["pullPaymentId"]?.Value<string>() ?? string.Empty;
|
||||
payoutLabel.PullPaymentPayouts.Add(pullPaymentId, new List<string>() { jObj["id"].Value<string>() });
|
||||
}
|
||||
else if (jObj.ContainsKey("payoutId") && jObj.ContainsKey("pullPaymentId"))
|
||||
{
|
||||
var pullPaymentId = jObj["pullPaymentId"]?.Value<string>() ?? string.Empty;
|
||||
var payoutId = jObj["payoutId"]?.Value<string>() ?? string.Empty;
|
||||
payoutLabel.PullPaymentPayouts.Add(pullPaymentId, new List<string>() { payoutId });
|
||||
}
|
||||
FixLegacy(jObj, (Label)payoutLabel);
|
||||
}
|
||||
static void FixLegacy(JObject jObj, Label label)
|
||||
|
@ -13,37 +13,30 @@ namespace BTCPayServer.TagHelpers
|
||||
public CurrenciesSuggestionsTagHelper(CurrencyNameTable currencies)
|
||||
{
|
||||
_currencies = currencies;
|
||||
|
||||
}
|
||||
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
output.Attributes.RemoveAll("currency-selection");
|
||||
output.PostElement.AppendHtml("<datalist id=\"currency-selection-suggestion\">");
|
||||
var currencies = _currencies.Currencies
|
||||
.Where(c => !c.Crypto)
|
||||
.OrderBy(c => c.Code).ToList();
|
||||
// insert btc at the front
|
||||
output.PostElement.AppendHtml("<option value=\"BTC\">BTC - Bitcoin</option>");
|
||||
output.PostElement.AppendHtml("<option value=\"SATS\">SATS - Satoshi</option>");
|
||||
// move most often used currencies up
|
||||
var currencies = _currencies.Currencies.Where(c => !c.Crypto).Select(c => c.Code).OrderBy(c => c).ToList();
|
||||
int pos = 0;
|
||||
InsertAt(currencies, "BTC", pos++);
|
||||
InsertAt(currencies, "SATS", pos++);
|
||||
InsertAt(currencies, "USD", pos++);
|
||||
InsertAt(currencies, "EUR", pos++);
|
||||
InsertAt(currencies, "JPY", pos++);
|
||||
InsertAt(currencies, "CNY", pos++);
|
||||
// add options
|
||||
foreach (var c in currencies)
|
||||
foreach (var curr in currencies)
|
||||
{
|
||||
output.PostElement.AppendHtml($"<option value=\"{c.Code}\">{c.Code} - {c.Name}</option>");
|
||||
output.PostElement.AppendHtml($"<option value=\"{curr}\">");
|
||||
}
|
||||
output.PostElement.AppendHtml("</datalist>");
|
||||
output.Attributes.Add("list", "currency-selection-suggestion");
|
||||
base.Process(context, output);
|
||||
}
|
||||
|
||||
private void InsertAt(List<CurrencyData> currencies, string code, int idx)
|
||||
private void InsertAt(List<string> currencies, string curr, int idx)
|
||||
{
|
||||
var curr = currencies.FirstOrDefault(c => c.Code == code);
|
||||
currencies.Remove(curr);
|
||||
currencies.Insert(idx, curr);
|
||||
}
|
||||
|
@ -91,7 +91,7 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="TargetCurrency" class="form-label"></label>
|
||||
<input asp-for="TargetCurrency" class="form-control w-auto" currency-selection />
|
||||
<input asp-for="TargetCurrency" class="form-control" currency-selection style="max-width:10ch;" />
|
||||
<small class="d-inline-block form-text text-muted">Uses the store's default currency (@Model.StoreDefaultCurrency) if empty.</small>
|
||||
<span asp-validation-for="TargetCurrency" class="text-danger"></span>
|
||||
</div>
|
||||
|
@ -161,7 +161,7 @@
|
||||
</div>
|
||||
<div class="form-group col-md-4" v-if="!srvModel.appIdEndpoint">
|
||||
<label class="form-label" for="Currency">Currency</label>
|
||||
<input asp-for="Currency" name="currency" class="form-control w-auto" currency-selection
|
||||
<input asp-for="Currency" name="currency" currency-selection class="form-control"
|
||||
v-model="srvModel.currency" v-on:change="inputChanges"
|
||||
:class="{'is-invalid': errors.has('currency') }" />
|
||||
</div>
|
||||
|
@ -36,7 +36,7 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Currency" class="form-label"></label>
|
||||
<input asp-for="Currency" class="form-control w-auto" currency-selection />
|
||||
<input asp-for="Currency" class="form-control" currency-selection style="max-width:10ch;" />
|
||||
<small class="d-inline-block form-text text-muted">Uses the store's default currency (@Model.StoreDefaultCurrency) if empty.</small>
|
||||
<span asp-validation-for="Currency" class="text-danger"></span>
|
||||
</div>
|
||||
|
@ -2,37 +2,35 @@
|
||||
@inject BTCPayServer.Services.BTCPayServerEnvironment _env
|
||||
|
||||
<footer class="btcpay-footer">
|
||||
<div class="d-flex flex-wrap flex-column justify-content-between flex-xl-row gap-3">
|
||||
<div class="d-flex flex-wrap justify-content-center justify-content-xl-start gap-4">
|
||||
<a href="https://github.com/btcpayserver/btcpayserver" class="d-flex align-items-center" target="_blank" rel="noreferrer noopener">
|
||||
<vc:icon symbol="github"/>
|
||||
<span style="margin-left:.4rem">Github</span>
|
||||
</a>
|
||||
<a href="https://chat.btcpayserver.org/" class="d-flex align-items-center" target="_blank" rel="noreferrer noopener">
|
||||
<vc:icon symbol="mattermost"/>
|
||||
<span style="margin-left:.4rem">Mattermost</span>
|
||||
</a>
|
||||
<a href="https://twitter.com/BtcpayServer" class="d-flex align-items-center" target="_blank" rel="noreferrer noopener">
|
||||
<vc:icon symbol="twitter"/>
|
||||
<span style="margin-left:.4rem">Twitter</span>
|
||||
</a>
|
||||
<a href="https://t.me/btcpayserver" class="d-flex align-items-center" target="_blank" rel="noreferrer noopener">
|
||||
<vc:icon symbol="telegram"/>
|
||||
<span style="margin-left:.4rem">Telegram</span>
|
||||
</a>
|
||||
@if (!string.IsNullOrEmpty(_env.OnionUrl) && !Context.Request.IsOnion())
|
||||
{
|
||||
<a href="@_env.OnionUrl" class="d-flex align-items-center" target="_onion" rel="noreferrer noopener" role="button" data-clipboard="@_env.OnionUrl" style="min-width:9em;">
|
||||
<vc:icon symbol="onion"/>
|
||||
<span style="margin-left:.4rem" data-clipboard-confirm="Copied URL ✔">Copy Tor URL</span>
|
||||
<div class="container">
|
||||
<div class="d-flex flex-wrap flex-column justify-content-between flex-xl-row py-2">
|
||||
<div class="d-flex flex-wrap justify-content-center justify-content-xl-start mb-3 mb-xl-0">
|
||||
<a href="https://github.com/btcpayserver/btcpayserver" class="d-flex align-items-center m-3 m-sm-0 me-sm-4" target="_blank" rel="noreferrer noopener">
|
||||
<vc:icon symbol="github"/>
|
||||
<span style="margin-left:.4rem">Github</span>
|
||||
</a>
|
||||
<a href="https://chat.btcpayserver.org/" class="d-flex align-items-center m-3 m-sm-0 me-sm-4" target="_blank" rel="noreferrer noopener">
|
||||
<vc:icon symbol="mattermost"/>
|
||||
<span style="margin-left:.4rem">Mattermost</span>
|
||||
</a>
|
||||
<a href="https://twitter.com/BtcpayServer" class="d-flex align-items-center m-3 m-sm-0" target="_blank" rel="noreferrer noopener">
|
||||
<vc:icon symbol="twitter"/>
|
||||
<span style="margin-left:.4rem">Twitter</span>
|
||||
</a>
|
||||
@if (!string.IsNullOrEmpty(_env.OnionUrl) && !Context.Request.IsOnion())
|
||||
{
|
||||
<a href="@_env.OnionUrl" class="d-flex align-items-center m-3 m-sm-0 ms-sm-4" target="_onion" rel="noreferrer noopener" role="button" data-clipboard="@_env.OnionUrl" style="min-width:9em;">
|
||||
<vc:icon symbol="onion"/>
|
||||
<span style="margin-left:.4rem" data-clipboard-confirm="Copied URL ✔">Copy Tor URL</span>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
@if (User.Identity.IsAuthenticated)
|
||||
{
|
||||
<div class="text-center">
|
||||
@_env.ToString()
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@if (User.Identity.IsAuthenticated)
|
||||
{
|
||||
<div class="text-center">
|
||||
@_env.ToString()
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</footer>
|
||||
|
@ -8,8 +8,10 @@
|
||||
<partial name="LayoutHead" />
|
||||
@await RenderSectionAsync("PageHeadContent", false)
|
||||
<style>
|
||||
.btcpay-footer { max-width: 600px; margin-left: auto; margin-right: auto; }
|
||||
.btcpay-footer .flex-xl-row { flex-direction: column !important; }
|
||||
.btcpay-footer .justify-content-xl-start { justify-content: center !important; }
|
||||
.btcpay-footer .mb-xl-0 { margin-bottom: 0.5rem !important; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="d-flex flex-column min-vh-100">
|
||||
|
@ -49,7 +49,7 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Currency" class="form-label"></label>
|
||||
<input asp-for="Currency" class="form-control w-auto" currency-selection />
|
||||
<input asp-for="Currency" currency-selection class="form-control" />
|
||||
<span asp-validation-for="Currency" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -202,6 +202,11 @@
|
||||
else
|
||||
{
|
||||
@Model.State
|
||||
@if (Model.StatusException != InvoiceExceptionStatus.None)
|
||||
{
|
||||
@String.Format(" ({0})", Model.StatusException.ToString())
|
||||
;
|
||||
}
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -29,6 +29,8 @@
|
||||
});
|
||||
});
|
||||
|
||||
delegate('click', '#switchTimeFormat', switchTimeFormat)
|
||||
|
||||
delegate('click', '.changeInvoiceState', e => {
|
||||
const { invoiceId, newState } = e.target.dataset;
|
||||
const pavpill = $("#pavpill_" + invoiceId);
|
||||
@ -282,11 +284,11 @@
|
||||
<tr>
|
||||
<th style="width:2rem;" class="only-for-js">
|
||||
<input id="selectAllCheckbox" type="checkbox" class="form-check-input" />
|
||||
<th class="w-150px">
|
||||
<div class="d-flex align-items-center gap-1">
|
||||
Date
|
||||
<button type="button" class="btn btn-link p-0 fa fa-clock-o switch-time-format" title="Switch date format"></button>
|
||||
</div>
|
||||
<th style="min-width:90px;" class="col-md-auto">
|
||||
Date
|
||||
<a id="switchTimeFormat" href="#">
|
||||
<span class="fa fa-clock-o" title="Switch date format"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th class="text-nowrap">Order Id</th>
|
||||
<th class="text-nowrap">Invoice Id</th>
|
||||
@ -302,7 +304,11 @@
|
||||
<td class="only-for-js">
|
||||
<input name="selectedItems" type="checkbox" class="selector form-check-input" value="@invoice.InvoiceId" />
|
||||
</td>
|
||||
<td>@invoice.Date.ToBrowserDate()</td>
|
||||
<td>
|
||||
<span class="switchTimeFormat" data-switch="@invoice.Date.ToTimeAgo()">
|
||||
@invoice.Date.ToBrowserDate()
|
||||
</span>
|
||||
</td>
|
||||
<td style="max-width:120px;">
|
||||
@if (invoice.RedirectUrl != string.Empty)
|
||||
{
|
||||
|
@ -1,3 +1,4 @@
|
||||
@using BTCPayServer.Client.Models
|
||||
@model (InvoiceDetailsModel Invoice, bool ShowAddress)
|
||||
@{ var invoice = Model.Invoice; }
|
||||
|
||||
@ -13,7 +14,7 @@
|
||||
<th class="text-end">Rate</th>
|
||||
<th class="text-end">Paid</th>
|
||||
<th class="text-end">Due</th>
|
||||
@if (invoice.Overpaid)
|
||||
@if (invoice.StatusException == InvoiceExceptionStatus.PaidOver)
|
||||
{
|
||||
<th class="text-end">Overpaid</th>
|
||||
}
|
||||
@ -33,7 +34,7 @@
|
||||
<td class="text-end">@payment.Rate</td>
|
||||
<td class="text-end">@payment.Paid</td>
|
||||
<td class="text-end">@payment.Due</td>
|
||||
@if (invoice.Overpaid)
|
||||
@if (invoice.StatusException == InvoiceExceptionStatus.PaidOver)
|
||||
{
|
||||
<td class="text-end">@payment.Overpaid</td>
|
||||
}
|
||||
|
@ -1,15 +1,6 @@
|
||||
@model (Dictionary<string, object> Items, int Level)
|
||||
|
||||
@functions{
|
||||
public bool IsValidURL(string source)
|
||||
{
|
||||
Uri uriResult;
|
||||
return Uri.TryCreate(source, UriKind.Absolute, out uriResult) &&
|
||||
(uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps);
|
||||
}
|
||||
}
|
||||
|
||||
<table class="table my-0">
|
||||
<table class="table table-hover my-0">
|
||||
@foreach (var (key, value) in Model.Items)
|
||||
{
|
||||
<tr>
|
||||
@ -20,7 +11,7 @@
|
||||
<th class="w-150px">@Safe.Raw(key)</th>
|
||||
}
|
||||
<td>
|
||||
@if (IsValidURL(str))
|
||||
@if (Uri.IsWellFormedUriString(str, UriKind.RelativeOrAbsolute))
|
||||
{
|
||||
<a href="@Safe.Raw(str)" target="_blank" rel="noreferrer noopener">@Safe.Raw(str)</a>
|
||||
}
|
||||
@ -37,7 +28,7 @@
|
||||
{
|
||||
<th class="w-150px">@Safe.Raw(key)</th>
|
||||
<td>
|
||||
@if (IsValidURL(str2))
|
||||
@if (Uri.IsWellFormedUriString(str2, UriKind.RelativeOrAbsolute))
|
||||
{
|
||||
<a href="@Safe.Raw(str2)" target="_blank" rel="noreferrer noopener">@Safe.Raw(str2)</a>
|
||||
}
|
||||
|
@ -77,7 +77,7 @@
|
||||
<label asp-for="CustomAmount" class="form-label"></label>
|
||||
<div class="input-group">
|
||||
<input asp-for="CustomAmount" type="number" step="any" asp-format="{0}" class="form-control"/>
|
||||
<input asp-for="CustomCurrency" type="text" class="form-control w-auto" currency-selection />
|
||||
<input asp-for="CustomCurrency" type="text" class="form-control" currency-selection style="max-width:10ch;"/>
|
||||
</div>
|
||||
<span asp-validation-for="CustomAmount" class="text-danger w-100"></span>
|
||||
<span asp-validation-for="CustomCurrency" class="text-danger w-100"></span>
|
||||
|
@ -33,17 +33,17 @@
|
||||
<table class="table table-hover table-responsive-md">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:30px" class="only-for-js">
|
||||
<th width="30px" class="only-for-js">
|
||||
@if (Model.Items.Count > 0)
|
||||
{
|
||||
<input name="selectedItems" id="selectAllCheckbox" type="checkbox" class="form-check-input" />
|
||||
}
|
||||
</th>
|
||||
<th class="w-150px">
|
||||
<div class="d-flex align-items-center gap-1">
|
||||
Date
|
||||
<button type="button" class="btn btn-link p-0 fa fa-clock-o switch-time-format" title="Switch date format"></button>
|
||||
</div>
|
||||
<th width="190px">
|
||||
Date
|
||||
<a href="#" id="switchTimeFormat">
|
||||
<span class="fa fa-clock-o" title="Switch date format"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th>Message</th>
|
||||
<th class="text-end">Actions</th>
|
||||
@ -56,7 +56,11 @@
|
||||
<td class="only-for-js">
|
||||
<input name="selectedItems" type="checkbox" class="selector form-check-input" value="@item.Id" />
|
||||
</td>
|
||||
<td class="toggleRowCheckbox">@item.Created.ToBrowserDate()</td>
|
||||
<td class="toggleRowCheckbox">
|
||||
<span class="switchTimeFormat" data-switch="@item.Created.ToTimeAgo()">
|
||||
@item.Created.ToBrowserDate()
|
||||
</span>
|
||||
</td>
|
||||
<td class="toggleRowCheckbox">
|
||||
@item.Body
|
||||
</td>
|
||||
@ -126,6 +130,8 @@ else
|
||||
updateSelectors();
|
||||
})
|
||||
|
||||
delegate('click', '#switchTimeFormat', switchTimeFormat)
|
||||
|
||||
delegate('click', '.btn-toggle-seen', e => {
|
||||
const row = $(e.target).parents(".notification-row").toggleClass("loading");
|
||||
const guid = row.data("guid");
|
||||
|
@ -49,7 +49,7 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Currency" class="form-label"></label>
|
||||
<input asp-for="Currency" class="form-control w-auto" currency-selection />
|
||||
<input asp-for="Currency" currency-selection class="form-control"/>
|
||||
<span asp-validation-for="Currency" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -62,6 +62,7 @@
|
||||
<td>@file.ApplicationUser.UserName</td>
|
||||
<td class="text-end">
|
||||
<a href="@Url.Action("Files", "UIServer", new { fileIds = new string[] { file.Id } })">Get Link</a>
|
||||
- <a asp-action="CreateTemporaryFileUrl" asp-route-fileId="@file.Id">Get Temp Link</a>
|
||||
- <a asp-action="DeleteFile" asp-route-fileId="@file.Id">Remove</a>
|
||||
</td>
|
||||
</tr>
|
||||
@ -82,6 +83,7 @@
|
||||
foreach (KeyValuePair<string, string> fileUrlPair in Model.DirectUrlByFiles)
|
||||
{
|
||||
var fileId = fileUrlPair.Key;
|
||||
var fileUrl = fileUrlPair.Value;
|
||||
var file = Model.Files.Single(storedFile => storedFile.Id.Equals(fileId, StringComparison.InvariantCultureIgnoreCase));
|
||||
|
||||
<div class="card mb-2">
|
||||
@ -99,6 +101,10 @@
|
||||
}, Context.Request.Scheme, Context.Request.Host.ToString())
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<strong>Direct URL:</strong>
|
||||
<a href="@fileUrl" target="_blank" rel="noreferrer noopener">@fileUrl</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -38,7 +38,7 @@
|
||||
</div>
|
||||
<div class="form-group col-4">
|
||||
<label asp-for="Currency" class="form-label"></label>
|
||||
<input asp-for="Currency" class="form-control w-auto" currency-selection />
|
||||
<input asp-for="Currency" currency-selection class="form-control"/>
|
||||
<span asp-validation-for="Currency" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
|
@ -31,7 +31,7 @@
|
||||
<h3 class="mt-5 mb-3">Payment</h3>
|
||||
<div class="form-group">
|
||||
<label asp-for="DefaultCurrency" class="form-label"></label>
|
||||
<input asp-for="DefaultCurrency" class="form-control w-auto" currency-selection />
|
||||
<input asp-for="DefaultCurrency" class="form-control" currency-selection style="max-width:10ch;" />
|
||||
<span asp-validation-for="DefaultCurrency" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group d-flex align-items-center">
|
||||
|
@ -21,7 +21,7 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="DefaultCurrency" class="form-label" data-required></label>
|
||||
<input asp-for="DefaultCurrency" class="form-control w-auto" currency-selection />
|
||||
<input asp-for="DefaultCurrency" class="form-control" currency-selection style="max-width:10ch;" />
|
||||
<span asp-validation-for="DefaultCurrency" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
@ -1,9 +1,7 @@
|
||||
@model ListTransactionsViewModel
|
||||
|
||||
@{
|
||||
var walletId = Context.GetRouteValue("walletId").ToString();
|
||||
var labelFilter = Context.Request.Query["labelFilter"].ToString();
|
||||
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(WalletsNavPages.Transactions, $"{Model.CryptoCode} Transactions", walletId);
|
||||
}
|
||||
@ -19,23 +17,31 @@
|
||||
max-width: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* pull actions area, so that it is besides the search form */
|
||||
@@media (min-width: 1200px) {
|
||||
#Filter + #Dropdowns {
|
||||
margin-top: -4rem;
|
||||
float: right;
|
||||
}
|
||||
|
||||
|
||||
#Filter + #Dropdowns #Actions {
|
||||
order: 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.unconf > * {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.switchTimeFormat {
|
||||
display: block;
|
||||
max-width: 125px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.badge-container {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
@ -54,20 +60,16 @@
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
|
||||
.label-tooltip .tooltip-inner {
|
||||
max-width: 15rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
|
||||
.label-tooltip ul {
|
||||
margin: 0;
|
||||
padding-left: var(--btcpay-space-m);
|
||||
}
|
||||
|
||||
#LoadingIndicator {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
</style>
|
||||
}
|
||||
|
||||
@ -77,81 +79,12 @@
|
||||
|
||||
@* Custom Range Modal *@
|
||||
<script>
|
||||
let observer = null;
|
||||
let $loadMore = document.getElementById('LoadMore');
|
||||
const $actions = document.getElementById('ListActions');
|
||||
const $list = document.getElementById('WalletTransactionsList');
|
||||
const $indicator = document.getElementById('LoadingIndicator');
|
||||
|
||||
delegate('click', '#selectAllCheckbox', e => {
|
||||
delegate('click', '#switchTimeFormat', switchTimeFormat);
|
||||
delegate('click', '#selectAllCheckbox', e => {
|
||||
document.querySelectorAll(".selector").forEach(checkbox => {
|
||||
checkbox.checked = e.target.checked;
|
||||
});
|
||||
});
|
||||
|
||||
delegate('click', '#GoToTop', () => {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
});
|
||||
|
||||
delegate('click', '#LoadMore', async () => {
|
||||
$loadMore.setAttribute('disabled', 'disabled');
|
||||
await loadMoreTransactions();
|
||||
});
|
||||
|
||||
if ($actions && $actions.offsetTop - window.innerHeight > 0) {
|
||||
document.getElementById('GoToTop').classList.remove('d-none');
|
||||
}
|
||||
|
||||
const count = @Safe.Json(Model.Count);
|
||||
const skipInitial = @Safe.Json(Model.Skip);
|
||||
const loadMoreUrl = @Safe.Json(Url.Action("WalletTransactions", new { walletId, labelFilter, skip = Model.Skip, count = Model.Count }));
|
||||
let skip = @Safe.Json(Model.Skip);
|
||||
|
||||
async function loadMoreTransactions() {
|
||||
$indicator.classList.remove('d-none');
|
||||
|
||||
const skipNext = skip + count;
|
||||
const url = loadMoreUrl.replace(`skip=${skipInitial}`, `skip=${skipNext}`)
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
'Accept': 'text/html',
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const html = await response.text();
|
||||
$list.innerHTML += html;
|
||||
skip = skipNext;
|
||||
|
||||
if ($loadMore) {
|
||||
// remove load more button
|
||||
$loadMore.remove();
|
||||
$loadMore = null;
|
||||
|
||||
// switch to infinite scroll mode
|
||||
observer = new IntersectionObserver(async entries => {
|
||||
const { isIntersecting } = entries[0];
|
||||
if (isIntersecting) {
|
||||
await loadMoreTransactions();
|
||||
}
|
||||
}, { rootMargin: '128px' });
|
||||
|
||||
// the actions div marks the end of the list table
|
||||
observer.observe($actions);
|
||||
}
|
||||
|
||||
if (html.trim() === '') {
|
||||
// in case the response html was empty, remove the observer and stop loading
|
||||
observer.unobserve($actions);
|
||||
}
|
||||
} else if ($loadMore) {
|
||||
$loadMore.removeAttribute('disabled');
|
||||
}
|
||||
|
||||
$indicator.classList.add('d-none');
|
||||
formatDateTimes(document.getElementById('switchTimeFormat').dataset.mode);
|
||||
}
|
||||
</script>
|
||||
}
|
||||
|
||||
@ -182,8 +115,8 @@
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-end" aria-labelledby="ActionsDropdownToggle">
|
||||
<form id="WalletActions" method="post" asp-action="WalletActions" asp-route-walletId="@walletId">
|
||||
<button id="BumpFee" name="command" type="submit" class="dropdown-item" value="cpfp">Bump fee (CPFP)</button>
|
||||
</form>
|
||||
<button id="BumpFee" name="command" type="submit" class="dropdown-item" value="cpfp">Bump fee (CPFP)</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dropdown d-inline-flex align-items-center gap-3" id="Export">
|
||||
@ -197,20 +130,20 @@
|
||||
</div>
|
||||
</div>
|
||||
<div style="clear:both"></div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col table-responsive-md" id="walletTable">
|
||||
<div class="col table-responsive-md">
|
||||
<table class="table table-hover">
|
||||
<thead class="thead-inverse">
|
||||
<tr>
|
||||
<th style="width:2rem;" class="only-for-js">
|
||||
<input id="selectAllCheckbox" type="checkbox" class="form-check-input" />
|
||||
<input id="selectAllCheckbox" type="checkbox" class="form-check-input"/>
|
||||
</th>
|
||||
<th class="w-150px">
|
||||
<div class="d-flex align-items-center gap-1">
|
||||
Date
|
||||
<button type="button" class="btn btn-link p-0 fa fa-clock-o switch-time-format" title="Switch date format" id="switchTimeFormat"></button>
|
||||
</div>
|
||||
<th style="max-width:125px;">
|
||||
Date
|
||||
<a id="switchTimeFormat" href="#">
|
||||
<span class="fa fa-clock-o" title="Switch date format"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th class="text-start">Label</th>
|
||||
<th>Transaction Id</th>
|
||||
@ -218,27 +151,136 @@
|
||||
<th class="text-end" style="min-width:60px"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="WalletTransactionsList">
|
||||
<partial name="_WalletTransactionsList" model="Model" />
|
||||
<tbody>
|
||||
@foreach (var transaction in Model.Transactions)
|
||||
{
|
||||
<tr>
|
||||
<td class="only-for-js">
|
||||
<input name="selectedTransactions" type="checkbox" class="selector form-check-input" form="WalletActions" value="@transaction.Id"/>
|
||||
</td>
|
||||
<td>
|
||||
<span class="switchTimeFormat" data-switch="@transaction.Timestamp.ToTimeAgo()">
|
||||
@transaction.Timestamp.ToBrowserDate()
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-start">
|
||||
@if (transaction.Labels.Any())
|
||||
{
|
||||
<div class="d-flex flex-wrap gap-2 align-items-center">
|
||||
@foreach (var label in transaction.Labels)
|
||||
{
|
||||
<div class="badge-container rounded-2" style="background-color:@label.Color;">
|
||||
<div
|
||||
class="badge transactionLabel position-relative d-block"
|
||||
style="background-color:@label.Color;padding-right: 16px; z-index: 1;"
|
||||
data-bs-html="true"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-custom-class="label-tooltip"
|
||||
title="@label.Tooltip">
|
||||
<a asp-route-labelFilter="@label.Text" style="color:@label.TextColor !important;">@label.Text</a>
|
||||
|
||||
<form
|
||||
asp-route-walletId="@Context.GetRouteValue("walletId")"
|
||||
asp-action="ModifyTransaction"
|
||||
method="post"
|
||||
class="removeTransactionLabelForm">
|
||||
<input type="hidden" name="transactionId" value="@transaction.Id"/>
|
||||
<button
|
||||
name="removelabel"
|
||||
style="color: @label.Color; filter: brightness(0.5);"
|
||||
type="submit"
|
||||
value="@label.Text">
|
||||
<span class="fa fa-close"></span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
@if (!string.IsNullOrEmpty(label.Link))
|
||||
{
|
||||
<a href="@label.Link" target="_blank" class="badge transaction-details-icon" style="background-color: @label.Color; filter: brightness(1.1);" rel="noreferrer noopener">
|
||||
<span class="fa fa-info-circle" title="Transaction details" style="color: @label.Color; filter: brightness(0.5);">
|
||||
<span class="visually-hidden">Transaction details</span>
|
||||
</span>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</td>
|
||||
<td class="smMaxWidth text-truncate @(transaction.IsConfirmed ? "" : "unconf")">
|
||||
<a href="@transaction.Link" target="_blank" rel="noreferrer noopener">
|
||||
@transaction.Id
|
||||
</a>
|
||||
</td>
|
||||
@if (transaction.Positive)
|
||||
{
|
||||
<td class="text-end text-success">@transaction.Balance</td>
|
||||
}
|
||||
else
|
||||
{
|
||||
<td class="text-end text-danger">@transaction.Balance</td>
|
||||
}
|
||||
<td class="text-end">
|
||||
<div class="dropstart d-inline-block me-2">
|
||||
<span class="fa fa-tags cursor-pointer" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
|
||||
<div class="dropdown-menu">
|
||||
<form asp-action="ModifyTransaction" method="post" style="width:260px;"
|
||||
asp-route-walletId="@Context.GetRouteValue("walletId")">
|
||||
<input type="hidden" name="transactionId" value="@transaction.Id"/>
|
||||
<div class="input-group input-group-sm p-2">
|
||||
<input name="addlabel" placeholder="Label name" maxlength="20" type="text" class="form-control form-control-sm"/>
|
||||
<button type="submit" class="btn btn-primary btn-sm"><span class="fa fa-plus"></span></button>
|
||||
</div>
|
||||
@if (Model.Labels.Count > 0)
|
||||
{
|
||||
<div class="dropdown-divider"></div>
|
||||
<div class="py-2 px-3 d-flex flex-wrap gap-2">
|
||||
@foreach (var label in Model.Labels)
|
||||
{
|
||||
@if (transaction.Labels.Contains(label))
|
||||
{
|
||||
<button name="removelabel" class="bg-transparent border-0 p-0" type="submit" value="@label.Text"><span class="badge" style="background-color:@label.Color;color:@label.TextColor"><span class="fa fa-check"></span> @label.Text</span></button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button name="addlabelclick" class="bg-transparent border-0 p-0" type="submit" value="@label.Text"><span class="badge" style="background-color:@label.Color;color:@label.TextColor">@label.Text</span></button>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dropstart d-inline-block">
|
||||
@if (string.IsNullOrEmpty(transaction.Comment))
|
||||
{
|
||||
<span class="fa fa-comment cursor-pointer" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="fa fa-commenting cursor-pointer" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
|
||||
}
|
||||
<div class="dropdown-menu">
|
||||
<form asp-action="ModifyTransaction" method="post" style="width:260px;"
|
||||
asp-route-walletId="@Context.GetRouteValue("walletId")">
|
||||
<input type="hidden" name="transactionId" value="@transaction.Id"/>
|
||||
<div class="input-group p-2">
|
||||
<textarea name="addcomment" maxlength="200" rows="2" cols="20" class="form-control form-control-sm p-1">@transaction.Comment</textarea>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<button type="submit" class="btn btn-primary btn-sm w-100">Save comment</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<noscript>
|
||||
<vc:pager view-model="Model"/>
|
||||
</noscript>
|
||||
|
||||
<div class="text-center only-for-js d-none" id="LoadingIndicator">
|
||||
<div class="spinner-border spinner-border-sm text-secondary ms-2" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex flex-wrap align-items-center justify-content-center gap-3 mb-5 only-for-js" id="ListActions">
|
||||
<button type="button" class="btn btn-secondary d-flex align-items-center" id="LoadMore">
|
||||
Load more
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary d-none" id="GoToTop">Go to top</button>
|
||||
</div>
|
||||
<vc:pager view-model="Model"/>
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -249,6 +291,6 @@ else
|
||||
|
||||
<p class="mt-4 mb-0">
|
||||
If BTCPay Server shows you an invalid balance, <a asp-action="WalletRescan" asp-route-walletId="@Context.GetRouteValue("walletId")">rescan your wallet</a>.
|
||||
<br />
|
||||
<br/>
|
||||
If some transactions appear in BTCPay Server, but are missing in another wallet, <a href="https://docs.btcpayserver.org/FAQ/Wallet/#missing-payments-in-my-software-or-hardware-wallet" rel="noreferrer noopener">follow these instructions</a>.
|
||||
</p>
|
||||
|
@ -1,119 +0,0 @@
|
||||
@model ListTransactionsViewModel
|
||||
|
||||
@foreach (var transaction in Model.Transactions)
|
||||
{
|
||||
<tr>
|
||||
<td class="only-for-js">
|
||||
<input name="selectedTransactions" type="checkbox" class="selector form-check-input" form="WalletActions" value="@transaction.Id" />
|
||||
</td>
|
||||
<td>
|
||||
@transaction.Timestamp.ToBrowserDate()
|
||||
</td>
|
||||
<td class="text-start">
|
||||
@if (transaction.Labels.Any())
|
||||
{
|
||||
<div class="d-flex flex-wrap gap-2 align-items-center">
|
||||
@foreach (var label in transaction.Labels)
|
||||
{
|
||||
<div class="badge-container rounded-2" style="background-color:@label.Color;">
|
||||
<div class="badge transactionLabel position-relative d-block"
|
||||
style="background-color:@label.Color;padding-right: 16px; z-index: 1;"
|
||||
data-bs-html="true"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-custom-class="label-tooltip"
|
||||
title="@label.Tooltip">
|
||||
<a asp-route-labelFilter="@label.Text" style="color:@label.TextColor !important;">@label.Text</a>
|
||||
|
||||
<form asp-route-walletId="@Context.GetRouteValue("walletId")"
|
||||
asp-action="ModifyTransaction"
|
||||
method="post"
|
||||
class="removeTransactionLabelForm">
|
||||
<input type="hidden" name="transactionId" value="@transaction.Id" />
|
||||
<button name="removelabel"
|
||||
style="color: @label.Color; filter: brightness(0.5);"
|
||||
type="submit"
|
||||
value="@label.Text">
|
||||
<span class="fa fa-close"></span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
@if (!string.IsNullOrEmpty(label.Link))
|
||||
{
|
||||
<a href="@label.Link" target="_blank" class="badge transaction-details-icon" style="background-color: @label.Color; filter: brightness(1.1);" rel="noreferrer noopener">
|
||||
<span class="fa fa-info-circle" title="Transaction details" style="color: @label.Color; filter: brightness(0.5);">
|
||||
<span class="visually-hidden">Transaction details</span>
|
||||
</span>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</td>
|
||||
<td class="smMaxWidth text-truncate @(transaction.IsConfirmed ? "" : "unconf")">
|
||||
<a href="@transaction.Link" target="_blank" rel="noreferrer noopener">
|
||||
@transaction.Id
|
||||
</a>
|
||||
</td>
|
||||
@if (transaction.Positive)
|
||||
{
|
||||
<td class="text-end text-success">@transaction.Balance</td>
|
||||
}
|
||||
else
|
||||
{
|
||||
<td class="text-end text-danger">@transaction.Balance</td>
|
||||
}
|
||||
<td class="text-end">
|
||||
<div class="dropstart d-inline-block me-2">
|
||||
<span class="fa fa-tags cursor-pointer" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
|
||||
<div class="dropdown-menu">
|
||||
<form asp-action="ModifyTransaction" method="post" style="width:260px;" asp-route-walletId="@Context.GetRouteValue("walletId")">
|
||||
<input type="hidden" name="transactionId" value="@transaction.Id" />
|
||||
<div class="input-group input-group-sm p-2">
|
||||
<input name="addlabel" placeholder="Label name" maxlength="20" type="text" class="form-control form-control-sm" />
|
||||
<button type="submit" class="btn btn-primary btn-sm"><span class="fa fa-plus"></span></button>
|
||||
</div>
|
||||
@if (Model.Labels.Count > 0)
|
||||
{
|
||||
<div class="dropdown-divider"></div>
|
||||
<div class="py-2 px-3 d-flex flex-wrap gap-2">
|
||||
@foreach (var label in Model.Labels)
|
||||
{
|
||||
@if (transaction.Labels.Contains(label))
|
||||
{
|
||||
<button name="removelabel" class="bg-transparent border-0 p-0" type="submit" value="@label.Text"><span class="badge" style="background-color:@label.Color;color:@label.TextColor"><span class="fa fa-check"></span> @label.Text</span></button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button name="addlabelclick" class="bg-transparent border-0 p-0" type="submit" value="@label.Text"><span class="badge" style="background-color:@label.Color;color:@label.TextColor">@label.Text</span></button>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dropstart d-inline-block">
|
||||
@if (string.IsNullOrEmpty(transaction.Comment))
|
||||
{
|
||||
<span class="fa fa-comment cursor-pointer" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="fa fa-commenting cursor-pointer" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
|
||||
}
|
||||
<div class="dropdown-menu">
|
||||
<form asp-action="ModifyTransaction" method="post" style="width:260px;" asp-route-walletId="@Context.GetRouteValue("walletId")">
|
||||
<input type="hidden" name="transactionId" value="@transaction.Id" />
|
||||
<div class="input-group p-2">
|
||||
<textarea name="addcomment" maxlength="200" rows="2" cols="20" class="form-control form-control-sm p-1">@transaction.Comment</textarea>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<button type="submit" class="btn btn-primary btn-sm w-100">Save comment</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
@ -19,7 +19,6 @@
|
||||
<symbol id="warning" viewBox="0 0 24 24"><path d="M12.337 3.101a.383.383 0 00-.674 0l-9.32 17.434a.383.383 0 00.338.564h18.638a.384.384 0 00.337-.564L12.337 3.101zM9.636 2.018c1.01-1.89 3.719-1.89 4.728 0l9.32 17.434a2.681 2.681 0 01-2.365 3.945H2.681a2.68 2.68 0 01-2.364-3.945L9.636 2.018zm3.896 15.25a1.532 1.532 0 11-3.064 0 1.532 1.532 0 013.064 0zm-.383-8.044a1.15 1.15 0 00-2.298 0v3.83a1.15 1.15 0 002.298 0v-3.83z" fill="currentColor"/></symbol>
|
||||
<symbol id="github" viewBox="0 0 25 24"><path clip-rule="evenodd" d="M12.75.3c-6.6 0-12 5.4-12 12 0 5.325 3.45 9.825 8.175 11.4.6.075.825-.225.825-.6v-2.025C6.375 21.825 5.7 19.5 5.7 19.5c-.525-1.35-1.35-1.725-1.35-1.725-1.125-.75.075-.75.075-.75 1.2.075 1.875 1.2 1.875 1.2 1.05 1.8 2.775 1.275 3.525.975a2.59 2.59 0 0 1 .75-1.575c-2.7-.3-5.475-1.35-5.475-5.925 0-1.275.45-2.4 1.2-3.225-.15-.3-.525-1.5.15-3.15 0 0 .975-.3 3.3 1.2.975-.3 1.95-.375 3-.375s2.025.15 3 .375c2.325-1.575 3.3-1.275 3.3-1.275.675 1.65.225 2.85.15 3.15.75.825 1.2 1.875 1.2 3.225 0 4.575-2.775 5.625-5.475 5.925.45.375.825 1.125.825 2.25v3.3c0 .3.225.675.825.6a12.015 12.015 0 0 0 8.175-11.4c0-6.6-5.4-12-12-12z" fill="currentColor" fill-rule="evenodd"/></symbol>
|
||||
<symbol id="twitter" viewBox="0 0 37 37"><path d="M36 18c0 9.945-8.055 18-18 18S0 27.945 0 18 8.055 0 18 0s18 8.055 18 18zm-21.294 9.495c7.983 0 12.348-6.615 12.348-12.348 0-.189 0-.378-.009-.558a8.891 8.891 0 0 0 2.169-2.25 8.808 8.808 0 0 1-2.493.684 4.337 4.337 0 0 0 1.908-2.403 8.788 8.788 0 0 1-2.754 1.053 4.319 4.319 0 0 0-3.168-1.368 4.34 4.34 0 0 0-4.338 4.338c0 .342.036.675.117.99a12.311 12.311 0 0 1-8.946-4.536 4.353 4.353 0 0 0-.585 2.178 4.32 4.32 0 0 0 1.935 3.609 4.263 4.263 0 0 1-1.962-.54v.054a4.345 4.345 0 0 0 3.483 4.257 4.326 4.326 0 0 1-1.962.072 4.333 4.333 0 0 0 4.05 3.015 8.724 8.724 0 0 1-6.426 1.791 12.091 12.091 0 0 0 6.633 1.962z" fill="currentColor"/></symbol>
|
||||
<symbol id="telegram" viewBox="0 0 496 512" fill="currentColor"><path d="M248,8C111.033,8,0,119.033,0,256S111.033,504,248,504,496,392.967,496,256,384.967,8,248,8ZM362.952,176.66c-3.732,39.215-19.881,134.378-28.1,178.3-3.476,18.584-10.322,24.816-16.948,25.425-14.4,1.326-25.338-9.517-39.287-18.661-21.827-14.308-34.158-23.215-55.346-37.177-24.485-16.135-8.612-25,5.342-39.5,3.652-3.793,67.107-61.51,68.335-66.746.153-.655.3-3.1-1.154-4.384s-3.59-.849-5.135-.5q-3.283.746-104.608,69.142-14.845,10.194-26.894,9.934c-8.855-.191-25.888-5.006-38.551-9.123-15.531-5.048-27.875-7.717-26.8-16.291q.84-6.7,18.45-13.7,108.446-47.248,144.628-62.3c68.872-28.647,83.183-33.623,92.511-33.789,2.052-.034,6.639.474,9.61,2.885a10.452,10.452,0,0,1,3.53,6.716A43.765,43.765,0,0,1,362.952,176.66Z"/></symbol>
|
||||
<symbol id="mattermost" viewBox="0 0 206 206"><path fill="currentColor" d="m163.012 19.596 1.082 21.794c17.667 19.519 24.641 47.161 15.846 73.14-13.129 38.782-56.419 59.169-96.693 45.535-40.272-13.633-62.278-56.124-49.15-94.905 8.825-26.066 31.275-43.822 57.276-48.524L105.422.038C61.592-1.15 20.242 26.056 5.448 69.76c-18.178 53.697 10.616 111.963 64.314 130.142 53.698 18.178 111.964-10.617 130.143-64.315 14.77-43.633-1.474-90.283-36.893-115.99"/><path fill="currentColor" d="m137.097 53.436-.596-17.531-.404-15.189s.084-7.322-.17-9.043a2.776 2.776 0 0 0-.305-.914l-.05-.109-.06-.094a2.378 2.378 0 0 0-1.293-1.07 2.382 2.382 0 0 0-1.714.078l-.033.014-.18.092a2.821 2.821 0 0 0-.75.518c-1.25 1.212-5.63 7.08-5.63 7.08l-9.547 11.82-11.123 13.563-19.098 23.75s-8.763 10.938-6.827 24.4c1.937 13.464 11.946 20.022 19.71 22.65 7.765 2.63 19.7 3.5 29.417-6.019 9.716-9.518 9.397-23.53 9.397-23.53l-.744-30.466z"/></symbol>
|
||||
<symbol id="notifications" viewBox="0 0 24 24"><path d="M12.1933 0.992188C17.152 0.992188 19.2346 5.35582 19.7305 7.04178C20.3255 9.12442 19.8297 10.017 20.3255 12.893C20.6231 14.7773 21.6148 16.3641 22.4082 17.2567C22.7057 17.5542 22.4082 18.05 22.0115 18.05H13.2842H12.1933H2.07762C1.68092 18.05 1.3834 17.5542 1.68092 17.2567C2.37514 16.3641 3.46605 14.7773 3.76357 12.893C4.16026 10.017 3.76357 9.12442 4.35861 7.04178C4.85448 5.35582 7.03629 0.992188 12.1933 0.992188Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" /><path d="M16.2595 18.0488C16.2595 18.2472 16.3586 18.5447 16.3586 18.743C16.3586 21.1232 14.4743 23.0075 12.0942 23.0075C9.71401 23.0075 7.82971 21.1232 7.82971 18.743C7.82971 18.5447 7.82971 18.3463 7.82971 18.148" fill="none" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" /></symbol>
|
||||
<symbol id="crowdfund" viewBox="0 0 24 24"><path d="M9.1638 12.4922C11.339 12.4922 13.1023 10.7288 13.1023 8.5537C13.1023 6.37854 11.339 4.61523 9.1638 4.61523C6.98865 4.61523 5.22534 6.37854 5.22534 8.5537C5.22534 10.7288 6.98865 12.4922 9.1638 12.4922Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M15.9331 18.0307C17.4965 18.0307 18.7638 16.7633 18.7638 15.1999C18.7638 13.6365 17.4965 12.3691 15.9331 12.3691C14.3697 12.3691 13.1023 13.6365 13.1023 15.1999C13.1023 16.7633 14.3697 18.0307 15.9331 18.0307Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M8.24067 19.3839C9.49818 19.3839 10.5176 18.3645 10.5176 17.107C10.5176 15.8495 9.49818 14.8301 8.24067 14.8301C6.98316 14.8301 5.96375 15.8495 5.96375 17.107C5.96375 18.3645 6.98316 19.3839 8.24067 19.3839Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></symbol>
|
||||
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 34 KiB |
@ -372,7 +372,7 @@
|
||||
.btcpay-footer {
|
||||
font-size: var(--btcpay-font-size-s);
|
||||
overflow: hidden;
|
||||
padding: 0 var(--content-padding-horizontal) 1rem;
|
||||
padding: .5em 0;
|
||||
color: var(--btcpay-footer-text);
|
||||
background: var(--btcpay-footer-bg);
|
||||
}
|
||||
@ -613,10 +613,16 @@
|
||||
max-width: calc(100vw - var(--sidebar-width) - (2 * var(--btcpay-space-xl)) - 1rem); /* 1rem for scrollbar */
|
||||
}
|
||||
|
||||
#mainContent > section {
|
||||
#mainContent > section,
|
||||
.btcpay-footer > .container {
|
||||
margin: 0;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.btcpay-footer {
|
||||
padding-left: var(--btcpay-space-xl);
|
||||
padding-right: var(--btcpay-space-xl);
|
||||
}
|
||||
|
||||
#SectionNav .nav {
|
||||
margin-top: calc(var(--btcpay-space-m) * -1);
|
||||
|
@ -1,30 +1,6 @@
|
||||
const flatpickrInstances = [];
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat
|
||||
const dtFormatOpts = { dateStyle: 'short', timeStyle: 'short' };
|
||||
|
||||
const formatDateTimes = format => {
|
||||
// select only elements which haven't been initialized before, those without data-localized
|
||||
document.querySelectorAll("time[datetime]:not([data-localized])").forEach($el => {
|
||||
const date = new Date($el.getAttribute("datetime"));
|
||||
// initialize and set localized attribute
|
||||
$el.dataset.localized = new Intl.DateTimeFormat('default', dtFormatOpts).format(date);
|
||||
// set text to chosen mode
|
||||
const mode = format || $el.dataset.initial;
|
||||
if ($el.dataset[mode]) $el.innerText = $el.dataset[mode];
|
||||
});
|
||||
};
|
||||
|
||||
const switchTimeFormat = event => {
|
||||
const curr = event.target.dataset.mode || 'localized';
|
||||
const mode = curr === 'relative' ? 'localized' : 'relative';
|
||||
document.querySelectorAll("time[datetime]").forEach($el => {
|
||||
$el.innerText = $el.dataset[mode];
|
||||
});
|
||||
event.target.dataset.mode = mode;
|
||||
};
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
// sticky header
|
||||
const stickyHeader = document.querySelector('.sticky-header-setup + .sticky-header');
|
||||
if (stickyHeader) {
|
||||
@ -36,7 +12,13 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
$("#TimezoneOffset").val(timezoneOffset);
|
||||
|
||||
// localize all elements that have localizeDate class
|
||||
formatDateTimes();
|
||||
$(".localizeDate").each(function (index) {
|
||||
var serverDate = $(this).text();
|
||||
var localDate = new Date(serverDate);
|
||||
|
||||
var dateString = localDate.toLocaleDateString() + " " + localDate.toLocaleTimeString();
|
||||
$(this).text(dateString);
|
||||
});
|
||||
|
||||
function updateTimeAgo(){
|
||||
var timeagoElements = $("[data-timeago-unixms]");
|
||||
@ -137,9 +119,6 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Time Format
|
||||
delegate('click', '.switch-time-format', switchTimeFormat);
|
||||
|
||||
// Theme Switch
|
||||
delegate('click', '.btcpay-theme-switch', e => {
|
||||
@ -150,18 +129,6 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
e.target.closest('.btcpay-theme-switch').blur()
|
||||
})
|
||||
|
||||
// Currency Selection: Remove the current input value once the element is focused, so that the user gets to
|
||||
// see the available options. If no selection or change is made, reset it to the previous value on blur.
|
||||
// Note: Use focusin/focusout instead of focus/blur, because the latter do not bubble up and delegate won't work.
|
||||
delegate('focusin', 'input[list="currency-selection-suggestion"]', e => {
|
||||
e.target.setAttribute('placeholder', e.target.value)
|
||||
e.target.value = '';
|
||||
})
|
||||
delegate('focusout', 'input[list="currency-selection-suggestion"]', e => {
|
||||
if (!e.target.value) e.target.value = e.target.getAttribute('placeholder')
|
||||
e.target.removeAttribute('placeholder')
|
||||
})
|
||||
|
||||
// Offcanvas navigation
|
||||
const mainMenuToggle = document.getElementById('mainMenuToggle')
|
||||
if (mainMenuToggle) {
|
||||
@ -193,4 +160,14 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
}
|
||||
});
|
||||
|
||||
function switchTimeFormat() {
|
||||
$(".switchTimeFormat").each(function (index) {
|
||||
var htmlVal = $(this).html();
|
||||
var switchVal = $(this).attr("data-switch");
|
||||
|
||||
$(this).html(switchVal);
|
||||
$(this).attr("data-switch", htmlVal);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
@ -296,7 +296,7 @@
|
||||
"showCustomAmount": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to include a special item in the store which allows user to input a custom payment amount",
|
||||
"default": false,
|
||||
"default": true,
|
||||
"nullable": true
|
||||
},
|
||||
"showDiscount": {
|
||||
|
@ -85,15 +85,6 @@
|
||||
],
|
||||
"summary": "Add a custodial account to a store.",
|
||||
"description": "Add a custodial account to a store.",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "storeId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"description": "The Store ID",
|
||||
"schema": { "type": "string" }
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"x-name": "request",
|
||||
"content": {
|
||||
@ -191,26 +182,6 @@
|
||||
],
|
||||
"summary": "Update custodial account",
|
||||
"description": "Update custodial account",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "storeId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"description": "The Store ID",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "accountId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"description": "The Custodian Account ID",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"x-name": "request",
|
||||
"content": {
|
||||
@ -254,26 +225,6 @@
|
||||
],
|
||||
"summary": "Delete store custodian account",
|
||||
"description": "Deletes a custodial account",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "storeId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"description": "The Store ID",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "accountId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"description": "The Custodian Account ID",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Custodian account deleted"
|
||||
|
@ -194,41 +194,6 @@
|
||||
"blockHeight": {
|
||||
"type": "integer",
|
||||
"description": "The block height of the lightning node"
|
||||
},
|
||||
"alias": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The alias of the lightning node"
|
||||
},
|
||||
"color": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The color attribute of the lightning node"
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The version name of the lightning node"
|
||||
},
|
||||
"peersCount": {
|
||||
"type": "integer",
|
||||
"nullable": true,
|
||||
"description": "The number of peers"
|
||||
},
|
||||
"activeChannelsCount": {
|
||||
"type": "integer",
|
||||
"nullable": true,
|
||||
"description": "The number of active channels"
|
||||
},
|
||||
"inactiveChannelsCount": {
|
||||
"type": "integer",
|
||||
"nullable": true,
|
||||
"description": "The number of inactive channels"
|
||||
},
|
||||
"pendingChannelsCount": {
|
||||
"type": "integer",
|
||||
"nullable": true,
|
||||
"description": "The number of pending channels"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -1,5 +1,5 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Version>1.6.12</Version>
|
||||
<Version>1.6.10</Version>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
75
Changelog.md
75
Changelog.md
@ -1,71 +1,4 @@
|
||||
# Changelog
|
||||
|
||||
## 1.6.12
|
||||
|
||||
### New features
|
||||
|
||||
* Greenfield: Extend LN GetInfo data (#4167) @dennisreimann
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Always show overpaid amount if invoice is overpaid (#4192) @bolatovumar
|
||||
* Fix custodian Swagger docs missing some path parameters (#4196) @AryanJ-NYC
|
||||
* Fix receipts for Lightning Address invoices (#4169) @dennisreimann
|
||||
* POS: Fix keypad view without custom amount (#4183) @dennisreimann @bolatovumar
|
||||
* Fix truncated dates in wallet transaction list (#4191) @dennisreimann
|
||||
* Update default value for "showCustomAmount" in Swagger docs (#4200) @bolatovumar
|
||||
|
||||
### Improvement
|
||||
|
||||
* The wallet transaction list use infinity scroll rather than pagination (#4074) @HamroRamro @dennisreimann
|
||||
* Remove direct and temp link functionality from the File Storage (#4177) @daviogg
|
||||
* Fix warning error when rebooting the server caused by some shitcoin currency pair format @NicolasDorier
|
||||
* Add Invoice.OrderId to list of supported email interpolation strings (#4202) @bolatovumar
|
||||
* Do not hide errors happening in tasks spawned by BaseAsyncService @NicolasDorier
|
||||
|
||||
## 1.6.11
|
||||
|
||||
### New feature
|
||||
|
||||
* Add support for updating POS app through Greenfield API (#3458) @bolatovumar
|
||||
* Allow specifing fee block target for onchain payout processor (#4065) @Kukks
|
||||
|
||||
### Improvement
|
||||
|
||||
* Make POS and Crowdfund system plugins (#3987) @dennisreimann
|
||||
* Enhance export function for invoices (#4060) @HamroRamro
|
||||
* Create dynamic manifest for pos apps (#4064) @Kukks
|
||||
* Update invoice amount description in Swagger template @bolatovumar
|
||||
* Improve payout processors description (#4109) @woutersamaey
|
||||
* Set explicit cursor style property on pay button with custom text (#4107) @bolatovumar
|
||||
* Use mempool space as default block explorer (#4100) @junderw
|
||||
* Improve Lightning Node setup examples (#4033) @dennisreimann
|
||||
* Refactor QR functionality and improve wallet import support (#4091) @dennisreimann
|
||||
* Sanitize filename for downloaded files (#4117) @dennisreimann
|
||||
* Improve PayButton error page (#4129) @dennisreimann
|
||||
* Consistent switch UI on Create Wallet views (#4135) @dennisreimann
|
||||
* Point Of Sale: Custom amount disabled by default (#4126) @daviogg
|
||||
* Improve "Advanced Settings" button (#4140) @dennisreimann
|
||||
* Improve currency selection (#4155) @dennisreimann
|
||||
* Add official Telegram link to footer (#4160) @daviogg
|
||||
* Minor updates to security issues and bug reports doc (#4161) @Bangalisch
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fix app-related API docs @bolatovumar
|
||||
* Fix missing route hints option for LNURL invoices (#4077) @dennisreimann
|
||||
* Scale-down PoS item image (#4076) @prusnak
|
||||
* Ensure apps can be deleted through UI (#4080) @bolatovumar
|
||||
* Make sure end date is after start date in Crowdfund app (#4084) @bolatovumar
|
||||
* Show iframe when showing invoice in Shopify plugin (#4106) @bolatovumar
|
||||
* LNURL max value is set to min when item type is minmum (#4115) @Kukks
|
||||
* Fix Monero and Zcash nav extensions (#4124) @dennisreimann
|
||||
* Add missing store ID to invoice links (#4128) @bolatovumar
|
||||
* Fix pagination of wallet's transactions (#4150) @dennisreimann @NicolasDorier
|
||||
* Remove redundant exception status from invoice state label (#4151) @bolatovumar
|
||||
* Fix Store Settings nav highlight (#4138) @daviogg
|
||||
* Fix crash on migration from old install (#4162) @NicolasDorier
|
||||
|
||||
## 1.6.10
|
||||
|
||||
### Bug fixes:
|
||||
@ -93,7 +26,7 @@
|
||||
|
||||
* Add no rate found error message in Invoices (#4039) @HamroRamro
|
||||
* Greenfield API docs improvements (#4041 #4035) @dennisreimann
|
||||
* Allow binding ip and port for non ssl scenarios too (#4031) @Kukks
|
||||
* Allow binding ip and port for non ssl scenarios too (#4031) @Kukks
|
||||
|
||||
### Bug fixes:
|
||||
|
||||
@ -208,14 +141,14 @@ It's very likely that by updating BTCPay Server in the past six months, you've a
|
||||
* Can add sender's name to any field accepting an email destination, for example `Nicolas Dorier <blah@example.com>` rather than just `blah@example.com` (#3891) @NicolasDorier
|
||||
* Support LNURL Withdraw in payouts (#3709) @Kukks
|
||||
* Can send parametized emails based on invoice events (#3611) @Kukks
|
||||
* Dashboard: Added toggle button to switch to store default currency (#3752) @SakshamSolanki @dennisreimann
|
||||
* Dashboard: Added toggle button to switch to store default currency (#3752) @SakshamSolanki @dennisreimann
|
||||
* Support Lightning node connection string with onion addresses (#3845) @Kukks
|
||||
* New rate provider: BUDA a chilean exchange (#3766) @Kukks
|
||||
* Add Refunds list to Invoice details page (#3815) @Kukks
|
||||
|
||||
### Bug fixes:
|
||||
|
||||
* UI: Fix cancel plugin command (#3903) @dennisreimann
|
||||
* UI: Fix cancel plugin command (#3903) @dennisreimann
|
||||
* Crowdfunding: Fix the links for the default Quake sounds (#3745) @dennisreimann
|
||||
* UI: Fix nav height issue on mobile devices (#3888) @bolatovumar
|
||||
* UI: Fix mark all notifications as seen return URL (#3848) @dennisreimann
|
||||
@ -295,7 +228,7 @@ It's very likely that by updating BTCPay Server in the past six months, you've a
|
||||
* Various UI fixes (#3702 #3721) @dennisreimann
|
||||
* Updated Payout processor Label for setting interval (#3698) @Bangalisch
|
||||
* Update validation of crowdfund app settings (#3708) @bolatovumar
|
||||
* Fix POS styling (#3713) @ishristov
|
||||
* Fix POS styling (#3713) @ishristov
|
||||
|
||||
### Improvements:
|
||||
|
||||
|
@ -169,11 +169,11 @@ On linux:
|
||||
|
||||
### How to debug
|
||||
|
||||
If you want to debug, use Jetbrain's Rider or Visual Studio 2022.
|
||||
If you want to debug, use Visual Studio Code or Visual Studio 2019.
|
||||
|
||||
You need to run the development time docker-compose as described [in the test guide](./BTCPayServer.Tests/README.md).
|
||||
|
||||
You can then run the debugger by using the Launch Profile `Docker-Regtest`.
|
||||
You can then run the debugger by using the Launch Profile `Docker-Regtest` on either Visual Studio Code or Visual Studio 2017.
|
||||
|
||||
If you need to debug ledger wallet interaction, install the development time certificate with:
|
||||
|
||||
|
@ -1,9 +1,7 @@
|
||||
# How to handle security issues and bug reports?
|
||||
Security issues and bugs should be reported privately, via email. To report a security issue, please send an email to **security@btcpayserver.org** (not for support).
|
||||
|
||||
Security issues and bugs should be reported privately via email. To report a security issue, please email **security@btcpayserver.org** (not for support).
|
||||
|
||||
You will receive a reply indicating the next steps in handling your report. If, for some reason, you do not receive a response within 24 hours, please follow up via email to ensure the original message was received.
|
||||
You will receive a reply indicating the next steps in handling your report. If for some reason you do not receive a reply within 24 hours, please follow up via email to ensure the original message was received.
|
||||
|
||||
After the initial reply to your report, you will be informed of the progress towards a fix and full announcement. You may be asked to provide additional information or guidance.
|
||||
|
||||
We appreciate your efforts to disclose your findings responsibly and will make every effort to acknowledge your contributions.
|
||||
We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions.
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0.401-bullseye-slim AS builder
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0.302-bullseye-slim AS builder
|
||||
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||
WORKDIR /source
|
||||
COPY nuget.config nuget.config
|
||||
@ -20,7 +20,7 @@ COPY Build/Version.csproj Build/Version.csproj
|
||||
ARG CONFIGURATION_NAME=Release
|
||||
RUN cd BTCPayServer && dotnet publish --output /app/ --configuration ${CONFIGURATION_NAME}
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:6.0.9-bullseye-slim
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:6.0.7-bullseye-slim
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends iproute2 openssh-client \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Note that we are using buster rather than bullseye. Somehow, raspberry pi 4 doesn't like bullseye.
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0.401-bullseye-slim AS builder
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0.302-bullseye-slim AS builder
|
||||
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||
RUN apt-get update \
|
||||
&& apt-get install -qq --no-install-recommends qemu qemu-user-static qemu-user binfmt-support
|
||||
@ -25,7 +25,7 @@ ARG CONFIGURATION_NAME=Release
|
||||
RUN cd BTCPayServer && dotnet publish --output /app/ --configuration ${CONFIGURATION_NAME}
|
||||
|
||||
# Note that we are using buster rather than bullseye. Somehow, raspberry pi 4 doesn't like bullseye.
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:6.0.9-bullseye-slim-arm32v7
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:6.0.7-bullseye-slim-arm32v7
|
||||
COPY --from=builder /usr/bin/qemu-arm-static /usr/bin/qemu-arm-static
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends iproute2 openssh-client \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
@ -1,5 +1,5 @@
|
||||
# This is a manifest image, will pull the image with the same arch as the builder machine
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0.401-bullseye-slim AS builder
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0.302-bullseye-slim AS builder
|
||||
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||
ENV LC_ALL en_US.UTF-8
|
||||
RUN apt-get update \
|
||||
@ -26,7 +26,7 @@ ARG CONFIGURATION_NAME=Release
|
||||
RUN cd BTCPayServer && dotnet publish --output /app/ --configuration ${CONFIGURATION_NAME}
|
||||
|
||||
# Force the builder machine to take make an arm runtime image. This is fine as long as the builder does not run any program
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:6.0.9-bullseye-slim-arm64v8
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:6.0.7-bullseye-slim-arm64v8
|
||||
COPY --from=builder /usr/bin/qemu-aarch64-static /usr/bin/qemu-aarch64-static
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends iproute2 openssh-client \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
Reference in New Issue
Block a user