Skip to content

Commit

Permalink
chore: Merge pull request #513 from dashpay/feature/amount-input-impr…
Browse files Browse the repository at this point in the history
…ovements

feat: Amount input improvements
  • Loading branch information
tikhop authored Feb 20, 2023
2 parents 1c12fe4 + 87a1eeb commit 31c6cad
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ struct AmountObject {
mainFormatted = str
} else {
plainAmount = 0
mainFormatted = "Error"
mainFormatted = NSLocalizedString("Updating Price", comment: "Updating Price")
}
}

Expand All @@ -119,8 +119,9 @@ extension AmountObject {
if amountType == .supplementary { return self }

let numberFormatter = localFormatter.copy() as! NumberFormatter
numberFormatter.numberStyle = .decimal
numberFormatter.minimumFractionDigits = localFormatter.minimumFractionDigits
numberFormatter.numberStyle = .none
numberFormatter.minimumIntegerDigits = 1
numberFormatter.minimumFractionDigits = 0
numberFormatter.maximumFractionDigits = localFormatter.maximumFractionDigits

guard let amountInternalRepresentation = numberFormatter.string(from: supplementaryAmount as NSDecimalNumber) else {
Expand Down
42 changes: 17 additions & 25 deletions DashWallet/Sources/UI/Payments/Amount/Model/BaseAmountModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,13 @@ class BaseAmountModel {
func setupCurrencyCode(_ code: String) {
guard let price = try? CurrencyExchanger.shared.rate(for: code) else { return }

localFormatter.currencyCode = code
localFormatter = NumberFormatter.fiatFormatter(currencyCode: code)
localCurrencyCode = code

currentInputItem = currentInputItem.currencyCode == kDashCurrency ? .dash : .app
let newInputItem = AmountInputItem.custom(currencyName: localCurrencyCode, currencyCode: localCurrencyCode)
currentInputItem = currentInputItem.isMain ? .dash : newInputItem
inputItems = [
.custom(currencyName: localCurrencyCode, currencyCode: localCurrencyCode),
newInputItem,
.dash,
]

Expand Down Expand Up @@ -303,39 +304,30 @@ extension BaseAmountModel {
}

func amountInputControlDidSwapInputs() {
assert(isSwapToLocalCurrencyAllowed, "Switching until price is not fetched is not allowed")
assert(inputItems.count == 2, "Swap only if we have two input types")

let inputItem = inputItems[0] == currentInputItem ? inputItems[1] : inputItems[0]
select(inputItem: inputItem)
}

func pasteFromClipboard() {
guard var string = UIPasteboard.general.string else { return }
string = string.localizedAmount()

guard let decimal = Decimal(string: string, locale: .current) else { return }
let decimalNumber = NSDecimalNumber(decimal: decimal)
guard let string = UIPasteboard.general.string else { return }

let formattedString: String?
let originalFormatter = currentInputItem.isMain
? NumberFormatter.dashFormatter
: localFormatter
let formatter = originalFormatter.copy() as! NumberFormatter
formatter.numberStyle = .decimal
formatter.minimumFractionDigits = 0
formatter.maximumFractionDigits = originalFormatter.maximumFractionDigits

var formatter: NumberFormatter
guard let number = formatter.number(from: string) else { return }

if activeAmountType == .main {
formatter = NumberFormatter.dashFormatter.copy() as! NumberFormatter
formatter.numberStyle = .decimal
formatter.minimumFractionDigits = NumberFormatter.dashFormatter.minimumFractionDigits
formatter.maximumFractionDigits = NumberFormatter.dashFormatter.maximumFractionDigits
formattedString = formatter.string(from: decimalNumber)
} else {
formatter = localFormatter.copy() as! NumberFormatter
formatter.numberStyle = .decimal
formatter.minimumFractionDigits = localFormatter.minimumFractionDigits
formatter.maximumFractionDigits = localFormatter.maximumFractionDigits
formattedString = formatter.string(from: decimalNumber)
}
formatter.numberStyle = .none
formatter.minimumFractionDigits = 0
formatter.maximumFractionDigits = originalFormatter.maximumFractionDigits

guard let string = formattedString else { return }
guard let string = formatter.string(from: number) else { return }

updateAmountObjects(with: string)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ - (instancetype)initWithType:(DWAmountInputValidatorType)type locale:(nullable N
}
case DWAmountInputValidatorTypeLocalCurrency: {
numberFormatter.maximumFractionDigits = 2;

break;
}
}
Expand Down Expand Up @@ -107,6 +106,10 @@ - (nullable NSString *)validatedAmountStringFromNumberString:(NSString *)validNu

NSNumberFormatter *nf = [numberFormatter copy];
nf.numberStyle = NSNumberFormatterNoStyle;
nf.roundingMode = NSNumberFormatterRoundDown;
nf.minimumIntegerDigits = 1;
nf.minimumFractionDigits = 0;
nf.maximumFractionDigits = numberFormatter.maximumFractionDigits;

NSNumber *number = [nf numberFromString:validNumberString];
if (number == nil) {
Expand All @@ -126,8 +129,8 @@ - (nullable NSString *)validatedAmountStringFromNumberString:(NSString *)validNu
}

NSString *fractionPart = [validNumberString substringFromIndex:separatorIndex + 1];
if (fractionPart.length > numberFormatter.maximumFractionDigits ||
(fractionPart.length == numberFormatter.maximumFractionDigits && fractionPart.integerValue == 0)) {
if (fractionPart.length > nf.maximumFractionDigits ||
(fractionPart.length == nf.maximumFractionDigits && fractionPart.integerValue == 0)) {
return nil;
}

Expand Down
41 changes: 30 additions & 11 deletions DashWalletTests/AmountObjectTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class MockRatesProvider: RatesProvider {
var updateHandler: (([DSCurrencyPriceObject]) -> Void)?

func startExchangeRateFetching() {
if let path = Bundle.main.path(forResource: "rates", ofType: "json") {
if let path = Bundle(for: Self.self).path(forResource: "rates", ofType: "json") {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
let rates = try JSONDecoder().decode(BaseDataResponse<CoinbaseExchangeRate>.self, from: data).data.rates!
Expand Down Expand Up @@ -53,6 +53,7 @@ final class AmountObjectTests: XCTestCase {
private var currencyExchanger = CurrencyExchanger(dataProvider: MockRatesProvider())

override func setUpWithError() throws {
currencyExchanger.startExchangeRateFetching()
// Put setup code here. This method is called before the invocation of each test method in the class.
}

Expand Down Expand Up @@ -108,6 +109,7 @@ final class AmountObjectTests: XCTestCase {
"56%@00",
"123%@45",
"34%@70",
"3412323234%@70",
]

let weirdLocaleIdentifiers: Set<String> = ["fr_CH", "kea_CV", "pt_CV", "en_CV"]
Expand All @@ -119,34 +121,51 @@ final class AmountObjectTests: XCTestCase {
if locale.isNonArabicDigitsLocale { continue }
guard let currencyCode = locale.currencyCode else { continue }

let localFormatter = NumberFormatter.fiatFormatter(currencyCode: currencyCode)
for inputFormat in inputFormats {
let input = String(format: inputFormat, locale.decimalSeparator!)
let inputNumber = Decimal(string: input, locale: locale)!

let mainAmount = AmountObject(plainAmount: inputNumber.plainDashAmount,
fiatCurrencyCode: currencyCode,
localFormatter: NumberFormatter.fiatFormatter(currencyCode: currencyCode),
currencyExchanger: currencyExchanger)
XCTAssert(mainAmount.localAmount.dashAmount.plainAmount == mainAmount.plainAmount)
let amount = AmountObject(plainAmount: inputNumber.plainDashAmount,
fiatCurrencyCode: currencyCode,
localFormatter: localFormatter,
currencyExchanger: currencyExchanger)

let numberFormatter = localFormatter.copy() as! NumberFormatter
numberFormatter.numberStyle = .none
numberFormatter.minimumFractionDigits = localFormatter.minimumFractionDigits
numberFormatter.maximumFractionDigits = localFormatter.maximumFractionDigits
let inputValue = numberFormatter.string(from: amount.supplementaryAmount as NSDecimalNumber)!

XCTAssert(amount.localAmount.amountInternalRepresentation == inputValue)
}
}
}

func testSwaping() {
let numbers: [String] = ["0", "2", "300", "0.1", "0.09", "1.0", "1.0003", "10.79", "0.00054321"]
func testSwapingFromMainToLocal() {
let numbers = ["1234.43"]
let currencyCode = "USD"

for item in numbers {
for (i, item) in numbers.enumerated() {
let localFormatter = NumberFormatter.fiatFormatter(currencyCode: currencyCode)

let amount = AmountObject(dashAmountString: item,
fiatCurrencyCode: currencyCode,
localFormatter: NumberFormatter.fiatFormatter(currencyCode: currencyCode),
localFormatter: localFormatter,
currencyExchanger: currencyExchanger)

XCTAssert(amount.plainAmount == Decimal(string: item)!.plainDashAmount)

let localAmount = amount.localAmount
XCTAssert(amount.plainAmount == localAmount.plainAmount)
XCTAssert(amount.supplementaryFormatted == localAmount.supplementaryFormatted)

let numberFormatter = localFormatter.copy() as! NumberFormatter
numberFormatter.numberStyle = .none
numberFormatter.minimumFractionDigits = localFormatter.minimumFractionDigits
numberFormatter.maximumFractionDigits = localFormatter.maximumFractionDigits
let input = numberFormatter.string(from: amount.supplementaryAmount as NSDecimalNumber)!

XCTAssert(localAmount.amountInternalRepresentation == input)
}
}
}

0 comments on commit 31c6cad

Please sign in to comment.