Implement send screen design for native cryptocurrency and ERC20 #1162

pull/1862/head
Vladyslav shepitko 4 years ago committed by Hwee-Boon Yar
parent 136ae801d6
commit 474953e24e
  1. 8
      AlphaWallet.xcodeproj/project.pbxproj
  2. 3
      AlphaWallet/Localization/en.lproj/Localizable.strings
  3. 3
      AlphaWallet/Localization/es.lproj/Localizable.strings
  4. 3
      AlphaWallet/Localization/ja.lproj/Localizable.strings
  5. 3
      AlphaWallet/Localization/ko.lproj/Localizable.strings
  6. 3
      AlphaWallet/Localization/zh-Hans.lproj/Localizable.strings
  7. 29
      AlphaWallet/Tokens/ViewModels/SendViewSectionHeaderViewModel.swift
  8. 77
      AlphaWallet/Tokens/Views/SendViewSectionHeader.swift
  9. 120
      AlphaWallet/Transfer/ViewControllers/SendViewController.swift
  10. 12
      AlphaWallet/Transfer/ViewModels/SendViewModel.swift
  11. 93
      AlphaWallet/UI/AddressTextField.swift
  12. 61
      AlphaWallet/UI/Views/AmountTextField.swift

@ -661,6 +661,8 @@
87D163A4242CD811002662D2 /* AddHideTokensCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87D163A1242CD811002662D2 /* AddHideTokensCoordinator.swift */; };
87D163A7242CD9A2002662D2 /* AddHideTokenSectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87D163A5242CD9A1002662D2 /* AddHideTokenSectionHeaderView.swift */; };
87D163A8242CD9A2002662D2 /* AddHideTokensView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87D163A6242CD9A1002662D2 /* AddHideTokensView.swift */; };
87ED8F8F2484F0D40005C69B /* SendViewSectionHeaderViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87ED8F8E2484F0D40005C69B /* SendViewSectionHeaderViewModel.swift */; };
87ED8F912484F1740005C69B /* SendViewSectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87ED8F902484F1740005C69B /* SendViewSectionHeader.swift */; };
AA26C61F20412A1E00318B9B /* TokensCardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA26C61D20412A1D00318B9B /* TokensCardViewController.swift */; };
AA26C62320412A4100318B9B /* UIViewInspectableEnhancements.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA26C62120412A4100318B9B /* UIViewInspectableEnhancements.swift */; };
AA26C62420412A4100318B9B /* Double.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA26C62220412A4100318B9B /* Double.swift */; };
@ -1378,6 +1380,8 @@
87D163A1242CD811002662D2 /* AddHideTokensCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddHideTokensCoordinator.swift; sourceTree = "<group>"; };
87D163A5242CD9A1002662D2 /* AddHideTokenSectionHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddHideTokenSectionHeaderView.swift; sourceTree = "<group>"; };
87D163A6242CD9A1002662D2 /* AddHideTokensView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddHideTokensView.swift; sourceTree = "<group>"; };
87ED8F8E2484F0D40005C69B /* SendViewSectionHeaderViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendViewSectionHeaderViewModel.swift; sourceTree = "<group>"; };
87ED8F902484F1740005C69B /* SendViewSectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendViewSectionHeader.swift; sourceTree = "<group>"; };
AA26C61D20412A1D00318B9B /* TokensCardViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokensCardViewController.swift; sourceTree = "<group>"; };
AA26C62120412A4100318B9B /* UIViewInspectableEnhancements.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewInspectableEnhancements.swift; sourceTree = "<group>"; };
AA26C62220412A4100318B9B /* Double.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Double.swift; sourceTree = "<group>"; };
@ -2005,6 +2009,7 @@
5E7C73BD3FD2C2844F5DEA45 /* SegmentedControl.swift */,
5E7C78FF8A5682C27E15B488 /* UITableViewCell+TokenCell.swift */,
5E7C709FA1317A56BC12CD35 /* AddHideTokensCell.swift */,
87ED8F902484F1740005C69B /* SendViewSectionHeader.swift */,
);
path = Views;
sourceTree = "<group>";
@ -2053,6 +2058,7 @@
5E7C799AF966DBF0D59DFB20 /* SegmentedControlViewModel.swift */,
8782035E2431FBC300792F12 /* ShowAddHideTokensViewModel.swift */,
87C8018B24350174007648CF /* AddHideTokenSectionHeaderViewModel.swift */,
87ED8F8E2484F0D40005C69B /* SendViewSectionHeaderViewModel.swift */,
);
path = ViewModels;
sourceTree = "<group>";
@ -4194,6 +4200,7 @@
5E7C70AE62DBB193399C7F5E /* ServerViewCell.swift in Sources */,
5E7C7EB845B0EE96CC8DCF43 /* ServerViewModel.swift in Sources */,
5E7C745A423BD10CFDED9A81 /* ServersViewModel.swift in Sources */,
87ED8F912484F1740005C69B /* SendViewSectionHeader.swift in Sources */,
5E7C7ECE164289A89734B4EF /* LocalesCoordinator.swift in Sources */,
5E7C730C6AEF556AFB9A4B2C /* LocalesViewController.swift in Sources */,
5E7C718043636901114BF76C /* LocalesViewModel.swift in Sources */,
@ -4376,6 +4383,7 @@
5E7C79DE18FC5CB46E75753A /* AssetAttributeMapping.swift in Sources */,
5E7C76DD6335158C70AA4F12 /* TokenScriptSignatureVerifier.swift in Sources */,
5E7C77AF8CA540D8F1404B6F /* AssetAttribute.swift in Sources */,
87ED8F8F2484F0D40005C69B /* SendViewSectionHeaderViewModel.swift in Sources */,
5E7C733D2CA2A0FC585D93D1 /* AssetInternalValue.swift in Sources */,
5E7C70794E07FF6B26AE297B /* DirectoryContentsWatcher.swift in Sources */,
5E7C757F9E9F7738B213C8B8 /* AssetDefinitionDiskBackingStore.swift in Sources */,

@ -62,6 +62,9 @@
"send.error.wrongInput" = "Wrong Input";
"send.paste.button.title" = "Paste";
"send.paste.button.addressBook" = "Address Book";
"send.amount" = "Amount";
"send.recipient" = "Recipient";
"send.recipientsAddress" = "Recipient’s Address";
"settings.biometricsDisabled.label.title" = "Passcode";
"settings.biometricsEnabled.label.title" = "Passcode / %@";
"settings.error.failedToSendEmail" = "Failed to send email. Make sure you have Mail app installed.";

@ -62,6 +62,9 @@
"send.error.wrongInput" = "Entrada incorrecta";
"send.paste.button.title" = "Pegar";
"send.paste.button.addressBook" = "Address Book";
"send.amount" = "Amount";
"send.recipient" = "Recipient";
"send.recipientsAddress" = "Recipient’s Address";
"settings.biometricsDisabled.label.title" = "Código de acceso";
"settings.biometricsEnabled.label.title" = "Código de acceso / %@";
"settings.error.failedToSendEmail" = "Error al enviar el correo electrónico. Asegúrate de que tienes una aplicación de correo instalada.";

@ -62,6 +62,9 @@
"send.error.wrongInput" = "誤った入力";
"send.paste.button.title" = "ペースト";
"send.paste.button.addressBook" = "Address Book";
"send.amount" = "Amount";
"send.recipient" = "Recipient";
"send.recipientsAddress" = "Recipient’s Address";
"settings.biometricsDisabled.label.title" = "パスコード";
"settings.biometricsEnabled.label.title" = "パスコード/ %@";
"settings.error.failedToSendEmail" = "メールを送信できませんでした。メール アプリのインストールを確認してください。";

@ -62,6 +62,9 @@
"send.error.wrongInput" = "잘못된 입력";
"send.paste.button.title" = "붙여넣기";
"send.paste.button.addressBook" = "Address Book";
"send.amount" = "Amount";
"send.recipient" = "Recipient";
"send.recipientsAddress" = "Recipient’s Address";
"settings.biometricsDisabled.label.title" = "암호";
"settings.biometricsEnabled.label.title" = "암호 / %@";
"settings.error.failedToSendEmail" = "이메일을 전송하지 못했습니다. 메일 앱이 설치되어 있는지 확인하십시오.";

@ -54,6 +54,9 @@
"send.error.wrongInput" = "错误的输入";
"send.paste.button.title" = "粘贴";
"send.paste.button.addressBook" = "Address Book";
"send.amount" = "Amount";
"send.recipient" = "Recipient";
"send.recipientsAddress" = "Recipient’s Address";
"settings.biometricsDisabled.label.title" = "密码";
"settings.biometricsEnabled.label.title" = "密码 / %@";
"settings.error.failedToSendEmail" = "无法发送邮件。请确保你已经安装了邮件应用。";

@ -0,0 +1,29 @@
//
// SendViewSectionHeaderViewModel.swift
// AlphaWallet
//
// Created by Vladyslav Shepitko on 01.06.2020.
//
import UIKit
struct SendViewSectionHeaderViewModel {
let text: String
var showTopSeparatorLine: Bool = true
var font: UIFont {
return Fonts.semibold(size: 15)!
}
var textColor: UIColor {
return R.color.dove()!
}
var backgroundColor: UIColor {
return R.color.alabaster()!
}
var separatorBackgroundColor: UIColor {
return R.color.mike()!
}
}

@ -0,0 +1,77 @@
//
// SendViewSectionHeader.swift
// AlphaWallet
//
// Created by Vladyslav Shepitko on 01.06.2020.
//
import UIKit
class SendViewSectionHeader: UIView {
private let textLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.setContentHuggingPriority(.required, for: .vertical)
label.setContentCompressionResistancePriority(.required, for: .vertical)
return label
}()
private let topSeparatorView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
private let bottomSeparatorView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
private var topSeparatorLineHeight: NSLayoutConstraint!
init() {
super.init(frame: .zero)
setupView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupView() {
translatesAutoresizingMaskIntoConstraints = false
addSubview(topSeparatorView)
addSubview(textLabel)
addSubview(bottomSeparatorView)
NSLayoutConstraint.activate([
textLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
textLabel.trailingAnchor.constraint(greaterThanOrEqualTo: trailingAnchor, constant: -16),
textLabel.topAnchor.constraint(equalTo: topAnchor, constant: 13),
textLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -13),
topSeparatorView.topAnchor.constraint(equalTo: topAnchor),
topSeparatorView.widthAnchor.constraint(equalTo: widthAnchor),
bottomSeparatorView.topAnchor.constraint(equalTo: bottomAnchor),
bottomSeparatorView.widthAnchor.constraint(equalTo: widthAnchor),
bottomSeparatorView.heightAnchor.constraint(equalToConstant: 1)
])
topSeparatorLineHeight = topSeparatorView.heightAnchor.constraint(equalToConstant: 1)
topSeparatorLineHeight.isActive = true
}
func configure(viewModel: SendViewSectionHeaderViewModel) {
textLabel.text = viewModel.text
textLabel.textColor = viewModel.textColor
textLabel.font = viewModel.font
backgroundColor = viewModel.backgroundColor
topSeparatorView.backgroundColor = viewModel.separatorBackgroundColor
bottomSeparatorView.backgroundColor = viewModel.separatorBackgroundColor
topSeparatorLineHeight.constant = viewModel.showTopSeparatorLine ? 1 : 0
}
}

@ -22,12 +22,12 @@ protocol SendViewControllerDelegate: class, CanOpenURL {
class SendViewController: UIViewController, CanScanQRCode {
private let roundedBackground = RoundedBackground()
private let scrollView = UIScrollView()
private let header = SendHeaderViewWithIntroduction()
private let targetAddressLabel = UILabel()
private let recipientHeader = SendViewSectionHeader()
private let amountHeader = SendViewSectionHeader()
private let recepientAddressLabel = UILabel()
private let amountLabel = UILabel()
private let buttonsBar = ButtonsBar(numberOfButtons: 1)
private var viewModel: SendViewModel
lazy private var headerViewModel = SendHeaderViewViewModelWithIntroduction(server: session.server, assetDefinitionStore: assetDefinitionStore)
private var balanceViewModel: BalanceBaseViewModel?
private let session: WalletSession
private let account: EthereumAccount
@ -40,6 +40,13 @@ class SendViewController: UIViewController, CanScanQRCode {
}()
private var currentSubscribableKeyForNativeCryptoCurrencyBalance: Subscribable<BalanceBaseViewModel>.SubscribableKey?
private var currentSubscribableKeyForNativeCryptoCurrencyPrice: Subscribable<Double>.SubscribableKey?
private let amountViewModel = SendViewSectionHeaderViewModel(
text: R.string.localizable.sendAmount().uppercased(),
showTopSeparatorLine: false
)
private let recipientViewModel = SendViewSectionHeaderViewModel(
text: R.string.localizable.sendRecipient().uppercased()
)
let targetAddressTextField = AddressTextField()
lazy var amountTextField = AmountTextField(server: session.server)
weak var delegate: SendViewControllerDelegate?
@ -84,10 +91,11 @@ class SendViewController: UIViewController, CanScanQRCode {
addressControlsContainer.translatesAutoresizingMaskIntoConstraints = false
addressControlsContainer.backgroundColor = .clear
targetAddressTextField.pasteButton.contentHorizontalAlignment = .right
let addressControlsStackView = [
targetAddressTextField.pasteButton,
targetAddressTextField.clearButton
].asStackView(axis: .horizontal)
].asStackView(axis: .horizontal, alignment: .trailing)
addressControlsStackView.translatesAutoresizingMaskIntoConstraints = false
addressControlsStackView.setContentHuggingPriority(.required, for: .horizontal)
addressControlsStackView.setContentCompressionResistancePriority(.required, for: .horizontal)
@ -95,19 +103,21 @@ class SendViewController: UIViewController, CanScanQRCode {
addressControlsContainer.addSubview(addressControlsStackView)
let stackView = [
header,
.spacer(height: ScreenChecker().isNarrowScreen ? 7 : 14),
amountHeader,
.spacer(height: ScreenChecker().isNarrowScreen ? 7 : 27),
amountLabel,
.spacer(height: ScreenChecker().isNarrowScreen ? 2 : 4),
amountTextField,
.spacer(height: 4),
amountTextField.alternativeAmountLabel,
.spacer(height: ScreenChecker().isNarrowScreen ? 7: 20),
targetAddressLabel,
[.spacerWidth(16), amountTextField.alternativeAmountLabel].asStackView(axis: .horizontal),
.spacer(height: ScreenChecker().isNarrowScreen ? 7: 14),
recipientHeader,
.spacer(height: ScreenChecker().isNarrowScreen ? 7: 16),
[.spacerWidth(16), recepientAddressLabel].asStackView(axis: .horizontal),
.spacer(height: ScreenChecker().isNarrowScreen ? 2 : 4),
targetAddressTextField,
.spacer(height: 4), [
[targetAddressTextField.ensAddressLabel, targetAddressTextField.statusLabel].asStackView(axis: .horizontal, alignment: .leading),
[.spacerWidth(16), targetAddressTextField.ensAddressLabel, targetAddressTextField.statusLabel].asStackView(axis: .horizontal, alignment: .leading),
addressControlsContainer
].asStackView(axis: .horizontal),
].asStackView(axis: .vertical)
@ -122,14 +132,20 @@ class SendViewController: UIViewController, CanScanQRCode {
footerBar.addSubview(buttonsBar)
NSLayoutConstraint.activate([
header.leadingAnchor.constraint(equalTo: roundedBackground.leadingAnchor, constant: 30),
header.trailingAnchor.constraint(equalTo: roundedBackground.trailingAnchor, constant: -30),
amountHeader.leadingAnchor.constraint(equalTo: roundedBackground.leadingAnchor, constant: 0),
amountHeader.trailingAnchor.constraint(equalTo: roundedBackground.trailingAnchor, constant: 0),
amountHeader.heightAnchor.constraint(equalToConstant: 50),
targetAddressTextField.leadingAnchor.constraint(equalTo: roundedBackground.leadingAnchor, constant: 30),
targetAddressTextField.trailingAnchor.constraint(equalTo: roundedBackground.trailingAnchor, constant: -30),
recipientHeader.leadingAnchor.constraint(equalTo: roundedBackground.leadingAnchor, constant: 0),
recipientHeader.trailingAnchor.constraint(equalTo: roundedBackground.trailingAnchor, constant: 0),
recipientHeader.heightAnchor.constraint(equalToConstant: 50),
amountTextField.leadingAnchor.constraint(equalTo: roundedBackground.leadingAnchor, constant: 30),
amountTextField.trailingAnchor.constraint(equalTo: roundedBackground.trailingAnchor, constant: -30),
recepientAddressLabel.heightAnchor.constraint(equalToConstant: 22),
targetAddressTextField.leadingAnchor.constraint(equalTo: roundedBackground.leadingAnchor, constant: 16),
targetAddressTextField.trailingAnchor.constraint(equalTo: roundedBackground.trailingAnchor, constant: -16),
amountTextField.leadingAnchor.constraint(equalTo: roundedBackground.leadingAnchor, constant: 16),
amountTextField.trailingAnchor.constraint(equalTo: roundedBackground.trailingAnchor, constant: -16),
amountTextField.heightAnchor.constraint(equalToConstant: ScreenChecker().isNarrowScreen ? 30 : 50),
stackView.leadingAnchor.constraint(equalTo: roundedBackground.leadingAnchor),
@ -152,7 +168,7 @@ class SendViewController: UIViewController, CanScanQRCode {
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
scrollView.bottomAnchor.constraint(equalTo: footerBar.topAnchor),
addressControlsStackView.trailingAnchor.constraint(equalTo: addressControlsContainer.trailingAnchor),
addressControlsStackView.trailingAnchor.constraint(equalTo: addressControlsContainer.trailingAnchor, constant: -7),
addressControlsStackView.topAnchor.constraint(equalTo: addressControlsContainer.topAnchor),
addressControlsStackView.bottomAnchor.constraint(equalTo: addressControlsContainer.bottomAnchor),
addressControlsStackView.leadingAnchor.constraint(greaterThanOrEqualTo: addressControlsContainer.leadingAnchor),
@ -164,6 +180,11 @@ class SendViewController: UIViewController, CanScanQRCode {
getGasPrice()
}
override func viewDidLoad() {
super.viewDidLoad()
activateAmountView()
}
@objc func closeKeyboard() {
view.endEditing(true)
}
@ -179,17 +200,19 @@ class SendViewController: UIViewController, CanScanQRCode {
view.backgroundColor = viewModel.backgroundColor
headerViewModel.showAlternativeAmount = viewModel.showAlternativeAmount
header.configure(viewModel: headerViewModel)
amountHeader.configure(viewModel: amountViewModel)
recipientHeader.configure(viewModel: recipientViewModel)
targetAddressLabel.font = viewModel.textFieldsLabelFont
targetAddressLabel.textColor = viewModel.textFieldsLabelTextColor
recepientAddressLabel.text = viewModel.recipientsAddress
recepientAddressLabel.font = viewModel.recepientLabelFont
recepientAddressLabel.textColor = viewModel.recepientLabelTextColor
amountLabel.font = viewModel.textFieldsLabelFont
amountLabel.textColor = viewModel.textFieldsLabelTextColor
switch transferType {
case .nativeCryptocurrency(_, let recipient, let amount):
amountTextField.selectCurrencyButton.isHidden = false
if let recipient = recipient {
targetAddressTextField.value = recipient.stringValue
targetAddressTextField.queueEnsResolution(ofValue: recipient.stringValue)
@ -202,7 +225,9 @@ class SendViewController: UIViewController, CanScanQRCode {
self?.amountTextField.cryptoToDollarRate = value
}
}
amountTextField.isAlternativeAmountEnabled = true
case .ERC20Token(_, let recipient, let amount):
amountTextField.selectCurrencyButton.isHidden = true
if let recipient = recipient {
targetAddressTextField.value = recipient.stringValue
targetAddressTextField.queueEnsResolution(ofValue: recipient.stringValue)
@ -213,6 +238,7 @@ class SendViewController: UIViewController, CanScanQRCode {
amountTextField.isAlternativeAmountEnabled = false
amountTextField.isFiatButtonHidden = true
case .ERC875Token, .ERC875TokenOrder, .ERC721Token, .ERC721ForTicketToken, .dapp:
amountTextField.selectCurrencyButton.isHidden = true
amountTextField.isAlternativeAmountEnabled = false
amountTextField.isFiatButtonHidden = true
}
@ -221,6 +247,12 @@ class SendViewController: UIViewController, CanScanQRCode {
let nextButton = buttonsBar.buttons[0]
nextButton.setTitle(R.string.localizable.send(), for: .normal)
nextButton.addTarget(self, action: #selector(send), for: .touchUpInside)
updateNavigationTitle()
}
private func updateNavigationTitle() {
title = "\(R.string.localizable.send()) \(transferType.symbol)"
}
func getGasPrice() {
@ -236,10 +268,10 @@ class SendViewController: UIViewController, CanScanQRCode {
@objc func send() {
let input = targetAddressTextField.value.trimmed
self.targetAddressTextField.errorState = .none
targetAddressTextField.errorState = .none
guard let address = AlphaWallet.Address(string: input) else {
self.targetAddressTextField.errorState = .error(Errors.invalidAddress.prettyError)
targetAddressTextField.errorState = .error(Errors.invalidAddress.prettyError)
return
}
@ -309,47 +341,23 @@ class SendViewController: UIViewController, CanScanQRCode {
currentSubscribableKeyForNativeCryptoCurrencyBalance.flatMap { session.balanceViewModel.unsubscribe($0) }
currentSubscribableKeyForNativeCryptoCurrencyPrice.flatMap { ethPrice.unsubscribe($0) }
switch transferType {
case .nativeCryptocurrency:
case .nativeCryptocurrency(_, let recipient, let amount):
currentSubscribableKeyForNativeCryptoCurrencyBalance = session.balanceViewModel.subscribe { [weak self] viewModel in
guard let celf = self, let viewModel = viewModel else { return }
let amount = viewModel.amountShort
celf.headerViewModel.title = "\(amount) \(celf.session.server.name) (\(viewModel.symbol))"
let etherToken = TokensDataStore.etherToken(forServer: celf.session.server)
let ticker = celf.storage.coinTicker(for: etherToken)
celf.headerViewModel.ticker = ticker
celf.headerViewModel.currencyAmount = celf.session.balanceCoordinator.viewModel.currencyAmount
celf.headerViewModel.currencyAmountWithoutSymbol = celf.session.balanceCoordinator.viewModel.currencyAmountWithoutSymbol
guard let celf = self else { return }
guard let tokenObject = celf.storage.token(forContract: celf.viewModel.transferType.contract) else { return }
//TODO handle if no ens/address? Seems no need to worry for now
guard let ensOrAddress = AddressOrEnsName(string: celf.targetAddressTextField.value) else { return }
let amountAsIntWithDecimals = EtherNumberFormatter.full.number(from: celf.amountTextField.ethCost, decimals: tokenObject.decimals)
celf.configureFor(contract: celf.viewModel.transferType.contract, recipient: ensOrAddress, amount: amountAsIntWithDecimals, shouldConfigureBalance: false)
celf.configureFor(contract: celf.viewModel.transferType.contract, recipient: recipient, amount: amount, shouldConfigureBalance: false)
}
session.refresh(.ethBalance)
case .ERC20Token(let token, _, _):
let viewModel = BalanceTokenViewModel(token: token)
let amount = viewModel.amountShort
//Note that if we want to display the token name directly from token.name, we have to be careful that DAI token's name has trailing \0
headerViewModel.title = "\(amount) \(token.titleInPluralForm(withAssetDefinitionStore: assetDefinitionStore))"
let etherToken = TokensDataStore.etherToken(forServer: session.server)
let ticker = storage.coinTicker(for: etherToken)
headerViewModel.ticker = ticker
headerViewModel.currencyAmount = session.balanceCoordinator.viewModel.currencyAmount
headerViewModel.currencyAmountWithoutSymbol = session.balanceCoordinator.viewModel.currencyAmountWithoutSymbol
//TODO is this the best place to put it? because this func is called configureBalanceViewModel() "balance"
headerViewModel.contractAddress = token.contractAddress
let amountAsIntWithDecimals = EtherNumberFormatter.full.number(from: amountTextField.ethCost, decimals: token.decimals)
guard let ensOrAddress = AddressOrEnsName(string: targetAddressTextField.value) else { return }
configureFor(contract: self.viewModel.transferType.contract, recipient: ensOrAddress, amount: amountAsIntWithDecimals, shouldConfigureBalance: false)
case .ERC20Token(let token, let recipient, let amount):
let amount = amount.flatMap { EtherNumberFormatter.full.number(from: $0, decimals: token.decimals) }
configureFor(contract: viewModel.transferType.contract, recipient: recipient, amount: amount, shouldConfigureBalance: false)
case .ERC875Token, .ERC875TokenOrder, .ERC721Token, .ERC721ForTicketToken, .dapp:
break
}
}
func didScanQRCode(_ result: String) {
self.activateAmountView()
activateAmountView()
guard let result = QRCodeValueParser.from(string: result) else { return }
switch result {
@ -441,7 +449,7 @@ extension SendViewController: AmountTextFieldDelegate {
}
func changeType(in textField: AmountTextField) {
//do nothing
updateNavigationTitle()
}
}
@ -460,7 +468,7 @@ extension SendViewController: AddressTextFieldDelegate {
}
func shouldReturn(in textField: AddressTextField) -> Bool {
activateAmountView()
textField.resignFirstResponder()
return true
}

@ -55,4 +55,16 @@ struct SendViewModel {
var textFieldsLabelFont: UIFont {
return Fonts.regular(size: 10)!
}
var recepientLabelFont: UIFont {
return Fonts.regular(size: 13)!
}
var recepientLabelTextColor: UIColor {
return R.color.dove()!
}
var recipientsAddress: String {
return R.string.localizable.sendRecipientsAddress()
}
}

@ -24,10 +24,10 @@ class AddressTextField: UIControl {
button.setTitleColor(DataEntry.Color.icon, for: .normal)
button.backgroundColor = .clear
button.contentHorizontalAlignment = .right
return button
}()
var clearButton: Button = {
let button = Button(size: .normal, style: .borderless)
button.translatesAutoresizingMaskIntoConstraints = false
@ -36,10 +36,10 @@ class AddressTextField: UIControl {
button.setTitleColor(DataEntry.Color.icon, for: .normal)
button.backgroundColor = .clear
button.contentHorizontalAlignment = .right
return button
}()
let label = UILabel()
let ensAddressLabel: UILabel = {
let label = UILabel()
@ -47,20 +47,20 @@ class AddressTextField: UIControl {
label.numberOfLines = 0
label.setContentHuggingPriority(.required, for: .horizontal)
label.setContentCompressionResistancePriority(.required, for: .horizontal)
return label
}()
let statusLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.setContentHuggingPriority(.required, for: .horizontal)
label.setContentCompressionResistancePriority(.required, for: .horizontal)
return label
}()
var value: String {
get {
if let ensResolvedAddress = ensAddressLabel.text, !ensResolvedAddress.isEmpty {
@ -73,10 +73,10 @@ class AddressTextField: UIControl {
//Client code sometimes sets back the address. We only set (and thus clear the ENS name) if it doesn't match the resolved address
guard ensAddressLabel.text != newValue else { return }
textField.text = newValue
let notification = Notification(name: UITextField.textDidChangeNotification, object: textField)
NotificationCenter.default.post(notification)
clearAddressFromResolvingEnsName()
}
}
@ -89,7 +89,7 @@ class AddressTextField: UIControl {
textField.returnKeyType = newValue
}
}
var errorState: TextField.TextFieldErrorState = .none {
didSet {
switch errorState {
@ -100,14 +100,14 @@ class AddressTextField: UIControl {
self.ensAddressLabel.isHidden = true
case .none:
statusLabel.text = nil
statusLabel.isHidden = true
statusLabel.isHidden = true
}
let borderColor = errorState.textFieldBorderColor(whileEditing: isFirstResponder)
let shouldDropShadow = errorState.textFieldShowShadow(whileEditing: isFirstResponder)
layer.borderColor = borderColor.cgColor
dropShadow(color: shouldDropShadow ? borderColor : .clear, radius: DataEntry.Metric.shadowRadius)
}
}
@ -118,7 +118,7 @@ class AddressTextField: UIControl {
super.init(frame: .zero)
pasteButton.addTarget(self, action: #selector(pasteAction), for: .touchUpInside)
clearButton.addTarget(self, action: #selector(clearAction), for: .touchUpInside)
textField.translatesAutoresizingMaskIntoConstraints = false
textField.delegate = self
textField.leftViewMode = .always
@ -137,25 +137,25 @@ class AddressTextField: UIControl {
ensAddressLabel.font = DataEntry.Font.label
ensAddressLabel.textAlignment = .center
updateClearAndPasteButtons(textField.text ?? "")
NSLayoutConstraint.activate([
textField.anchorsConstraint(to: self),
heightAnchor.constraint(equalToConstant: ScreenChecker().isNarrowScreen ? 30 : 50),
])
NotificationCenter.default.addObserver(self,
selector: #selector(textDidChangeNotification(_:)),
name: UITextField.textDidChangeNotification, object: nil)
}
@objc private func textDidChangeNotification(_ notification: Notification) {
guard textField == notification.object as? UITextField, let text = textField.text else {
return
}
updateClearAndPasteButtons(text)
}
private func updateClearAndPasteButtons(_ text: String) {
clearButton.isHidden = text.isEmpty
pasteButton.isHidden = !text.isEmpty
@ -175,11 +175,11 @@ class AddressTextField: UIControl {
isConfigured = true
cornerRadius = DataEntry.Metric.cornerRadius
label.font = DataEntry.Font.textFieldTitle
label.textColor = DataEntry.Color.label
label.textAlignment = .left
ensAddressLabel.font = DataEntry.Font.label
ensAddressLabel.textColor = DataEntry.Color.ensText
ensAddressLabel.isHidden = true
@ -193,27 +193,27 @@ class AddressTextField: UIControl {
textField.placeholder = R.string.localizable.addressEnsLabelMessage()
textField.autocapitalizationType = .none
textField.autocorrectionType = .no
statusLabel.font = DataEntry.Font.textFieldStatus
statusLabel.textColor = DataEntry.Color.textFieldStatus
statusLabel.textAlignment = .left
textField.textColor = DataEntry.Color.text
textField.font = DataEntry.Font.textField
layer.borderWidth = DataEntry.Metric.borderThickness
backgroundColor = DataEntry.Color.textFieldBackground
layer.borderColor = errorState.textFieldBorderColor(whileEditing: isFirstResponder).cgColor
errorState = .none
}
private func makeTargetAddressRightView() -> UIView {
let scanQRCodeButton = Button(size: .normal, style: .borderless)
scanQRCodeButton.translatesAutoresizingMaskIntoConstraints = false
scanQRCodeButton.setImage(R.image.qr_code_icon(), for: .normal)
scanQRCodeButton.addTarget(self, action: #selector(openReader), for: .touchUpInside)
scanQRCodeButton.backgroundColor = .clear
let targetAddressRightView = [scanQRCodeButton].asStackView(distribution: .fill)
//As of iOS 13, we need to constrain the width of `rightView`
let rightViewFittingSize = targetAddressRightView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
@ -224,23 +224,23 @@ class AddressTextField: UIControl {
return targetAddressRightView
}
@objc func clearAction() {
textField.text?.removeAll()
clearAddressFromResolvingEnsName()
let notification = Notification(name: UITextField.textDidChangeNotification, object: textField)
NotificationCenter.default.post(notification)
}
@objc func pasteAction() {
clearAddressFromResolvingEnsName()
guard let value = UIPasteboard.general.string?.trimmed else {
delegate?.displayError(error: SendInputErrors.emptyClipBoard, for: self)
return
}
if CryptoAddressValidator.isValidAddress(value) {
self.value = value
delegate?.didPaste(in: self)
@ -252,23 +252,23 @@ class AddressTextField: UIControl {
textField.text = value
let notification = Notification(name: UITextField.textDidChangeNotification, object: textField)
NotificationCenter.default.post(notification)
GetENSAddressCoordinator(server: serverToResolveEns).getENSAddressFromResolver(for: value) { result in
guard let address = result.value else {
//Don't show an error when pasting what seems like a wrong ENS name for better usability
self.delegate?.didPaste(in: self)
return
}
guard CryptoAddressValidator.isValidAddress(address.address) else {
self.delegate?.displayError(error: Errors.invalidAddress, for: self)
return
}
self.errorState = .none
self.ensAddressLabel.isHidden = false
self.ensAddressLabel.text = address.address
self.delegate?.didPaste(in: self)
}
}
@ -280,7 +280,7 @@ class AddressTextField: UIControl {
func queueEnsResolution(ofValue value: String) {
errorState = .none
let value = value.trimmed
guard value.isPossibleEnsName else { return }
let oldTextValue = textField.text?.trimmed
@ -291,7 +291,7 @@ class AddressTextField: UIControl {
//TODO good to show an error message in the UI/label that it is not a valid ENS name
return
}
guard oldTextValue == strongSelf.textField.text?.trimmed else { return }
strongSelf.ensAddressLabel.isHidden = false
strongSelf.ensAddressLabel.text = address.address
@ -303,27 +303,32 @@ class AddressTextField: UIControl {
ensAddressLabel.text = nil
ensAddressLabel.isHidden = true
}
override func resignFirstResponder() -> Bool {
super.resignFirstResponder()
return textField.resignFirstResponder()
}
}
extension AddressTextField: UITextFieldDelegate {
func textFieldDidEndEditing(_ textField: UITextField) {
let borderColor = errorState.textFieldBorderColor(whileEditing: false)
let shouldDropShadow = errorState.textFieldShowShadow(whileEditing: false)
layer.borderColor = borderColor.cgColor
backgroundColor = DataEntry.Color.textFieldBackground
dropShadow(color: shouldDropShadow ? borderColor : .clear, radius: DataEntry.Metric.shadowRadius)
}
func textFieldDidBeginEditing(_ textField: UITextField) {
let borderColor = errorState.textFieldBorderColor(whileEditing: true)
layer.borderColor = borderColor.cgColor
backgroundColor = Colors.appWhite
dropShadow(color: borderColor, radius: DataEntry.Metric.shadowRadius)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
guard let delegate = delegate else { return true }
return delegate.shouldReturn(in: self)

@ -11,7 +11,7 @@ class AmountTextField: UIControl {
enum Currency {
case cryptoCurrency(String, UIImage)
case usd(String)
var icon: UIImage {
switch self {
case .cryptoCurrency(_, let image):
@ -37,6 +37,7 @@ class AmountTextField: UIControl {
.font: DataEntry.Font.amountTextField!, .foregroundColor: DataEntry.Color.placeholder
])
textField.translatesAutoresizingMaskIntoConstraints = false
textField.adjustsFontSizeToFitWidth = true
textField.delegate = self
textField.keyboardType = .decimalPad
textField.leftViewMode = .always
@ -45,23 +46,22 @@ class AmountTextField: UIControl {
textField.textColor = .black
textField.font = DataEntry.Font.amountTextField
textField.textAlignment = .right
return textField
}()
let statusLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
var cryptoToDollarRate: Double? = nil {
didSet {
if let _ = cryptoToDollarRate {
updateAlternatePricingDisplay()
}
isAlternativeAmountEnabled = cryptoToDollarRate != nil
}
}
var ethCost: String {
@ -76,12 +76,14 @@ class AmountTextField: UIControl {
set {
switch currentPair.left {
case .cryptoCurrency:
break
textField.text = newValue
case .usd:
currentPair = currentPair.swapPair()
updateFiatButtonTitle()
if let amount = Double(newValue), let cryptoToDollarRate = cryptoToDollarRate {
textField.text = String(amount * cryptoToDollarRate)
} else {
textField.text = ""
}
}
textField.text = newValue
updateAlternatePricingDisplay()
}
}
@ -112,33 +114,33 @@ class AmountTextField: UIControl {
label.numberOfLines = 0
label.textColor = Colors.appGreenContrastBackground
label.font = DataEntry.Font.label
return label
}()
lazy var selectCurrencyButton: SelectCurrencyButton = {
let button = SelectCurrencyButton()
switch self.currentPair.left {
switch currentPair.left {
case .cryptoCurrency(let symbol, _), .usd(let symbol):
button.text = symbol
button.image = self.currentPair.left.icon
button.image = currentPair.left.icon
}
button.addTarget(self, action: #selector(fiatAction), for: .touchUpInside)
return button
}()
private var allowedCharacters: String = {
let decimalSeparator = Locale.current.decimalSeparator ?? ""
return "0123456789" + decimalSeparator + EtherNumberFormatter.decimalPoint
}()
lazy var decimalFormatter: DecimalFormatter = {
return DecimalFormatter()
}()
var isAlternativeAmountEnabled: Bool {
get {
return !alternativeAmountLabel.isHidden
@ -147,9 +149,16 @@ class AmountTextField: UIControl {
alternativeAmountLabel.isHidden = !newValue
}
}
var currencySymbol: String {
switch currentPair.left {
case .cryptoCurrency(let symbol, _), .usd(let symbol):
return symbol
}
}
weak var delegate: AmountTextFieldDelegate?
init(server: RPCServer) {
switch server {
case .xDai:
@ -161,11 +170,11 @@ class AmountTextField: UIControl {
super.init(frame: .zero)
translatesAutoresizingMaskIntoConstraints = false
let stackView = [selectCurrencyButton, .spacerWidth(4), textField].asStackView(axis: .horizontal)
stackView.translatesAutoresizingMaskIntoConstraints = false
addSubview(stackView)
computeAlternateAmount()
isAlternativeAmountEnabled = cryptoToDollarRate != nil
NSLayoutConstraint.activate([
@ -175,7 +184,7 @@ class AmountTextField: UIControl {
@objc func fiatAction(button: UIButton) {
guard cryptoToDollarRate != nil else { return }
let oldAlternateAmount = convertToAlternateAmount()
currentPair = currentPair.swapPair()
updateFiatButtonTitle()
@ -290,7 +299,7 @@ class AmountTextField: UIControl {
}
extension AmountTextField: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let allowChange = amountChanged(in: range, to: string)
if allowChange {

Loading…
Cancel
Save