| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 |
- //
- // LNExchangePanel.swift
- // Lanu
- //
- // Created by OneeChan on 2026/1/8.
- //
- import Foundation
- import UIKit
- import SnapKit
- enum LNExchangeType {
- case coinToDiamond
- case diamondToCoin
- case beanToCoin
- case beanToDiamond
-
- var exchangeType: (from: LNCurrencyType, to: LNCurrencyType) {
- switch self {
- case .coinToDiamond: (.coin, .diamond)
- case .diamondToCoin: (.diamond, .coin)
- case .beanToCoin: (.bean, .coin)
- case .beanToDiamond: (.bean, .diamond)
- }
- }
- }
- class LNExchangePanel: LNPopupView {
- private let exchangeType: LNExchangeType
-
- private let exchangeView = UIView()
- private let remainLabel = UILabel()
- private let exchangeInput = UITextField()
- private let expectLabel = UILabel()
-
- private let exchangeButton = UIButton()
- private var delayTaskId: String?
-
- init(exchangeType: LNExchangeType) {
- self.exchangeType = exchangeType
- super.init(frame: .zero)
-
- setupViews()
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
- }
- extension LNExchangePanel: UITextFieldDelegate {
- func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
- let currentText = textField.text ?? ""
-
- guard let range = Range(range, in: currentText) else { return false }
- let newText = currentText.replacingCharacters(in: range, with: string)
- if newText.isEmpty {
- return true
- }
- if newText.starts(with: "00") {
- return false
- }
-
- if newText.filter({ $0 == "." }).count > 2 {
- return false
- }
-
- guard Double(newText) != nil else {
- return false
- }
-
- return true
- }
-
- func textFieldShouldReturn(_ textField: UITextField) -> Bool {
- textField.resignFirstResponder()
- return true
- }
- }
- extension LNExchangePanel {
- private func checkExchangeButton() {
- let text = exchangeInput.text ?? ""
- if text.isEmpty {
- exchangeInput.attributedPlaceholder = .init(string: .init(key: "A00261"),
- attributes: [.font: UIFont.heading_h3,
- .foregroundColor: UIColor.text_2])
- } else {
- exchangeInput.attributedPlaceholder = nil
- }
-
- fetchExchangeResult()
- }
-
- private func fetchExchangeResult() {
- LNDelayTask.cancel(key: delayTaskId)
-
- exchangeButton.isEnabled = false
- exchangeButton.setBackgroundImage(nil, for: .normal)
- delayTaskId = LNDelayTask.perform(delay: 1.0) { [weak self] in
- guard let self else { return }
- let text = exchangeInput.text ?? ""
- let value = Double(text) ?? 0
- if value == 0 {
- expectLabel.text = "0"
- return
- }
- LNPurchaseManager.shared.expectResult(exchangeType: exchangeType, count: value)
- { [weak self] result in
- guard let self else { return }
- guard exchangeInput.text == text else { return }
-
- if let result {
- expectLabel.text = "\(result)"
- exchangeButton.isEnabled = true
- exchangeButton.setBackgroundImage(.primary_8, for: .normal)
- } else {
- expectLabel.text = ""
- }
- }
- }
- }
-
- private func setupViews() {
- let titleView = buildTitleView()
- container.addSubview(titleView)
- titleView.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview()
- make.top.equalToSuperview()
- }
-
- let remainView = buildRemain()
- container.addSubview(remainView)
- remainView.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview()
- make.top.equalTo(titleView.snp.bottom).offset(4)
- }
-
- exchangeView.backgroundColor = .fill_1
- exchangeView.layer.cornerRadius = 12
- container.addSubview(exchangeView)
- exchangeView.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview().inset(16)
- make.top.equalTo(remainView.snp.bottom).offset(4)
- }
-
- let diamondExchange = buildExchangeView()
- exchangeView.addSubview(diamondExchange)
- diamondExchange.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview()
- make.top.equalToSuperview()
- }
-
- let line = UIView()
- line.backgroundColor = .fill_3
- exchangeView.addSubview(line)
- line.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview().inset(10)
- make.top.equalTo(diamondExchange.snp.bottom).offset(4)
- make.height.equalTo(1)
- }
-
- let coinView = buildExpectView()
- exchangeView.addSubview(coinView)
- coinView.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview()
- make.bottom.equalToSuperview()
- make.top.equalTo(line.snp.bottom).offset(4)
- }
-
- exchangeButton.setTitle(.init(key: "A00262"), for: .normal)
- exchangeButton.setTitleColor(.text_1, for: .normal)
- exchangeButton.titleLabel?.font = .heading_h3
- exchangeButton.backgroundColor = .text_2
- exchangeButton.layer.cornerRadius = 23.5
- exchangeButton.clipsToBounds = true
- exchangeButton.isEnabled = false
- exchangeButton.setBackgroundImage(nil, for: .normal)
- exchangeButton.addAction(UIAction(handler: { [weak self] _ in
- guard let self else { return }
- guard let amout = Double(exchangeInput.text ?? "") else { return }
- guard amout > 0 else { return }
- LNPurchaseManager.shared.exchangeCurrency(exchangeType: exchangeType, amount: amout) { [weak self] success in
- guard let self else { return }
- guard success else { return }
- dismiss()
- showToast(.init(key: "A00263"))
- }
- }), for: .touchUpInside)
- container.addSubview(exchangeButton)
- exchangeButton.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview().inset(16)
- make.height.equalTo(47)
- make.top.equalTo(exchangeView.snp.bottom).offset(24)
- make.bottom.equalToSuperview().offset(commonBottomInset)
- }
- }
-
- private func buildTitleView() -> UIView {
- let titleView = UIView()
- titleView.isUserInteractionEnabled = false
- titleView.snp.makeConstraints { make in
- make.height.equalTo(50)
- }
-
- let titleLabel = UILabel()
- titleLabel.text = switch exchangeType {
- case .coinToDiamond, .beanToDiamond: .init(key: "A00264")
- case .diamondToCoin, .beanToCoin: .init(key: "A00273")
- }
- titleLabel.font = .heading_h3
- titleLabel.textColor = .text_5
- titleView.addSubview(titleLabel)
- titleLabel.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(16)
- make.centerY.equalToSuperview()
- }
-
- return titleView
- }
-
- private func buildRemain() -> UIView {
- let container = UIView()
- container.onTap { [weak self] in
- guard let self else { return }
- endEditing(true)
- }
- container.snp.makeConstraints { make in
- make.height.equalTo(36)
- }
-
- let exchange = UIButton()
- exchange.setTitle(.init(key: "A00265"), for: .normal)
- exchange.setTitleColor(.text_6, for: .normal)
- exchange.titleLabel?.font = .body_xs
- exchange.addAction(UIAction(handler: { [weak self] _ in
- guard let self else { return }
- exchangeInput.text = switch exchangeType {
- case .coinToDiamond: myWalletInfo.coin.toDisplay
- case .diamondToCoin: myWalletInfo.diamond.toDisplay
- case .beanToCoin, .beanToDiamond: myWalletInfo.availableBean.toDisplay
- }
- checkExchangeButton()
- }), for: .touchUpInside)
- container.addSubview(exchange)
- exchange.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.trailing.equalToSuperview().offset(-16)
- }
-
- let currencyIcon: UIImageView = switch exchangeType {
- case .coinToDiamond: .coinImageView()
- case .diamondToCoin: .diamondImageView()
- case .beanToCoin, .beanToDiamond: .beanImageView()
- }
- container.addSubview(currencyIcon)
- currencyIcon.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(16)
- make.centerY.equalToSuperview()
- make.width.height.equalTo(24)
- }
-
- remainLabel.text = switch exchangeType {
- case .coinToDiamond: myWalletInfo.coin.toDisplay
- case .diamondToCoin: myWalletInfo.diamond.toDisplay
- case .beanToCoin, .beanToDiamond: myWalletInfo.availableBean.toDisplay
- }
- remainLabel.font = .heading_h1
- remainLabel.textColor = .text_5
- container.addSubview(remainLabel)
- remainLabel.snp.makeConstraints { make in
- make.leading.equalTo(currencyIcon.snp.trailing).offset(2)
- make.centerY.equalToSuperview()
- }
-
- return container
- }
-
- private func buildExchangeView() -> UIView {
- let container = UIView()
- container.onTap { [weak self] in
- guard let self else { return }
- endEditing(true)
- }
- container.snp.makeConstraints { make in
- make.height.equalTo(67)
- }
-
- let titleLabel = UILabel()
- titleLabel.text = switch exchangeType {
- case .coinToDiamond: .init(key: "A00266")
- case .diamondToCoin: .init(key: "A00274")
- case .beanToCoin, .beanToDiamond: .init(key: "B00106")
- }
- titleLabel.font = .heading_h4
- titleLabel.textColor = .text_5
- container.addSubview(titleLabel)
- titleLabel.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(12)
- make.centerY.equalToSuperview()
- }
-
- exchangeInput.font = .heading_h2
- exchangeInput.textColor = .text_5
- exchangeInput.visibleView = exchangeView
- exchangeInput.delegate = self
- exchangeInput.returnKeyType = .done
- exchangeInput.attributedPlaceholder = .init(
- string: .init(key: "A00261"),
- attributes: [.font: UIFont.heading_h3,
- .foregroundColor: UIColor.text_2])
- exchangeInput.keyboardType = .decimalPad
- exchangeInput.addAction(UIAction(handler: { [weak self] _ in
- guard let self else { return }
- checkExchangeButton()
- }), for: .editingChanged)
- container.addSubview(exchangeInput)
- exchangeInput.snp.makeConstraints { make in
- make.trailing.equalToSuperview().offset(-12)
- make.centerY.equalToSuperview()
- }
-
- let expectIcon: UIImageView = switch exchangeType {
- case .coinToDiamond: .coinImageView()
- case .diamondToCoin: .diamondImageView()
- case .beanToCoin, .beanToDiamond: .beanImageView()
- }
- container.addSubview(expectIcon)
- expectIcon.snp.makeConstraints { make in
- make.trailing.equalTo(exchangeInput.snp.leading).offset(-2)
- make.centerY.equalToSuperview()
- make.width.height.equalTo(20)
- }
-
- return container
- }
-
- private func buildExpectView() -> UIView {
- let container = UIView()
- container.snp.makeConstraints { make in
- make.height.equalTo(67)
- }
-
- let titleLabel = UILabel()
- titleLabel.text = switch exchangeType {
- case .coinToDiamond, .beanToDiamond: .init(key: "A00267")
- case .diamondToCoin, .beanToCoin: .init(key: "A00275")
- }
- titleLabel.font = .heading_h4
- titleLabel.textColor = .text_5
- container.addSubview(titleLabel)
- titleLabel.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(12)
- make.centerY.equalToSuperview()
- }
-
- expectLabel.text = "0"
- expectLabel.font = .heading_h2
- expectLabel.textColor = .text_5
- container.addSubview(expectLabel)
- expectLabel.snp.makeConstraints { make in
- make.trailing.equalToSuperview().offset(-12)
- make.centerY.equalToSuperview()
- }
-
- let expectIcon: UIImageView = switch exchangeType {
- case .coinToDiamond, .beanToDiamond: .diamondImageView()
- case .diamondToCoin, .beanToCoin: .coinImageView()
- }
- container.addSubview(expectIcon)
- expectIcon.snp.makeConstraints { make in
- make.trailing.equalTo(expectLabel.snp.leading).offset(-2)
- make.centerY.equalToSuperview()
- make.width.height.equalTo(20)
- }
-
- return container
- }
- }
|