From 1bc0e834e524fc9800eeef2f242bcd0aa4e1be50 Mon Sep 17 00:00:00 2001 From: Dennis Reimann Date: Tue, 16 Jan 2024 16:54:49 +0100 Subject: [PATCH] UI updates --- BTCPayApp.Tests/BTCPayAppTestServer.cs | 5 - BTCPayApp.UI/Components/Keypad.razor | 144 +++++++++++++++---- BTCPayApp.UI/Components/Keypad.razor.css | 36 ++++- BTCPayApp.UI/Pages/PointOfSalePage.razor | 13 +- BTCPayApp.UI/wwwroot/bootstrap/bootstrap.css | 4 + BTCPayApp.UI/wwwroot/img/icon-sprite.svg | 6 +- 6 files changed, 168 insertions(+), 40 deletions(-) diff --git a/BTCPayApp.Tests/BTCPayAppTestServer.cs b/BTCPayApp.Tests/BTCPayAppTestServer.cs index 287dd046..b8cd4374 100644 --- a/BTCPayApp.Tests/BTCPayAppTestServer.cs +++ b/BTCPayApp.Tests/BTCPayAppTestServer.cs @@ -1,9 +1,4 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Hosting.Server; -using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.AspNetCore.Mvc.Testing; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Playwright; diff --git a/BTCPayApp.UI/Components/Keypad.razor b/BTCPayApp.UI/Components/Keypad.razor index 8ebc87ee..4964a4a3 100644 --- a/BTCPayApp.UI/Components/Keypad.razor +++ b/BTCPayApp.UI/Components/Keypad.razor @@ -1,12 +1,15 @@ -@using Newtonsoft.Json.Linq -@using System.Text.RegularExpressions +@using System.Text.RegularExpressions @using System.Globalization +@using Newtonsoft.Json +@using Newtonsoft.Json.Linq @inject IJSRuntime JS -
+
+ +
@CurrencyCode
-
@FormatCurrency(GetTotal(), false)
+
@FormatCurrency(GetTotal(), false)
@Calculation(Model)
@if (IsDiscountEnabled || IsTipEnabled) @@ -56,13 +59,14 @@ }
- -
+ @if (!string.IsNullOrEmpty(RecentTransactionsUrl)) + { + + + } + @code { - [Parameter] - public string? CurrencyCode { get; set; } +#nullable enable + [Parameter, EditorRequired] + public string CurrencyCode { get; set; } [Parameter] - public string? CurrencySymbol { get; set; } + public string RecentTransactionsUrl { get; set; } [Parameter] - public int CurrencyDivisibility { get; set; } + public NumberFormatInfo? CurrencyInfo { get; set; } [Parameter] public bool IsDiscountEnabled { get; set; } [Parameter] public bool IsTipEnabled { get; set; } [Parameter] public int[]? CustomTipPercentages { get; set; } + [Parameter] + public EventCallback OnClickCallback { get; set; } private bool IsSubmitting { get; set; } + private List? RecentTransactions { get; set; } const int DefaultFontSize = 64; static char[] Keys = { '1', '2', '3', '4', '5', '6', '7', '8', '9', 'C', '0', '+' }; @@ -117,8 +178,11 @@ private ElementReference _keypadTop; private ElementReference _keypadAmount; - + private string? _currencySymbol; + private int _currencyDivisibility; + private bool _recentTransactionsLoading; private InputMode Mode { get; set; } = InputMode.Amount; + private KeypadModel Model { get; set; } = new (); public class KeypadModel { @@ -128,11 +192,12 @@ public decimal? Tip { get; set; } } - private KeypadModel Model { get; set; } = new (); - - private void Submit() + protected override void OnInitialized() { - IsSubmitting = true; + base.OnInitialized(); + + _currencySymbol = CurrencyInfo?.CurrencySymbol ?? CurrencyCode; + _currencyDivisibility = CurrencyInfo?.CurrencyDecimalDigits ?? 0; } private async Task KeyPress(char key) @@ -154,9 +219,8 @@ } else if (key == '+' && lastAmount != 0) { Model.Amounts.Add(0); } else { // Is a digit - Model.Amounts[lastIndex] = Math.Min(ApplyKeyToValue(key, lastAmount, CurrencyDivisibility), decimal.MaxValue / 10); + Model.Amounts[lastIndex] = Math.Min(ApplyKeyToValue(key, lastAmount, _currencyDivisibility), decimal.MaxValue / 10); } - await UpdateFontSize(); } else { if (key == 'C') { if (Mode == InputMode.Tip) @@ -169,7 +233,7 @@ Model.DiscountPercent = null; } } else { - var divisibility = Mode == InputMode.Tip ? CurrencyDivisibility : 0; + var divisibility = Mode == InputMode.Tip ? _currencyDivisibility : 0; if (Mode == InputMode.Tip) { Model.Tip = Math.Min(ApplyKeyToValue(key, Model.Tip ?? 0, divisibility), decimal.MaxValue / 10); @@ -182,12 +246,12 @@ } } } + await UpdateFontSize(); } private decimal ApplyKeyToValue(char key, decimal value, int divisibility) { - var str = value is 0 ? "" : Formatted(value, divisibility); - //var minDiv = str.Length < divisibility ? str.Length + 1 : divisibility; + var str = value is 0 ? "" : FormattedInvariant(value, divisibility); str = (str + key).Replace(".", ""); if (divisibility > 0) { @@ -215,7 +279,7 @@ Mode = InputMode.Amount; } - private JObject GetData() + private string GetData() { var data = new JObject { @@ -242,7 +306,7 @@ { data["tipPercentage"] = Model.TipPercent; } - return data; + return JsonConvert.SerializeObject(data); } private List GetModes() @@ -262,7 +326,7 @@ { var amount = GetAmount(); return amount > 0 && Model.DiscountPercent is > 0 - ? Math.Round(amount * (Model.DiscountPercent.Value / 100.0m), CurrencyDivisibility) + ? Math.Round(amount * (Model.DiscountPercent.Value / 100.0m), _currencyDivisibility) : 0; } @@ -270,9 +334,9 @@ { if (Model.TipPercent is > 0) { var amount = GetAmount() - GetDiscount(); - return Math.Round(amount * (Model.TipPercent.Value / 100.0m), CurrencyDivisibility); + return Math.Round(amount * (Model.TipPercent.Value / 100.0m), _currencyDivisibility); } - return Model.Tip is > 0 ? Math.Round(Model.Tip.Value, CurrencyDivisibility) : 0.0m; + return Model.Tip is > 0 ? Math.Round(Model.Tip.Value, _currencyDivisibility) : 0.0m; } private decimal GetTotal() @@ -296,8 +360,8 @@ { if (CurrencyCode is "BTC" or "SATS") return FormatCrypto(value, withSymbol); try { - var symbol = withSymbol ? $" {CurrencySymbol ?? CurrencyCode}" : ""; - return $"{Formatted(value, CurrencyDivisibility)}{symbol}"; + var formatted = value.ToString("C", CurrencyInfo); + return withSymbol ? formatted : formatted.Replace(_currencySymbol, "").Trim(); } catch (Exception) { @@ -306,11 +370,11 @@ } private string FormatCrypto(decimal value, bool withSymbol) { - var symbol = withSymbol ? $" {CurrencySymbol ?? CurrencyCode}" : ""; - return $"{Formatted(value, CurrencyDivisibility)}{symbol}"; + var symbol = withSymbol ? $" {_currencySymbol}" : ""; + return $"{FormattedInvariant(value, _currencyDivisibility)}{symbol}"; } - private string Formatted(decimal value, int divisibility) { + private string FormattedInvariant(decimal value, int divisibility) { return string.Format(CultureInfo.InvariantCulture, $"{{0:0.{new string('0', divisibility)}}}", value); } @@ -326,4 +390,22 @@ StateHasChanged(); } + + private async Task LoadRecentTransactions() + { + _recentTransactionsLoading = true; + StateHasChanged(); + RecentTransactions = null; + _recentTransactionsLoading = false; + StateHasChanged(); + } + + private class RecentTransaction + { + public string Id { get; set; } + public DateTimeOffset Date { get; set; } + public string Url { get; set; } + public string Price { get; set; } + public string Status { get; set; } + } } diff --git a/BTCPayApp.UI/Components/Keypad.razor.css b/BTCPayApp.UI/Components/Keypad.razor.css index 63cff871..db55ee0e 100644 --- a/BTCPayApp.UI/Components/Keypad.razor.css +++ b/BTCPayApp.UI/Components/Keypad.razor.css @@ -1,3 +1,4 @@ + /* modes */ #ModeTabs { min-height: 2.75rem; @@ -15,6 +16,7 @@ padding: 0; position: relative; border-radius: 0; + border-color: transparent !important; font-weight: var(--btcpay-font-weight-semibold); font-size: 24px; min-height: 3.5rem; @@ -55,9 +57,41 @@ min-height: 1.5rem; } +#RecentTransactionsToggle { + position: absolute; + top: var(--btcpay-space-l); + left: var(--btcpay-space-m); + z-index: 1; +} + +#RecentTransactionsToggle .icon { + --btn-icon-size: 2.25em; +} +#RecentTransactionsRefresh[disabled] .icon { + animation: 1.25s linear infinite spinner-border; +} +#RecentTransactions .list-group { + margin: calc(var(--btcpay-modal-padding) * -1); + width: calc(100% + var(--btcpay-modal-padding) * 2); +} + +#RecentTransactions .list-group-item { + background-color: transparent; +} + +#RecentTransactions .list-group .badge-container { + flex: 0 0 5.125rem; + text-align: right; +} + +@media (max-width: 359px) { + #RecentTransactions .list-group .badge-container { + flex-grow: 1; + } +} + /* fix sticky hover effect on mobile browsers */ @media (hover: none) { - .keypad .btn-secondary:hover, .actions .btn-secondary:hover { border-color: var(--btcpay-secondary-border-active) !important; } diff --git a/BTCPayApp.UI/Pages/PointOfSalePage.razor b/BTCPayApp.UI/Pages/PointOfSalePage.razor index 52ae5614..6fbb8e83 100644 --- a/BTCPayApp.UI/Pages/PointOfSalePage.razor +++ b/BTCPayApp.UI/Pages/PointOfSalePage.razor @@ -1,10 +1,21 @@ @attribute [Route(Routes.PointOfSale)] @layout SimpleLayout +@using System.Globalization @inherits Fluxor.Blazor.Web.Components.FluxorComponent Point Of Sale
-
+ +@code { + public NumberFormatInfo CurrencyInfo { get; set; } = new() + { + CurrencySymbol = "\u20ac", + CurrencyDecimalDigits = 2, + CurrencyDecimalSeparator = ",", + CurrencyGroupSeparator = "." + }; +} diff --git a/BTCPayApp.UI/wwwroot/bootstrap/bootstrap.css b/BTCPayApp.UI/wwwroot/bootstrap/bootstrap.css index 7a5a0af3..d0ab8bd1 100644 --- a/BTCPayApp.UI/wwwroot/bootstrap/bootstrap.css +++ b/BTCPayApp.UI/wwwroot/bootstrap/bootstrap.css @@ -12255,6 +12255,10 @@ ul:not([class]) li { color: var(--btcpay-body-text-muted) !important; } +.text-warning { + color: rgba(var(--btcpay-body-text-warning-rgb), var(--btcpay-text-opacity)) !important; +} + /* Modals */ .modal-content { box-shadow: 0 20px 20px rgba(0, 0, 0, 0.15); diff --git a/BTCPayApp.UI/wwwroot/img/icon-sprite.svg b/BTCPayApp.UI/wwwroot/img/icon-sprite.svg index d438dafe..cd969635 100644 --- a/BTCPayApp.UI/wwwroot/img/icon-sprite.svg +++ b/BTCPayApp.UI/wwwroot/img/icon-sprite.svg @@ -63,6 +63,7 @@ + @@ -77,8 +78,9 @@ - - + + +