Skip to content

Commit

Permalink
Merge pull request #12661 from ichthus1604/FixAmountPaste
Browse files Browse the repository at this point in the history
[VDG] Fix Amount Paste
  • Loading branch information
soosr committed Mar 25, 2024
2 parents 6001415 + 3bb135a commit d10f474
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 32 deletions.
33 changes: 21 additions & 12 deletions WalletWasabi.Fluent/Controls/CurrencyEntryBox.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using WalletWasabi.Fluent.Helpers;
using WalletWasabi.Fluent.Infrastructure;
using WalletWasabi.Helpers;
using WalletWasabi.Userfacing;
using static WalletWasabi.Userfacing.CurrencyInput;

namespace WalletWasabi.Fluent.Controls;
Expand Down Expand Up @@ -208,9 +209,6 @@ private TextInputEventArgs InsertLeadingZeroForDecimal(TextInputEventArgs e)
[GeneratedRegex($"^(?<Whole>[0-9{GroupSeparator}]*)(\\{DecimalSeparator}?(?<Frac>[0-9{GroupSeparator}]*))$")]
private static partial Regex RegexBtcFormat();

[GeneratedRegex($"^[0-9{GroupSeparator}{DecimalSeparator}]*$")]
private static partial Regex RegexDecimalCharsOnly();

[GeneratedRegex($"{GroupSeparator}{{2,}}")]
private static partial Regex RegexConsecutiveSpaces();

Expand Down Expand Up @@ -238,7 +236,7 @@ private bool ValidateEntryText(string preComposedText)
var rule2 = whole >= 8 && (preComposedText.EndsWith(GroupSeparator) || wholeStr.EndsWith(GroupSeparator));

// Check for non-numeric chars.
var rule3 = !RegexDecimalCharsOnly().IsMatch(preComposedText);
var rule3 = !CurrencyInput.RegexDecimalCharsOnly().IsMatch(preComposedText);
if (rule1 || rule2 || rule3)
{
return false;
Expand Down Expand Up @@ -308,9 +306,15 @@ public async void ModifiedPasteAsync()
return;
}

// Ignore paste if there are invalid characters
if (!CurrencyInput.RegexValidCharsOnly().IsMatch(text))
{
return;
}

text = text.Replace("\r", "").Replace("\n", "").Trim();

if (!TryParse(text, out text))
if (!TryParsePastedValue(text, out text))
{
return;
}
Expand All @@ -327,26 +331,31 @@ public async void ModifiedPasteAsync()
}
}

private bool TryParse(string text, [NotNullWhen(true)] out string? result)
private bool TryParsePastedValue(string text, [NotNullWhen(true)] out string? result)
{
if (!IsFiat)
{
if (CurrencyInput.TryCorrectBitcoinAmount(text, out var corrected))
{
text = corrected;
}

var money = ValidatePasteBalance
? ClipboardObserver.ParseToMoney(text, BalanceBtc)
: ClipboardObserver.ParseToMoney(text);
if (money is not null)
{
var fractionalCount =
text.Contains('.')
? text.Skip(text.LastIndexOf('.')).Where(char.IsDigit).Count()
: 0;

result = money.ToDecimal(MoneyUnit.BTC).FormattedBtcExactFractional(fractionalCount);
result = money.ToDecimal(MoneyUnit.BTC).FormattedBtcExactFractional(text);
return true;
}
}
else
{
if (CurrencyInput.TryCorrectAmount(text, out var corrected))
{
text = corrected;
}

var usd = ValidatePasteBalance
? ClipboardObserver.ParseToUsd(text, BalanceUsd)
: ClipboardObserver.ParseToUsd(text);
Expand Down
2 changes: 1 addition & 1 deletion WalletWasabi.Fluent/Controls/DualCurrencyEntryBox.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ private void InputText(string? text)
}
else
{
if (CurrencyInput.TryCorrectBitcoinAmount(text, out var better) && better != Constants.MaximumNumberOfBitcoins.ToString())
if (CurrencyInput.TryCorrectBitcoinAmount(text, out var better))
{
text = better;
}
Expand Down
10 changes: 10 additions & 0 deletions WalletWasabi.Fluent/Extensions/CurrencyExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ public static string FormattedBtcExactFractional(this decimal amount, int fracti
return string.Format(FormatInfo, fullFormat, amount).Trim();
}

public static string FormattedBtcExactFractional(this decimal amount, string originalText)
{
var fractionalCount =
originalText.Contains('.')
? originalText.Skip(originalText.LastIndexOf('.')).Where(char.IsDigit).Count()
: 0;

return FormattedBtcExactFractional(amount, fractionalCount);
}

public static string FormattedFiat(this decimal amount, string format = "N2")
{
return amount.ToString(format, FormatInfo).Trim();
Expand Down
19 changes: 15 additions & 4 deletions WalletWasabi.Fluent/Infrastructure/ClipboardObserver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ public ClipboardObserver(IObservable<Amount> balances)
public IObservable<string?> ClipboardBtcContentChanged(IScheduler scheduler)
{
return ApplicationHelper.ClipboardTextChanged(scheduler)
.CombineLatest(_balances.Select(x => x.Btc), ParseToMoney)
.Select(money => money?.ToDecimal(MoneyUnit.BTC).FormattedBtc());
.CombineLatest(_balances.Select(x => x.Btc), ParseToMoney);
}

public static decimal? ParseToUsd(string? text)
Expand Down Expand Up @@ -70,8 +69,20 @@ public ClipboardObserver(IObservable<Amount> balances)
return Money.TryParse(text, out var n) ? n : default;
}

public static Money? ParseToMoney(string? text, Money balance)
public static string? ParseToMoney(string? text, Money balance)
{
return ParseToMoney(text).Ensure(m => m <= balance);
// Ignore paste if there are invalid characters
if (text is null || !CurrencyInput.RegexValidCharsOnly().IsMatch(text))
{
return null;
}

if (CurrencyInput.TryCorrectBitcoinAmount(text, out var corrected))
{
text = corrected;
}

var money = ParseToMoney(text).Ensure(m => m <= balance);
return money?.ToDecimal(MoneyUnit.BTC).FormattedBtcExactFractional(text);
}
}
2 changes: 1 addition & 1 deletion WalletWasabi.Fluent/Views/Wallets/Send/SendView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@
IsConversionReversed="{Binding ConversionReversed, Mode=TwoWay}"
BalanceBtc="{Binding Balance^.Btc}"
BalanceUsd="{Binding Balance^.Usd^}"
ValidatePasteBalance="True">
ValidatePasteBalance="False">
<DualCurrencyEntryBox.Resources>

<converters:BooleanConverter x:Key="SuggestionPlacementConverter"
Expand Down
14 changes: 7 additions & 7 deletions WalletWasabi.Tests/UnitTests/Userfacing/CurrencyInputTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,13 @@ public BitcoinTestData()
{
Add("1.000000000", true, "1.00000000");
Add("1.111111119", true, "1.11111111");
Add("20999999.97690001", true, "20999999.9769");
Add("30999999", true, "20999999.9769");
Add("303333333333333333999999", true, "20999999.9769");
Add("20999999.977", true, "20999999.9769");
Add("209999990.9769", true, "20999999.9769");
Add("20999999.976910000000000", true, "20999999.9769");
Add("209999990000000000.97000000000069", true, "20999999.9769");
Add("20999999.97690001", false, null);
Add("30999999", false, null);
Add("303333333333333333999999", false, null);
Add("20999999.977", false, null);
Add("209999990.9769", false, null);
Add("20999999.976910000000000", true, "20999999.97691000");
Add("209999990000000000.97000000000069", true, "209999990000000000.97000000");
Add("1.000000001", true, "1.00000000");
Add("20999999.97000000000069", true, "20999999.97000000");
}
Expand Down
14 changes: 7 additions & 7 deletions WalletWasabi/Userfacing/CurrencyInput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace WalletWasabi.Userfacing;

public static class CurrencyInput
public static partial class CurrencyInput
{
public const string DecimalSeparator = ".";
public const string GroupSeparator = " ";
Expand All @@ -19,6 +19,12 @@ public static class CurrencyInput
NumberDecimalSeparator = DecimalSeparator
};

[GeneratedRegex($"^[0-9{GroupSeparator}{DecimalSeparator}]*$")]
public static partial Regex RegexDecimalCharsOnly();

[GeneratedRegex(@"[\d.,٫٬⎖·\']")]
public static partial Regex RegexValidCharsOnly();

public static bool TryCorrectAmount(string original, [NotNullWhen(true)] out string? best)
{
var corrected = original;
Expand Down Expand Up @@ -104,12 +110,6 @@ public static bool TryCorrectBitcoinAmount(string original, [NotNullWhen(true)]
corrected = corrected[..(dotIndex + 1 + 8)];
}

// Make sure you don't send more bitcoins than how much there is in existence.
if (corrected.Length != 0 && corrected != "." && (!decimal.TryParse(corrected, out decimal btcDecimal) || btcDecimal > Constants.MaximumNumberOfBitcoins))
{
corrected = Constants.MaximumNumberOfBitcoins.ToString();
}

if (corrected != original)
{
best = corrected;
Expand Down

0 comments on commit d10f474

Please sign in to comment.