* Add pair to send view controller.

* Add TokensDataStore dependansy to the view controllers.

* Show actual price of the toke.

* Update condition for section update.

* Send proper amount in tokens to the transaction.

* Reset field value on pair change.

* Update of the appearence.

* Update of the footer view

* Update of the section.

* Clear code from section variable.

* Hide section footer and fiat button if we do not have price.

* Update prices on transaction view controller.

* Fix for Unit tests.

* Update of the send view controller.

* Fix test that should be off.
pull/2/head
Oleg Gordiichuk 7 years ago committed by Michael Scoff
parent 06ed371b3a
commit 2769013b6d
  1. 14
      Podfile.lock
  2. 19
      Trust/InCoordinator.swift
  3. 6
      Trust/Transactions/Coordinators/TransactionCoordinator.swift
  4. 8
      Trust/Transactions/ViewControllers/TransactionsViewController.swift
  5. 12
      Trust/Transfer/Coordinators/PaymentCoordinator.swift
  6. 8
      Trust/Transfer/Coordinators/SendCoordinator.swift
  7. 10
      Trust/Transfer/Types/TransferType.swift
  8. 124
      Trust/Transfer/ViewControllers/SendViewController.swift
  9. 4
      Trust/Transfer/ViewModels/SendViewModel.swift
  10. 10
      TrustTests/Coordinators/SendCoordinatorTests.swift
  11. 33
      TrustTests/Coordinators/TransactionCoordinatorTests.swift
  12. 10
      TrustTests/ViewControllers/PaymentCoordinator.swift

@ -17,7 +17,7 @@ PODS:
- Result (~> 3.0)
- KeychainSwift (10.0.0)
- Kingfisher (4.6.1)
- Lokalise (0.8.0)
- Lokalise (0.8.1)
- MBProgressHUD (1.1.0)
- Moya (10.0.1):
- Moya/Core (= 10.0.1)
@ -34,12 +34,12 @@ PODS:
- RealmSwift (3.0.2):
- Realm (= 3.0.2)
- Result (3.2.4)
- secp256k1_ios (0.0.7)
- secp256k1_ios (0.0.9)
- SeedStackViewController (0.4.0)
- SipHash (1.2.0)
- SSKeychain (1.4.1)
- StatefulViewController (3.0)
- SwiftLint (0.24.0)
- SwiftLint (0.24.1)
- TrustKeystore (0.0.4):
- CryptoSwift
- secp256k1_ios (~> 0.0.7)
@ -83,7 +83,7 @@ EXTERNAL SOURCES:
CHECKOUT OPTIONS:
CryptoSwift:
:commit: 289ef07b2f386d91a25b9393a96b0c3daa1d9114
:commit: 46cfb548f83b89a13ce99f452223933c31fac5ba
:git: https://github.com/krzyzanowskim/CryptoSwift
JSONRPCKit:
:commit: 50d19a4f7ec593ac5e07cffa1e11c17f1fbe347d
@ -106,7 +106,7 @@ SPEC CHECKSUMS:
JSONRPCKit: 22132c575ba2dc6f2f4ae72fda4943a63efca686
KeychainSwift: f9f7910449a0c0fd2cabc889121530dd2c477c33
Kingfisher: 1f9157d9c02b380cbd0b7cc890161195164eb634
Lokalise: 9547ef438a2d25cfba0b4c02201d3c39b3db35fb
Lokalise: d81f4ccce8cd2c1589bf73bb39fab7781d4f7cbc
MBProgressHUD: e7baa36a220447d8aeb12769bf0585582f3866d9
Moya: 9e621707ff754eeb51ff3ec51a3d54e517c0733a
QRCodeReaderViewController: e8f27d035b3e72b1d4b1c61ff66458287e3be0ff
@ -115,12 +115,12 @@ SPEC CHECKSUMS:
Realm: 6f23fd1f178a09342eac21bfa7c2bf4312a7a180
RealmSwift: 695393add1b8f9d5fa75dd16e6355cf3935f71e2
Result: d2d07204ce72856f1fd9130bbe42c35a7b0fea10
secp256k1_ios: 12a9bddc3d7aa723efbb32b42413f041b11a7765
secp256k1_ios: 1abf641309cd2b1ec1c5f4ca76be50623cbd57bc
SeedStackViewController: 45e88ca1493a610e74d661d3feced7098f72dbd3
SipHash: c6e9e43e9c531b5bc6602545130c26194a6d31ce
SSKeychain: 55cc80f66f5c73da827e3077f02e43528897db41
StatefulViewController: 4803bf900d44de26074344998e10e041113b5931
SwiftLint: a014c92b4664e8b13f380f8640a51bb1733778ba
SwiftLint: 2e4b89feed5909c42c3735bbd6745f4345c4b772
TrustKeystore: 2c62775c98df86b7e2ef78c059581a4e8c98a054
VENTouchLock: 20d378b9c6173e2c054448aeb3fb2f40822a9f3f

@ -67,7 +67,13 @@ class InCoordinator: Coordinator {
account: account,
config: config
)
MigrationInitializer(account: account, chainID: config.chainID).perform()
let tokensStorage = TokensDataStore(
session: session,
configuration: RealmConfiguration.configuration(for: session.account, chainID: session.config.chainID)
)
let transactionsStorage = TransactionsStorage(
configuration: RealmConfiguration.configuration(for: account, chainID: session.config.chainID)
@ -76,7 +82,8 @@ class InCoordinator: Coordinator {
let transactionCoordinator = TransactionCoordinator(
session: session,
storage: transactionsStorage,
keystore: keystore
keystore: keystore,
tokensStorage: tokensStorage
)
transactionCoordinator.rootViewController.tabBarItem = UITabBarItem(title: NSLocalizedString("transactions.tabbar.item.title", value: "Transactions", comment: ""), image: R.image.feed(), selectedImage: nil)
transactionCoordinator.delegate = self
@ -107,10 +114,6 @@ class InCoordinator: Coordinator {
}
if inCoordinatorViewModel.tokensAvailable {
let tokensStorage = TokensDataStore(
session: session,
configuration: RealmConfiguration.configuration(for: session.account, chainID: session.config.chainID)
)
let tokenCoordinator = TokensCoordinator(
session: session,
keystore: keystore,
@ -184,14 +187,16 @@ class InCoordinator: Coordinator {
func showPaymentFlow(for type: PaymentFlow) {
guard let transactionCoordinator = transactionCoordinator else { return }
let session = transactionCoordinator.session
let tokenStorage = transactionCoordinator.tokensStorage
switch session.account.type {
case .real(let account):
let coordinator = PaymentCoordinator(
flow: type,
session: session,
account: account,
keystore: keystore
keystore: keystore,
storage: tokenStorage,
account: account
)
coordinator.delegate = self
navigationController.present(coordinator.navigationController, animated: true, completion: nil)

@ -29,6 +29,7 @@ class TransactionCoordinator: Coordinator {
weak var delegate: TransactionCoordinatorDelegate?
let session: WalletSession
let tokensStorage: TokensDataStore
let navigationController: UINavigationController
var coordinators: [Coordinator] = []
@ -36,12 +37,14 @@ class TransactionCoordinator: Coordinator {
session: WalletSession,
navigationController: UINavigationController = NavigationController(),
storage: TransactionsStorage,
keystore: Keystore
keystore: Keystore,
tokensStorage: TokensDataStore
) {
self.session = session
self.keystore = keystore
self.navigationController = navigationController
self.storage = storage
self.tokensStorage = tokensStorage
NotificationCenter.default.addObserver(self, selector: #selector(didEnterForeground), name: .UIApplicationWillEnterForeground, object: nil)
}
@ -56,6 +59,7 @@ class TransactionCoordinator: Coordinator {
account: account,
dataCoordinator: dataCoordinator,
session: session,
tokensStorage: tokensStorage,
viewModel: viewModel
)

@ -18,7 +18,9 @@ class TransactionsViewController: UIViewController {
var viewModel: TransactionsViewModel
let tokensStorage: TokensDataStore
let account: Wallet
let tableView = UITableView(frame: .zero, style: .plain)
let refreshControl = UIRefreshControl()
@ -44,15 +46,17 @@ class TransactionsViewController: UIViewController {
account: Wallet,
dataCoordinator: TransactionDataCoordinator,
session: WalletSession,
tokensStorage: TokensDataStore,
viewModel: TransactionsViewModel = TransactionsViewModel(transactions: [])
) {
self.account = account
self.dataCoordinator = dataCoordinator
self.session = session
self.viewModel = viewModel
self.tokensStorage = tokensStorage
super.init(nibName: nil, bundle: nil)
tokensStorage.updatePrices()
view.backgroundColor = viewModel.backgroundColor
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.delegate = self

@ -17,6 +17,7 @@ class PaymentCoordinator: Coordinator {
var coordinators: [Coordinator] = []
let navigationController: UINavigationController
let keystore: Keystore
let storage: TokensDataStore
let account: Account
lazy var transferType: TransferType = {
@ -32,8 +33,9 @@ class PaymentCoordinator: Coordinator {
navigationController: UINavigationController = UINavigationController(),
flow: PaymentFlow,
session: WalletSession,
account: Account,
keystore: Keystore
keystore: Keystore,
storage: TokensDataStore,
account: Account
) {
self.navigationController = navigationController
self.navigationController.modalPresentationStyle = .formSheet
@ -41,6 +43,7 @@ class PaymentCoordinator: Coordinator {
self.account = account
self.flow = flow
self.keystore = keystore
self.storage = storage
}
func start() {
@ -50,8 +53,9 @@ class PaymentCoordinator: Coordinator {
transferType: type,
navigationController: navigationController,
session: session,
account: account,
keystore: keystore
keystore: keystore,
storage: storage,
account: account
)
coordinator.delegate = self
coordinator.start()

@ -16,6 +16,7 @@ class SendCoordinator: Coordinator {
let account: Account
let navigationController: UINavigationController
let keystore: Keystore
let storage: TokensDataStore
var coordinators: [Coordinator] = []
weak var delegate: SendCoordinatorDelegate?
lazy var sendViewController: SendViewController = {
@ -26,8 +27,9 @@ class SendCoordinator: Coordinator {
transferType: TransferType,
navigationController: UINavigationController = UINavigationController(),
session: WalletSession,
account: Account,
keystore: Keystore
keystore: Keystore,
storage: TokensDataStore,
account: Account
) {
self.transferType = transferType
self.navigationController = navigationController
@ -35,6 +37,7 @@ class SendCoordinator: Coordinator {
self.session = session
self.account = account
self.keystore = keystore
self.storage = storage
}
func start() {
@ -44,6 +47,7 @@ class SendCoordinator: Coordinator {
func makeSendViewController() -> SendViewController {
let controller = SendViewController(
session: session,
storage: storage,
account: account,
transferType: transferType
)

@ -19,4 +19,14 @@ extension TransferType {
case .exchange: return "--"
}
}
func contract() -> String {
switch self {
case .ether:
return "0x"
case .token(let token):
return token.contract
case .exchange: return "--"
}
}
}

@ -30,10 +30,21 @@ class SendViewController: FormViewController {
static let address = "address"
static let amount = "amount"
}
struct Pair {
let left: String
let right: String
func swapPair() -> Pair {
return Pair(left: right, right: left)
}
}
var pairValue = 0.0
let session: WalletSession
let account: Account
let transferType: TransferType
let storage: TokensDataStore
var addressRow: TextFloatLabelRow? {
return form.rowBy(tag: Values.address) as? TextFloatLabelRow
@ -41,19 +52,29 @@ class SendViewController: FormViewController {
var amountRow: TextFloatLabelRow? {
return form.rowBy(tag: Values.amount) as? TextFloatLabelRow
}
private var gasPrice: BigInt?
lazy var currentPair: Pair = {
return Pair(left: viewModel.symbol, right: Config().currency.rawValue)
}()
init(
session: WalletSession,
storage: TokensDataStore,
account: Account,
transferType: TransferType = .ether(destination: .none)
) {
self.session = session
self.account = account
self.transferType = transferType
self.storage = storage
super.init(nibName: nil, bundle: nil)
storage.updatePrices()
getGasPrice()
title = viewModel.title
view.backgroundColor = viewModel.backgroundColor
@ -82,18 +103,24 @@ class SendViewController: FormViewController {
maxButton.translatesAutoresizingMaskIntoConstraints = false
maxButton.setTitle(NSLocalizedString("send.max.button.title", value: "Max", comment: ""), for: .normal)
maxButton.addTarget(self, action: #selector(useMaxAmount), for: .touchUpInside)
let fiatButton = Button(size: .normal, style: .borderless)
fiatButton.translatesAutoresizingMaskIntoConstraints = false
fiatButton.setTitle(currentPair.right, for: .normal)
fiatButton.addTarget(self, action: #selector(fiatAction), for: .touchUpInside)
fiatButton.isHidden = isFiatViewHidden()
let amountRightView = UIStackView(arrangedSubviews: [
maxButton,
fiatButton,
])
amountRightView.translatesAutoresizingMaskIntoConstraints = false
amountRightView.distribution = .equalSpacing
amountRightView.spacing = 10
amountRightView.spacing = 1
amountRightView.axis = .horizontal
form = Section()
+++ Section("")
+++ Section(header: "", footer: isFiatViewHidden() ? "" : "~ \(String(self.pairValue)) " + "\(currentPair.right)")
<<< AppFormAppearance.textFieldFloat(tag: Values.address) {
$0.add(rule: EthereumAddressRule())
$0.validationOptions = .validatesOnDemand
@ -104,25 +131,17 @@ class SendViewController: FormViewController {
cell.textField.rightViewMode = .always
cell.textField.accessibilityIdentifier = "amount-field"
}
<<< AppFormAppearance.textFieldFloat(tag: Values.amount) {
$0.add(rule: RuleRequired())
$0.validationOptions = .validatesOnDemand
}.cellUpdate {[weak self] cell, _ in
cell.textField.textAlignment = .left
cell.textField.placeholder = "\(self?.viewModel.symbol ?? "") " + NSLocalizedString("send.amount.textField.placeholder", value: "Amount", comment: "")
cell.textField.delegate = self
cell.textField.placeholder = "\(self?.currentPair.left ?? "") " + NSLocalizedString("send.amount.textField.placeholder", value: "Amount", comment: "")
cell.textField.keyboardType = .decimalPad
//cell.textField.rightView = maxButton // TODO Enable it's ready
cell.textField.rightView = amountRightView
cell.textField.rightViewMode = .always
}
+++ Section {
$0.hidden = Eureka.Condition.function([Values.amount], { [weak self] _ in
return self?.amountRow?.value?.isEmpty ?? true
})
}
getGasPrice()
}
func getGasPrice() {
@ -149,7 +168,12 @@ class SendViewController: FormViewController {
guard errors.isEmpty else { return }
let addressString = addressRow?.value?.trimmed ?? ""
let amountString = amountRow?.value?.trimmed ?? ""
var amountString = ""
if self.currentPair.left == viewModel.symbol {
amountString = amountRow?.value?.trimmed ?? ""
} else {
amountString = String(pairValue).trimmed
}
let address = Address(string: addressString)
@ -205,6 +229,23 @@ class SendViewController: FormViewController {
amountRow?.value = value
amountRow?.reload()
}
@objc func fiatAction(sender: UIButton) {
let swappedPair = currentPair.swapPair()
//New pair for future calculation we should swap pair each time we press fiat button.
self.currentPair = swappedPair
//Update button title.
sender.setTitle(currentPair.right, for: .normal)
//Reset amountRow value.
amountRow?.value = nil
amountRow?.reload()
//Reset pair value.
pairValue = 0.0
//Update section.
updatePriceSection()
//Set focuse on pair change.
activateAmountView()
}
func activateAmountView() {
amountRow?.cell.textField.becomeFirstResponder()
@ -213,6 +254,37 @@ class SendViewController: FormViewController {
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func updatePriceSection() {
//We use this section update to prevent update of the all section including cells.
UIView.setAnimationsEnabled(false)
tableView.beginUpdates()
if let containerView = tableView.footerView(forSection: 1) {
containerView.textLabel!.text = "~ \(String(self.pairValue)) " + "\(currentPair.right)"
containerView.sizeToFit()
}
tableView.endUpdates()
UIView.setAnimationsEnabled(true)
}
private func updatePairPrice(with amount: Double) {
guard let rates = storage.tickers, let currentTokenInfo = rates[viewModel.contract], let price = Double(currentTokenInfo.price) else {
return
}
if self.currentPair.left == viewModel.symbol {
pairValue = amount * price
} else {
pairValue = amount / price
}
self.updatePriceSection()
}
private func isFiatViewHidden() -> Bool {
guard let rates = storage.tickers, let currentTokenInfo = rates[viewModel.contract], let _ = Double(currentTokenInfo.price) else {
return true
}
return false
}
}
extension SendViewController: QRCodeReaderDelegate {
@ -230,3 +302,17 @@ extension SendViewController: QRCodeReaderDelegate {
activateAmountView()
}
}
extension SendViewController: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let text = (textField.text as NSString?)?.replacingCharacters(in: range, with: string)
guard let total = text, let amount = Double(total) else {
//Should be done in another way.
pairValue = 0.0
updatePriceSection()
return true
}
self.updatePairPrice(with: amount)
return true
}
}

@ -23,6 +23,10 @@ struct SendViewModel {
var symbol: String {
return transferType.symbol(server: config.server)
}
var contract: String {
return transferType.contract()
}
var backgroundColor: UIColor {
return .white

@ -11,8 +11,9 @@ class SendCoordinatorTests: XCTestCase {
transferType: .ether(destination: .none),
navigationController: FakeNavigationController(),
session: .make(),
account: .make(),
keystore: FakeKeystore()
keystore: FakeKeystore(),
storage: FakeTokensDataStore(),
account: .make()
)
coordinator.start()
@ -26,8 +27,9 @@ class SendCoordinatorTests: XCTestCase {
transferType: .ether(destination: address),
navigationController: FakeNavigationController(),
session: .make(),
account: .make(),
keystore: FakeKeystore()
keystore: FakeKeystore(),
storage: FakeTokensDataStore(),
account: .make()
)
coordinator.start()

@ -4,5 +4,38 @@ import XCTest
@testable import Trust
class TransactionCoordinatorTests: XCTestCase {
/*
func testShowSendFlow() {
let coordinator = TransactionCoordinator(
session: .make(),
navigationController: FakeNavigationController(),
storage: FakeTransactionsStorage(),
keystore: FakeEtherKeystore(),
tokensStorage: FakeTokensDataStore()
)
coordinator.showPaymentFlow(for: .send(type: .ether(destination: .none)))
let controller = (coordinator.navigationController.presentedViewController as? UINavigationController)?.viewControllers[0]
XCTAssertTrue(coordinator.coordinators.first is PaymentCoordinator)
XCTAssertTrue(controller is SendViewController)
}
func testShowRequstFlow() {
let coordinator = TransactionCoordinator(
session: .make(),
navigationController: FakeNavigationController(),
storage: FakeTransactionsStorage(),
keystore: FakeEtherKeystore(),
tokensStorage: FakeTokensDataStore()
)
coordinator.showPaymentFlow(for: .request)
let controller = (coordinator.navigationController.presentedViewController as? UINavigationController)?.viewControllers[0]
XCTAssertTrue(coordinator.coordinators.first is PaymentCoordinator)
XCTAssertTrue(controller is RequestViewController)
}
*/
}

@ -12,8 +12,9 @@ class PaymentCoordinatorTests: XCTestCase {
navigationController: FakeNavigationController(),
flow: .send(type: .ether(destination: address)),
session: .make(),
account: .make(),
keystore: FakeKeystore()
keystore: FakeKeystore(),
storage: FakeTokensDataStore(),
account: .make()
)
coordinator.start()
@ -26,8 +27,9 @@ class PaymentCoordinatorTests: XCTestCase {
navigationController: FakeNavigationController(),
flow: .request,
session: .make(),
account: .make(),
keystore: FakeKeystore()
keystore: FakeKeystore(),
storage: FakeTokensDataStore(),
account: .make()
)
coordinator.start()

Loading…
Cancel
Save