351 lines
14 KiB
Swift
351 lines
14 KiB
Swift
//
|
|
// SwapControllerMain.swift
|
|
// Wallet
|
|
//
|
|
// Created by Saveliy Stavitsky on 3/1/21.
|
|
// Copyright © 2021 AM. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
import WalletFoundation
|
|
|
|
//https://www.hackingwithswift.com/example-code/language/how-to-split-an-array-into-chunks
|
|
extension Array {
|
|
func chunked(into size: Int) -> [[Element]] {
|
|
return stride(from: 0, to: count, by: size).map {
|
|
Array(self[$0 ..< Swift.min($0 + size, count)])
|
|
}
|
|
}
|
|
}
|
|
|
|
class SwapControllerMain: UIViewController {
|
|
|
|
let service = Swap.Service.Pools()
|
|
|
|
@IBOutlet private weak var tokenGiveView: CommonViewCard!
|
|
@IBOutlet private weak var tokenGetStackView: UIStackView!
|
|
@IBOutlet private weak var changeTokensButton: UIButton!
|
|
@IBOutlet private weak var tokenGetView: CommonViewCard!
|
|
|
|
@IBOutlet private weak var conversionRateStackView: UIStackView!
|
|
@IBOutlet private weak var forwardConversionRate: UILabel!
|
|
@IBOutlet private weak var backwardConversionRate: UILabel!
|
|
|
|
@IBOutlet private weak var conversionFieldsStackView: UIStackView!
|
|
@IBOutlet private weak var amount1TextField: CommonTextField!
|
|
@IBOutlet private weak var amount2TextField: CommonTextField!
|
|
|
|
@IBOutlet private weak var minimumTextField: CommonTextField!
|
|
|
|
@IBOutlet private weak var routeContainerView: UIView!
|
|
@IBOutlet private weak var routeStackView: UIStackView!
|
|
@IBOutlet private weak var routeTokensStackView: UIStackView!
|
|
|
|
@IBOutlet var swapButton: CommonButtonAction!
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
self.title = L10n.Swap.Main.title
|
|
self.navigationItem.leftBarButtonItem = .pop(self)
|
|
tabBarItem.title = L10n.Swap.Main.TabBar.title
|
|
|
|
tokenGiveView.action = .cards(collection: nil)
|
|
tokenGetView.action = .cards(collection: nil)
|
|
|
|
for textField in [amount1TextField, amount2TextField, minimumTextField] {
|
|
textField?.delegate = self
|
|
textField?.keyboardType = .decimalPad
|
|
}
|
|
|
|
swapButton.isEnabled = false
|
|
|
|
service.didUpdate = { [weak self] in
|
|
self?.setupTokensViews()
|
|
}
|
|
}
|
|
|
|
override func viewWillAppear(_ animated: Bool) {
|
|
super.viewWillAppear(animated)
|
|
Accounts().isLocked = true
|
|
service.tokenGive = nil
|
|
service.tokenGet = nil
|
|
setupTokensViews()
|
|
service.fetch {
|
|
Alert.error($0)
|
|
}
|
|
|
|
self.navigationController >>- {
|
|
$0.navigationBar.isTranslucent = false
|
|
$0.setNavigationBarHidden(false, animated: true)
|
|
}
|
|
}
|
|
|
|
override func viewWillDisappear(_ animated: Bool) {
|
|
super.viewWillDisappear(animated)
|
|
Accounts().isLocked = false
|
|
}
|
|
|
|
func setupTokensViews() {
|
|
if service.showPreselectError {
|
|
service.showPreselectError = false
|
|
Popup.show(title: L10n.Swap.Main.PreselectToken.errorTitle,
|
|
text: L10n.Swap.Main.PreselectToken.errorText,
|
|
submit: L10n.Common.Button.understand)
|
|
}
|
|
|
|
tokenGiveView.action = .cards(
|
|
tokens: service.tokensFrom,
|
|
selected: service.tokenGive,
|
|
empty: .menu(
|
|
image: Asset.commonToken.image.tinted(with: Asset.deepWater.color),
|
|
title: L10n.Swap.Main.TokenGive.titleCard), { [weak self] in
|
|
self?.service.tokenGive = self?.service.tokensFrom.object(menu: $0)
|
|
self?.amount1TextField.precision = self?.service.tokensFrom.object(menu: $0)?.precision
|
|
self?.setupTokensViews()
|
|
})
|
|
|
|
tokenGetStackView.isHidden = service.tokenGive == nil
|
|
tokenGetView.action = .cards(
|
|
tokens: service.tokensTo,
|
|
selected: service.tokenGet,
|
|
empty: .menu(
|
|
image: Asset.commonToken.image.tinted(with: Asset.deepWater.color),
|
|
title: L10n.Swap.Main.TokenGet.titleCard), { [weak self] in
|
|
self?.service.tokenGet = self?.service.tokensTo.object(menu: $0)
|
|
self?.amount2TextField.precision = self?.service.tokensTo.object(menu: $0)?.precision
|
|
self?.minimumTextField.precision = self?.service.tokensTo.object(menu: $0)?.precision
|
|
self?.setupTokensViews()
|
|
})
|
|
|
|
changeTokensButton.isHidden = service.tokenGive == nil || service.tokenGet == nil
|
|
minimumTextField.isHidden = service.amountSend.isEmpty || service.amountReceive.isEmpty
|
|
conversionRateStackView.isHidden = tokenGetStackView.isHidden || service.tokenGet == nil
|
|
conversionFieldsStackView.isHidden = conversionRateStackView.isHidden
|
|
routeContainerView.isHidden = service.tokenGive == nil || service.tokenGet == nil
|
|
routeStackView.isHidden = service.tokenGive == nil || service.tokenGet == nil
|
|
|
|
guard let tokenGive = service.tokenGive, let tokenGet = service.tokenGet else {
|
|
return
|
|
}
|
|
let processAmount = service.amountSend.toOptionalDecimal() != nil
|
|
? service.processRate(amount: service.amountSend.toDecimal()) / service.amountSend.toDecimal()
|
|
: service.processRate(amount: 1.0)
|
|
forwardConversionRate.lzText = "\(Decimal(1).toString(precision: tokenGive)) ≈ \(processAmount.toString(precision: tokenGet))"
|
|
backwardConversionRate.lzText = "(\(Decimal(1).toString(precision: tokenGet)) ≈ \((1 / processAmount).toString(precision: tokenGive)))"
|
|
|
|
let giveImage = UIImageView(token: tokenGive)
|
|
giveImage.addHeightWidthConstraints(height: 24, width: 24)
|
|
amount1TextField.accessoryView = giveImage
|
|
amount1TextField.value = service.amountSend
|
|
amount1TextField.error = service.amountSendError
|
|
|
|
let getImage = UIImageView(token: tokenGet)
|
|
getImage.addHeightWidthConstraints(height: 24, width: 24)
|
|
amount2TextField.accessoryView = getImage
|
|
amount2TextField.value = service.amountReceive
|
|
amount2TextField.error = service.amountReceiveError
|
|
|
|
minimumTextField.value = service.amountMinimum
|
|
// minimumTextField.error = service.amountMinimumShowError ? L10n.Swap.Main.Minimum.error : nil
|
|
|
|
routeTokensStackView.arrangedSubviews.forEach({ $0.removeFromSuperview() })
|
|
|
|
for tokensChunk in service.route.chunked(into: (Int(UIScreen.main.bounds.width) - 32) / 85) {
|
|
let stackViewLine = UIStackView(subviews: [], axis: .horizontal, distribution: .fill, alignment: .center, spacing: 0)
|
|
for token in tokensChunk {
|
|
let image = UIImageView(token: token)
|
|
image.addHeightWidthConstraints(height: 32, width: 32)
|
|
let label = UILabel()
|
|
label.font = UIFont.font(style: .medium, size: 10)
|
|
label.textColor = Asset.textCoal.color
|
|
label.textAlignment = .center
|
|
label.text = token.symbol
|
|
label.addHeightWidthConstraints(height: 12, width: 65)
|
|
let stack = UIStackView(subviews: [image, label], axis: .vertical, distribution: .fill, alignment: .center, spacing: 2)
|
|
stackViewLine.addArrangedSubview(stack)
|
|
|
|
let imageArrow = UIImageView(image: Asset.commonArrowRight.image)
|
|
imageArrow.addHeightWidthConstraints(height: 20, width: 20)
|
|
stackViewLine.addArrangedSubview(imageArrow)
|
|
}
|
|
routeTokensStackView.addArrangedSubview(stackViewLine)
|
|
}
|
|
(routeTokensStackView.arrangedSubviews.last as? UIStackView)?
|
|
.arrangedSubviews.last?.removeFromSuperview()
|
|
|
|
swapButton.isEnabled = !amount1TextField.value.isEmpty && !amount2TextField.value.isEmpty
|
|
&& service.amountSendError == nil && service.amountReceiveError == nil
|
|
&& service.amountSend.toDecimal() > 0 && service.amountReceive.toDecimal() > 0
|
|
// && (service.amountMinimum ?? 0) > 0
|
|
}
|
|
|
|
@IBAction func changeTokensPressed(_ sender: Any) {
|
|
if let tokenGive = service.tokenGive, let tokenGet = service.tokenGet {
|
|
service.tokenGive = tokenGet
|
|
amount1TextField.precision = tokenGet.precision
|
|
|
|
service.tokenGet = tokenGive
|
|
amount2TextField.precision = tokenGive.precision
|
|
minimumTextField.precision = tokenGive.precision
|
|
|
|
service.preselectTokenGive = nil
|
|
|
|
setupTokensViews()
|
|
}
|
|
}
|
|
|
|
@IBAction func swapPressed(_ sender: Any) {
|
|
view.endEditing(true)
|
|
guard let tokenGive = service.tokenGive, let tokenGet = service.tokenGet else {
|
|
return
|
|
}
|
|
|
|
let amountSend = service.amountSend.toDecimal().toString(max: tokenGive)
|
|
let amountReceive = service.amountReceive.toDecimal().toString(max: tokenGet)
|
|
|
|
var views: [UIView] = [
|
|
.label(
|
|
text: L10n.Swap.Main.Confirmation.description
|
|
.attributed(style: .regular, size: 14, color: Asset.textCoal.color),
|
|
numberOfLines: 0
|
|
),
|
|
.field(
|
|
title: L10n.Swap.Main.Confirmation.give,
|
|
text: amountSend
|
|
),
|
|
.field(
|
|
title: L10n.Swap.Main.Confirmation.get,
|
|
text: amountReceive
|
|
)
|
|
]
|
|
if let amountMinimum = service.amountMinimum.toOptionalDecimal() {
|
|
views.append(
|
|
.field(
|
|
title: L10n.Swap.Main.Confirmation.minimum,
|
|
titleStyle: "regular_16",
|
|
titleColor: Asset.textPebble.color,
|
|
text: amountMinimum.toString(max: tokenGet),
|
|
textStyle: "regular_16", textColor: Asset.textPebble.color
|
|
)
|
|
)
|
|
}
|
|
Popup.show(title: L10n.Common.Title.confirmation, views: views, submit: L10n.Common.Button.confirm, in: self) { ctrl in
|
|
AccountViewAuthorize.showGetPrivateKey(in: self) { [weak self] in
|
|
Loader.show(in: ctrl)
|
|
self?.service.submit(privateKey: $0) { [weak self] (result) in
|
|
guard let self = self else { return }
|
|
Loader.hide(in: ctrl)
|
|
ctrl.dismiss(animated: true)
|
|
switch result {
|
|
case .success(let transactionId):
|
|
self.showSuccessfulPopup(amountSend: amountSend, amountReceive: amountReceive, transactionId: transactionId,
|
|
completion: {
|
|
self.clear()
|
|
self.service.tokenGet = self.service.getToken(tokenGet)
|
|
self.service.tokenGive = self.service.getToken(tokenGive)
|
|
self.service.fetch({ _ in })
|
|
})
|
|
|
|
case let .failure(error):
|
|
Alert.notify(error, pop: self)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private func clear() {
|
|
amount1TextField.value = .init()
|
|
amount2TextField.value = .init()
|
|
minimumTextField.value = .init()
|
|
service.update(send: "")
|
|
service.update(receive: "")
|
|
service.update(minimum: "")
|
|
}
|
|
}
|
|
|
|
extension SwapControllerMain: CommonTextFieldDelegate {
|
|
|
|
func textField(_ textField: CommonTextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
|
let newString = (textField.value as NSString).replacingCharacters(in: range, with: string)
|
|
switch textField {
|
|
case amount1TextField:
|
|
if let precision = service.tokenGive?.precision,
|
|
newString.precision > precision {
|
|
return false
|
|
}
|
|
case amount2TextField:
|
|
if let precision = service.tokenGet?.precision,
|
|
newString.precision > precision {
|
|
return false
|
|
}
|
|
case minimumTextField:
|
|
if let precision = service.tokenGet?.precision,
|
|
newString.precision > precision {
|
|
return false
|
|
}
|
|
default:
|
|
break
|
|
}
|
|
return true
|
|
}
|
|
|
|
func textFieldDidChange(_ textField: CommonTextField) {
|
|
switch textField {
|
|
case amount1TextField:
|
|
textField.value = textField.value.replacingOccurrences(of: ",", with: ".")
|
|
let newPosition = textField.textField.endOfDocument
|
|
textField.textField.selectedTextRange = textField.textField.textRange(from: newPosition, to: newPosition)
|
|
service.update(send: textField.value)
|
|
case amount2TextField:
|
|
textField.value = textField.value.replacingOccurrences(of: ",", with: ".")
|
|
let newPosition = textField.textField.endOfDocument
|
|
textField.textField.selectedTextRange = textField.textField.textRange(from: newPosition, to: newPosition)
|
|
service.update(receive: textField.value)
|
|
case minimumTextField:
|
|
textField.value = textField.value.replacingOccurrences(of: ",", with: ".")
|
|
let newPosition = textField.textField.endOfDocument
|
|
textField.textField.selectedTextRange = textField.textField.textRange(from: newPosition, to: newPosition)
|
|
service.update(minimum: textField.value)
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
extension SwapControllerMain {
|
|
fileprivate func showSuccessfulPopup(amountSend: String,
|
|
amountReceive: String,
|
|
transactionId: String,
|
|
completion: @escaping () -> Void) {
|
|
let views: [UIView] = [
|
|
.field(
|
|
title: L10n.Swap.Main.Success.give,
|
|
text: amountSend
|
|
),
|
|
.field(
|
|
title: L10n.Swap.Main.Success.get,
|
|
text: amountReceive
|
|
),
|
|
.field(
|
|
title: L10n.Swap.Main.Success.id,
|
|
text: transactionId,
|
|
rightButtonAvailbale: true,
|
|
rightButtonImage: Asset.commonCopy.image,
|
|
rightButtonAction: {
|
|
Alert.copy(transactionId)
|
|
}
|
|
)
|
|
]
|
|
|
|
Popup.show(title: L10n.Swap.Main.Success.title, views: views, submit: L10n.Common.Button.checkOnBloksIO, submitStyle: .outline, cancel: L10n.Common.Button.done, in: self, { ctrl in
|
|
guard let checkTransactionUrl = URL(transactionId: transactionId) else { return }
|
|
|
|
UIApplication.shared.open(checkTransactionUrl, options: [:], completionHandler: nil)
|
|
}, {
|
|
completion()
|
|
})
|
|
}
|
|
}
|