Skip to content

Commit

Permalink
Keypad updates
Browse files Browse the repository at this point in the history
  • Loading branch information
dennisreimann committed Nov 9, 2023
1 parent 11d0d76 commit a7c6641
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 51 deletions.
120 changes: 71 additions & 49 deletions BTCPayServer/Blazor/Keypad.razor
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
@using Newtonsoft.Json.Linq
@using System.Text.RegularExpressions
@using System.Globalization
@inject IJSRuntime JS

<form class="d-flex flex-column gap-4" @onsubmit="Submit">
<div ref="display" class="d-flex flex-column align-items-center px-4 mb-auto">
<div class="d-flex flex-column align-items-center px-4 mb-auto" @ref="_keypadTop">
<div class="fw-semibold text-muted" id="Currency">@CurrencyCode</div>
<div class="fw-bold lh-sm" style="font-size:@(FontSize + "px")" id="Amount">@FormatCurrency(GetTotal(), false)</div>
<div class="fw-bold lh-sm" style="font-size:@($"{FontSize}px")" @ref="_keypadAmount">@FormatCurrency(GetTotal(), false)</div>
<div class="text-muted text-center mt-2" id="Calculation">@Calculation(Model)</div>
</div>
@if (IsDiscountEnabled || IsTipEnabled)
Expand All @@ -12,7 +16,7 @@
{
<div class="tab-pane fade px-2 @(Mode == InputMode.Discount ? "show active" : "")" role="tabpanel" aria-labelledby="ModeTablist-Discount">
<div class="h4 fw-semibold text-muted text-center" id="Discount">
<span class="h3 text-body me-1">@Model.DiscountPercent%</span> discount
<span class="h3 text-body me-1">@(Model.DiscountPercent ?? 0)%</span> discount
</div>
</div>
}
Expand Down Expand Up @@ -74,7 +78,7 @@
@if (IsSubmitting)
{
<div class="spinner-border spinner-border-sm" role="status">
<span class="sr-only">Loading...</span>
<span class="visually-hidden">Loading...</span>
</div>
}
else
Expand All @@ -85,55 +89,54 @@
</form>

@code {
#nullable enable
[Parameter]
public string CurrencyCode { get; set; }
public string? CurrencyCode { get; set; }
[Parameter]
public string CurrencySymbol { get; set; }
public string? CurrencySymbol { get; set; }
[Parameter]
public int CurrencyDivisibility { get; set; }
[Parameter]
public bool IsDiscountEnabled { get; set; }
[Parameter]
public bool IsTipEnabled { get; set; }
[Parameter]
public int[] CustomTipPercentages { get; set; }
public int[]? CustomTipPercentages { get; set; }

private bool IsSubmitting { get; set; }

const int DefaultFontSize = 64;
static char[] Keys = { '1', '2', '3', '4', '5', '6', '7', '8', '9', 'C', '0', '+' };
public int FontSize { get; set; } = DefaultFontSize;
private int FontSize { get; set; } = DefaultFontSize;

public enum InputMode
{
Amount,
Discount,
Tip
}

public InputMode Mode { get; set; } = InputMode.Amount;


private ElementReference _keypadTop;
private ElementReference _keypadAmount;

private InputMode Mode { get; set; } = InputMode.Amount;

public class KeypadModel
{
public List<decimal> Amounts { get; set; } = new ();
public List<decimal> Amounts { get; set; } = new () { 0 };
public int? DiscountPercent { get; set; }
public int? TipPercent { get; set; }
public decimal? Tip { get; set; }
}

private KeypadModel Model { get; set; }

protected override void OnInitialized()
{
Model ??= new KeypadModel();
}

private KeypadModel Model { get; set; } = new ();

private void Submit()
{
IsSubmitting = true;
}

public void KeyPress(char key = '1')
private async Task KeyPress(char key)
{
if (Mode == InputMode.Amount) {
var lastIndex = Model.Amounts.Count - 1;
Expand All @@ -152,8 +155,9 @@
} else if (key == '+' && lastAmount != 0) {
Model.Amounts.Add(0);
} else { // Is a digit
Model.Amounts[lastIndex] = ApplyKeyToValue(key, lastAmount, CurrencyDivisibility);
Model.Amounts[lastIndex] = Math.Min(ApplyKeyToValue(key, lastAmount, CurrencyDivisibility), decimal.MaxValue / 10);
}
await UpdateFontSize();
} else {
if (key == 'C') {
if (Mode == InputMode.Tip)
Expand All @@ -169,51 +173,56 @@
var divisibility = Mode == InputMode.Tip ? CurrencyDivisibility : 0;
if (Mode == InputMode.Tip)
{
Model.Tip = ApplyKeyToValue(key, Model.Tip ?? 0, divisibility);
Model.Tip = Math.Min(ApplyKeyToValue(key, Model.Tip ?? 0, divisibility), decimal.MaxValue / 10);
Model.TipPercent = null;
}
else
{
Model.DiscountPercent = (int)ApplyKeyToValue(key, Model.DiscountPercent ?? 0, divisibility);
var num = (int)ApplyKeyToValue(key, Model.DiscountPercent ?? 0, divisibility);
Model.DiscountPercent = Math.Min(num, 100);
}
}
}
}

public decimal ApplyKeyToValue(char key, decimal value, int divisibility)
private decimal ApplyKeyToValue(char key, decimal value, int divisibility)
{
var val = value is 0 ? "" : Formatted(value, divisibility);
val = (val + key)
.Replace(".", "")
.PadLeft(divisibility, '0');
//.Replace(new Regex("(\\d*)(\\d{{divisibility}})", "\\1.\\2");
return decimal.Parse(val);
var str = value is 0 ? "" : Formatted(value, divisibility);
str = (str + key).Replace(".", "");
if (divisibility > 0)
{
str = str.PadLeft(divisibility + 1, '0');
str = Regex.Replace(str, $"(\\d*)(\\d{{{divisibility}}})", "$1.$2");
}

return decimal.Parse(str, CultureInfo.InvariantCulture);
}

public void DoublePress(char key)
private void DoublePress(char key)
{
if (key == 'C') {
Clear();
}
}

public void Clear()
private void Clear()
{
Model.Amounts.Clear();
Model.Amounts.Add(0);
Model.DiscountPercent = null;
Model.TipPercent = null;
Model.Tip = null;
Mode = InputMode.Amount;
}

private JObject GetData()
{
var data = new JObject
{
["subTotal"] = GetAmount(),
["total"] = GetTotal(),
};

var discount = GetDiscount();
if (discount > 0)
{
Expand All @@ -223,7 +232,7 @@
{
data["discountPercentage"] = Model.DiscountPercent;
}

var tip = GetTip();
if (tip > 0)
{
Expand All @@ -235,28 +244,28 @@
}
return data;
}

private List<InputMode> GetModes()
{
var modes = new List<InputMode> { InputMode.Amount };
if (IsDiscountEnabled) modes.Add(InputMode.Discount);
if (IsTipEnabled) modes.Add(InputMode.Tip);
return modes;
}

private decimal GetAmount()
{
return Model.Amounts.Count > 0 ? Model.Amounts.Sum() : Model.Amounts.FirstOrDefault();
}

private decimal GetDiscount()
{
var amount = GetAmount();
return amount > 0 && Model.DiscountPercent is > 0
? Math.Round(amount * (Model.DiscountPercent.Value / 100.0m), CurrencyDivisibility)
: 0;
}

private decimal GetTip()
{
if (Model.TipPercent is > 0) {
Expand All @@ -271,15 +280,15 @@
return GetAmount() - GetDiscount() + GetTip();
}

private string Calculation(KeypadModel model)
private string? Calculation(KeypadModel model)
{
if (model.Amounts.Count < 2 && model.DiscountPercent is not > 0 && !model.Tip.HasValue) return null;
var calc = string.Join(" + ", model.Amounts.Select(amt => FormatCurrency(amt, true)));
var discount = GetDiscount();
if (discount > 0) calc += $" - {FormatCurrency(discount, true)} (${model.DiscountPercent}%)";
if (discount > 0) calc += $" - {FormatCurrency(discount, true)} ({model.DiscountPercent}%)";
var tip = GetTip();
if (model.Tip > 0) calc += $" + ${FormatCurrency(tip, true)}";
if (model.TipPercent > 0) calc += $" (${model.TipPercent}%)";
if (tip > 0) calc += $" + {FormatCurrency(tip, true)}";
if (model.TipPercent > 0) calc += $" ({model.TipPercent}%)";
return calc;
}

Expand All @@ -295,13 +304,26 @@
return FormatCrypto(value, withSymbol);
}
}

private string FormatCrypto(decimal value, bool withSymbol) {
var symbol = withSymbol ? $" {CurrencySymbol ?? CurrencyCode}" : "";
return $"{Formatted(value, CurrencyDivisibility)}{symbol}";
}

private string Formatted(decimal value, int divisibility) {
return string.Format($"{{0:0.{new string('0', divisibility)}}}", value);
return string.Format(CultureInfo.InvariantCulture, $"{{0:0.{new string('0', divisibility)}}}", value);
}

private async Task UpdateFontSize()
{
var top = await JS.InvokeAsync<decimal>("Interop.getWidth", new object[] { _keypadTop });
var amt = await JS.InvokeAsync<decimal>("Interop.getWidth", new object[] { _keypadAmount });
var gamma = top / amt;

FontSize = (int)(top < amt
? Math.Floor(FontSize * gamma)
: Math.Min(FontSize * gamma, DefaultFontSize));

StateHasChanged();
}
}
2 changes: 1 addition & 1 deletion BTCPayServer/Views/Shared/PointOfSale/Public/Cart.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@
<td colspan="2" class="pt-4">
<button id="CartSubmit" class="btn btn-primary btn-lg w-100" :disabled="payButtonLoading" type="submit">
<div v-if="payButtonLoading" class="spinner-border spinner-border-sm" role="status">
<span class="sr-only">Loading...</span>
<span class="visually-hidden">Loading...</span>
</div>
<template v-else>Pay</template>
</button>
Expand Down
1 change: 1 addition & 0 deletions BTCPayServer/Views/Shared/PointOfSale/Public/Light.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<link href="~/pos/keypad.css" asp-append-version="true" rel="stylesheet" />
}
@section PageFootContent {
<script src="~/js/blazor-interop.js" autostart="false" asp-append-version="true"></script>
<script src="~/_blazorfiles/_framework/blazor.server.js" autostart="false" asp-append-version="true"></script>
}
<div class="public-page-wrap">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
</div>
<button class="btn btn-lg btn-primary mx-3" type="submit" :disabled="payButtonLoading" id="pay-button">
<div v-if="payButtonLoading" class="spinner-border spinner-border-sm" role="status">
<span class="sr-only">Loading...</span>
<span class="visually-hidden">Loading...</span>
</div>
<template v-else>Charge</template>
</button>
Expand Down
5 changes: 5 additions & 0 deletions BTCPayServer/wwwroot/js/blazor-interop.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Interop = {
getWidth(el) {
return el.clientWidth;
}
}

0 comments on commit a7c6641

Please sign in to comment.