Skip to content

Commit

Permalink
accepting decimal values as amount input. changed behaviour in Prepar…
Browse files Browse the repository at this point in the history
…eTransactionViewModel to only change the amount textfields text value in the view when receiving an amount externally from QR scan or deeplink (reducing complexity logic and fixing issues with decimal strings with trailing '.0' etc)
  • Loading branch information
Sajjon committed Jun 20, 2019
1 parent abd1cd1 commit 53d3d86
Show file tree
Hide file tree
Showing 27 changed files with 4,071 additions and 3,711 deletions.
2 changes: 1 addition & 1 deletion Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use_modular_headers!

def shared
# The Zilliqa Swift SDK
pod 'Zesame', :git => 'https://github.com/OpenZesame/Zesame.git', :branch => 'bech32'
pod 'Zesame', :git => 'https://github.com/OpenZesame/Zesame.git', :branch => 'decimal_amount'

# Used for Crash reporting
pod 'Firebase/Core'
Expand Down
8 changes: 4 additions & 4 deletions Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ DEPENDENCIES:
- SwiftyBeaver
- TinyConstraints
- Validator
- Zesame (from `https://github.com/OpenZesame/Zesame.git`, branch `bech32`)
- Zesame (from `https://github.com/OpenZesame/Zesame.git`, branch `decimal_amount`)

SPEC REPOS:
https://github.com/cocoapods/specs.git:
Expand Down Expand Up @@ -157,12 +157,12 @@ SPEC REPOS:

EXTERNAL SOURCES:
Zesame:
:branch: bech32
:branch: decimal_amount
:git: https://github.com/OpenZesame/Zesame.git

CHECKOUT OPTIONS:
Zesame:
:commit: 2cc80066fbd3b0346269ddfa66defc597968ff06
:commit: 8da70b2c224da9d186867cf462078b5603f1d21c
:git: https://github.com/OpenZesame/Zesame.git

SPEC CHECKSUMS:
Expand Down Expand Up @@ -202,6 +202,6 @@ SPEC CHECKSUMS:
Validator: b34ab17a8fffd0c1ffd8f9002ead4d1e02cd9e6e
Zesame: c7bbd6548ef648af5480338f37a94bd4fe0bc4e3

PODFILE CHECKSUM: 6e65ec8728ce77747f9fc47866b42ac7c7bfa1f6
PODFILE CHECKSUM: 1fe225be0adb22d399a61c5e12f3713681222bb3

COCOAPODS: 1.6.1
8 changes: 4 additions & 4 deletions Pods/Manifest.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7,256 changes: 3,634 additions & 3,622 deletions Pods/Pods.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion Pods/Zesame/Source/Extensions/String+AmountValidation.swift

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 8 additions & 4 deletions Source/Application/Generated/SwiftGen/L10n-Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -384,8 +384,10 @@ internal enum L10n {
internal static let send = L10n.tr("Localizable", "Scene.PrepareTransaction.Button.Send")
}
internal enum Field {
/// Amount
internal static let amount = L10n.tr("Localizable", "Scene.PrepareTransaction.Field.Amount")
/// Amount in %@
internal static func amount(_ p1: String) -> String {
return L10n.tr("Localizable", "Scene.PrepareTransaction.Field.Amount", p1)
}
/// Encryption password
internal static let encryptionPassword = L10n.tr("Localizable", "Scene.PrepareTransaction.Field.EncryptionPassword")
/// Gas price (min %@)
Expand Down Expand Up @@ -430,8 +432,10 @@ internal enum L10n {
}
}
internal enum Field {
/// Request amount
internal static let requestAmount = L10n.tr("Localizable", "Scene.Receive.Field.RequestAmount")
/// Request amount in %@
internal static func requestAmount(_ p1: String) -> String {
return L10n.tr("Localizable", "Scene.Receive.Field.RequestAmount", p1)
}
}
internal enum Label {
/// My public address
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@ enum AnyValidation {
case valid(withRemark: String?)
case empty
case errorMessage(String)

init<Value, Error>(_ validation: Validation<Value, Error>) where Error: InputError {
switch validation {
case .invalid(let invalid):
switch invalid {
case .empty:
self = .empty
case .error(let error):
self = .errorMessage(error.errorMessage)
}
case .valid(_, let maybeRemark):
self = .valid(withRemark: maybeRemark?.errorMessage)
}
}
}

// MARK: - Convenience Getters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public struct ValidationRuleHexadecimalCharacters: ValidationRule {
public init(error: Swift.Error) {
nestedRule = ValidationRuleCondition<InputType>(error: error) {
guard let inputString = $0 else { return false }
return CharacterSet.hexadecimalDigits.isSuperset(of: CharacterSet(charactersIn: inputString))
return CharacterSet.hexadecimalDigitsIncluding0x.isSuperset(of: CharacterSet(charactersIn: inputString))
}
self.error = error
}
Expand Down
82 changes: 82 additions & 0 deletions Source/Application/InputValidators/Validators/AmountFromText.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//
// MIT License
//
// Copyright (c) 2018-2019 Open Zesame (https://github.com/OpenZesame)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//

import Foundation
import Zesame

enum AmountFromText<Amount: ExpressibleByAmount> {
case amount(Amount, in: Zesame.Unit)
case string(String)
}

extension AmountFromText {
var amount: Amount? {
guard case let .amount(amount, _) = self else { return nil }
return amount
}

var string: String? {
guard case let .string(text) = self else { return nil }
return text
}
}

extension AmountFromText {

init(string amountString: String, unit: Zesame.Unit) throws {
let correctSeparator = Locale.current.decimalSeparatorForSure
let wrongSeparator = correctSeparator == "." ? "," : "."

let incorrectDecimalSeparatorRemoved = amountString.replacingOccurrences(of: wrongSeparator, with: correctSeparator)
do {
let amount: Amount
switch unit {
case .zil:
amount = try Amount(zil: try Zil(trimming: incorrectDecimalSeparatorRemoved))
case .li:
amount = try Amount(li: try Li(trimming: incorrectDecimalSeparatorRemoved))
case .qa:
amount = try Amount(qa: try Qa(trimming: incorrectDecimalSeparatorRemoved))
}
self = .amount(amount, in: unit)
} catch {
if let zilAmountError = error as? Zesame.AmountError<Amount>, zilAmountError == .endsWithDecimalSeparator {
self = .string(incorrectDecimalSeparatorRemoved)
} else if let zilError = error as? Zesame.AmountError<Zil>, zilError == .endsWithDecimalSeparator {
self = .string(incorrectDecimalSeparatorRemoved)
} else {
throw error
}
}
}

var display: String {
switch self {
case .amount(let amount, let unit):
return AmountFormatter().format(amount: amount, in: unit)
case .string(let string):
return string
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,18 @@

import Zesame

struct AmountValidator: InputValidator {
struct AmountValidator<Amount: ExpressibleByAmount>: InputValidator {

typealias Error = AmountError<Zil>
typealias Error = AmountError<Amount>

func validate(input zil: String) -> Validation<ZilAmount, Error> {
func validate(
input: (amountString: String, unit: Zesame.Unit)
) -> Validation<AmountFromText<Amount>, Error> {
let (amountString, unit) = input
do {
return .valid(try ZilAmount(zil: zil))
return .valid(try AmountFromText(string: amountString, unit: unit))
} catch {
return self.error(Error(error: error)!)
return self.error(Error(error: error))
}
}
}
Expand All @@ -43,8 +46,9 @@ enum AmountError<ConvertTo: ExpressibleByAmount>: Swift.Error, InputError {
case tooLarge(max: String, unit: Unit)
case tooSmall(min: String, unit: Unit)
case nonNumericString
case other(Swift.Error)

init?(error: Swift.Error) {
init(error: Swift.Error) {
if let zilError = error as? Zesame.AmountError<Zil> {
self.init(zesameError: zilError)
} else if let liError = error as? Zesame.AmountError<Li> {
Expand All @@ -56,13 +60,13 @@ enum AmountError<ConvertTo: ExpressibleByAmount>: Swift.Error, InputError {
} else if let zilAmountError = error as? Zesame.AmountError<ZilAmount> {
self.init(zesameError: zilAmountError)
} else {
return nil
self = .other(error)
}
}

init<T>(zesameError: Zesame.AmountError<T>) where T: ExpressibleByAmount & Upperbound & Lowerbound {
switch zesameError {
case .nonNumericString: self = .nonNumericString
case .nonNumericString, .endsWithDecimalSeparator, .moreThanOneDecimalSeparator, .tooManyDecimalPlaces, .containsNonDecimalStringCharacter: self = .nonNumericString
case .tooLarge(let max):
let convertedMax = max.asString(in: ConvertTo.unit)
self = .tooLarge(max: convertedMax, unit: ConvertTo.unit)
Expand All @@ -74,7 +78,7 @@ enum AmountError<ConvertTo: ExpressibleByAmount>: Swift.Error, InputError {

init<T>(zesameError: Zesame.AmountError<T>) where T: ExpressibleByAmount & Unbound {
switch zesameError {
case .nonNumericString: self = .nonNumericString
case .nonNumericString, .endsWithDecimalSeparator, .moreThanOneDecimalSeparator, .tooManyDecimalPlaces, .containsNonDecimalStringCharacter: self = .nonNumericString
case .tooLarge, .tooSmall: incorrectImplementation("Unbound amounts should not throw `tooLarge` or `tooSmall` errors")
}
}
Expand All @@ -84,6 +88,8 @@ enum AmountError<ConvertTo: ExpressibleByAmount>: Swift.Error, InputError {
case .tooLarge(let max, let unit): return.tooLarge("\(max.thousands) \(unit.name)")
case .tooSmall(let min, let unit): return.tooSmall("\(min.thousands) \(unit.name)")
case .nonNumericString: return.nonNumericString
case .other(let anyError):
return.nonNumericString // TODO which error message to display?
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ struct GasPriceValidator: InputValidator {
do {
gasPrice = try GasPrice.init(li: li)
} catch {
return self.error(Error(error: error)!)
return self.error(Error(error: error))
}
return .valid(gasPrice)
}
Expand Down
4 changes: 2 additions & 2 deletions Source/Application/Localization/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@
// MARK: PrepareTransaction
"Scene.PrepareTransaction.Title" = "Send";
"Scene.PrepareTransaction.Field.Recipient" = "To address";
"Scene.PrepareTransaction.Field.Amount" = "Amount";
"Scene.PrepareTransaction.Field.Amount" = "Amount in %@";
"Scene.PrepareTransaction.Button.MaxAmount" = "Max";
"Scene.PrepareTransaction.Label.GasInSmallUnits" = "Gas price is measured in %@";
"Scene.PrepareTransaction.Field.GasPrice" = "Gas price (min %@)";
Expand Down Expand Up @@ -208,7 +208,7 @@
"Scene.Receive.Button.CopyMyAddress" = "Copy";
"Scene.Receive.Button.RequestPayment" = "Request payment";
"Scene.Receive.Event.Toast.DidCopyAddress" = "Copied address";
"Scene.Receive.Field.RequestAmount" = "Request amount";
"Scene.Receive.Field.RequestAmount" = "Request amount in %@";

// MARK: - Settings
"Scene.Settings.Title" = "Settings";
Expand Down
Loading

0 comments on commit 53d3d86

Please sign in to comment.