273 lines
9.3 KiB
Swift
273 lines
9.3 KiB
Swift
|
|
//
|
||
|
|
// CoinsRechargeSheet.swift
|
||
|
|
// Crush
|
||
|
|
//
|
||
|
|
// Created by Leon on 2025/9/16.
|
||
|
|
//
|
||
|
|
|
||
|
|
import UIKit
|
||
|
|
import Combine
|
||
|
|
|
||
|
|
class CoinsRechargeSheet: EGPopBaseView {
|
||
|
|
var titleLabel: UILabel!
|
||
|
|
var descLabel: CLLabel!
|
||
|
|
var closeButton: EPIconTertiaryButton!
|
||
|
|
|
||
|
|
var bottomView: UIView!
|
||
|
|
var balanceLabel: CLIconLabel!
|
||
|
|
var rechargeButton: StyleButton!
|
||
|
|
|
||
|
|
var layout = UICollectionViewFlowLayout()
|
||
|
|
var cv: UICollectionView!
|
||
|
|
|
||
|
|
// MARK: - 数据源和状态管理
|
||
|
|
private var cancellables = Set<AnyCancellable>()
|
||
|
|
@Published private var datas = [IAPProducts]()
|
||
|
|
@Published private var selectIndex = 0
|
||
|
|
@Published private var selectProduct: IAPProducts?
|
||
|
|
private let viewModel = CoinsRechargeViewModel()
|
||
|
|
|
||
|
|
init() {
|
||
|
|
super.init(direction: .bottom)
|
||
|
|
|
||
|
|
contentView.backgroundColor = .c.csbn
|
||
|
|
contentLength = 549 + UIWindow.safeAreaBottom
|
||
|
|
|
||
|
|
setupViews()
|
||
|
|
setupDatas()
|
||
|
|
setupEvents()
|
||
|
|
}
|
||
|
|
|
||
|
|
required init?(coder: NSCoder) {
|
||
|
|
fatalError("init(coder:) has not been implemented")
|
||
|
|
}
|
||
|
|
|
||
|
|
private func setupViews() {
|
||
|
|
contentView.backgroundColor = .c.csbn
|
||
|
|
|
||
|
|
closeButton = {
|
||
|
|
let v = EPIconTertiaryButton(radius: .round, iconSize: .small, iconCode: .delete)
|
||
|
|
v.addTarget(self, action: #selector(bgButtonPressed), for: .touchUpInside)
|
||
|
|
contentView.addSubview(v)
|
||
|
|
v.snp.makeConstraints { make in
|
||
|
|
make.top.equalToSuperview().offset(20)
|
||
|
|
make.size.equalTo(v.bgImageSize())
|
||
|
|
make.trailing.equalToSuperview().offset(-16)
|
||
|
|
}
|
||
|
|
return v
|
||
|
|
}()
|
||
|
|
|
||
|
|
titleLabel = {
|
||
|
|
let v = UILabel()
|
||
|
|
v.font = .t.ttm
|
||
|
|
v.textColor = .text
|
||
|
|
v.textAlignment = .center
|
||
|
|
contentView.addSubview(v)
|
||
|
|
v.snp.makeConstraints { make in
|
||
|
|
make.leading.equalToSuperview().offset(24)
|
||
|
|
make.trailing.equalToSuperview().offset(-24)
|
||
|
|
make.top.equalToSuperview().offset(32)
|
||
|
|
}
|
||
|
|
return v
|
||
|
|
}()
|
||
|
|
|
||
|
|
descLabel = {
|
||
|
|
let v = CLLabel()
|
||
|
|
v.font = .t.tbs
|
||
|
|
v.numberOfLines = 2
|
||
|
|
contentView.addSubview(v)
|
||
|
|
v.snp.makeConstraints { make in
|
||
|
|
make.leading.equalToSuperview().offset(CGFloat.lrs)
|
||
|
|
make.trailing.equalToSuperview().offset(-CGFloat.lrs)
|
||
|
|
// make.top.equalTo(titleLabel.snp.bottom).offset(24)
|
||
|
|
// make.top.equalToSuperview().offset(80)
|
||
|
|
make.centerY.equalTo(contentView.snp.top).offset(80 + 10)
|
||
|
|
}
|
||
|
|
return v
|
||
|
|
}()
|
||
|
|
|
||
|
|
bottomView = {
|
||
|
|
let v = UIView()
|
||
|
|
contentView.addSubview(v)
|
||
|
|
v.snp.makeConstraints { make in
|
||
|
|
make.leading.trailing.equalToSuperview()
|
||
|
|
make.bottom.equalToSuperview().offset(-UIWindow.safeAreaBottom)
|
||
|
|
make.height.equalTo(80)
|
||
|
|
}
|
||
|
|
return v
|
||
|
|
}()
|
||
|
|
|
||
|
|
balanceLabel = {
|
||
|
|
let v = CLIconLabel()
|
||
|
|
v.spacing = 8
|
||
|
|
v.iconSize = CGSize(width: 24, height: 24)
|
||
|
|
v.iconImageView.image = UIImage.icon32Diamond
|
||
|
|
v.contentLabel.textColor = .text
|
||
|
|
v.contentLabel.font = .t.tndm
|
||
|
|
v.contentLabel.text = "0"
|
||
|
|
bottomView.addSubview(v)
|
||
|
|
v.snp.makeConstraints { make in
|
||
|
|
make.leading.equalToSuperview().offset(24)
|
||
|
|
make.centerY.equalToSuperview()
|
||
|
|
}
|
||
|
|
return v
|
||
|
|
}()
|
||
|
|
|
||
|
|
rechargeButton = {
|
||
|
|
let v = StyleButton()
|
||
|
|
v.primary(size: .large)
|
||
|
|
v.addTarget(self, action: #selector(rechargeButtonAction), for: .touchUpInside)
|
||
|
|
bottomView.addSubview(v)
|
||
|
|
v.snp.makeConstraints { make in
|
||
|
|
make.centerY.equalToSuperview()
|
||
|
|
make.trailing.equalToSuperview().offset(-24)
|
||
|
|
//make.leading.equalToSuperview().offset(<#T##amount: any ConstraintOffsetTarget##any ConstraintOffsetTarget#>)
|
||
|
|
make.leading.greaterThanOrEqualTo(balanceLabel.snp.trailing).offset(16)
|
||
|
|
}
|
||
|
|
return v
|
||
|
|
}()
|
||
|
|
|
||
|
|
do {
|
||
|
|
let width = floor((UIScreen.width - 24 * 2 - 16) * 0.5)
|
||
|
|
let height = 96.0
|
||
|
|
|
||
|
|
layout.scrollDirection = .vertical
|
||
|
|
layout.itemSize = CGSize(width: width, height: height)
|
||
|
|
layout.minimumLineSpacing = 16
|
||
|
|
layout.minimumInteritemSpacing = 16
|
||
|
|
layout.sectionInset = UIEdgeInsets(top: 0, left: 24, bottom: 0, right: 24)
|
||
|
|
|
||
|
|
cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
||
|
|
cv.backgroundColor = .clear
|
||
|
|
cv.showsHorizontalScrollIndicator = false
|
||
|
|
cv.delegate = self
|
||
|
|
cv.dataSource = self
|
||
|
|
cv.contentInsetAdjustmentBehavior = .never
|
||
|
|
cv.register(CoinsRechargeGridCell.self, forCellWithReuseIdentifier: "CoinsRechargeGridCell")
|
||
|
|
|
||
|
|
|
||
|
|
contentView.addSubview(cv)
|
||
|
|
cv.snp.makeConstraints { make in
|
||
|
|
make.leading.trailing.equalToSuperview()
|
||
|
|
make.top.equalToSuperview().offset(136)
|
||
|
|
//make.bottom.equalToSuperview()
|
||
|
|
make.bottom.equalTo(bottomView.snp.top)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
titleLabel.text = "Crush Coin insufficient"
|
||
|
|
descLabel.text = "The Crush coin is insufficient and cannot continue, please recharge"
|
||
|
|
rechargeButton.setTitle("Recharge", for: .normal)
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - 事件设置
|
||
|
|
private func setupEvents() {
|
||
|
|
// 监听选择索引变化
|
||
|
|
$selectIndex.sink { [weak self] index in
|
||
|
|
guard let self = self else { return }
|
||
|
|
if index < self.datas.count {
|
||
|
|
self.selectProduct = self.datas[index]
|
||
|
|
}
|
||
|
|
}.store(in: &cancellables)
|
||
|
|
|
||
|
|
$datas.sink {[weak self] products in
|
||
|
|
self?.selectIndex = self?.selectIndex ?? 0
|
||
|
|
}.store(in: &cancellables)
|
||
|
|
|
||
|
|
// 监听钱包余额变化
|
||
|
|
WalletCore.shared.$balance.sink { [weak self] wallet in
|
||
|
|
self?.balanceLabel.contentLabel.text = wallet.displayBalance()
|
||
|
|
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
|
||
|
|
// self?.dismiss()
|
||
|
|
// }
|
||
|
|
}.store(in: &cancellables)
|
||
|
|
|
||
|
|
NotificationCenter.default.addObserver(self, selector: #selector(notifyChargeDone), name: AppNotificationName.chargeDonePushTradeId.notificationName, object: nil)
|
||
|
|
}
|
||
|
|
|
||
|
|
@objc private func notifyChargeDone(){
|
||
|
|
dismiss()
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - 数据加载
|
||
|
|
private func setupDatas() {
|
||
|
|
loadBalance()
|
||
|
|
loadCoinsTiers()
|
||
|
|
}
|
||
|
|
|
||
|
|
private func loadBalance() {
|
||
|
|
WalletCore.shared.refreshWallet()
|
||
|
|
}
|
||
|
|
|
||
|
|
private func loadCoinsTiers() {
|
||
|
|
CLPurchase.shared.loadCoinTiersProducts { [weak self] response in
|
||
|
|
self?.handleValidProductsInEpalServer(products: response?.productList)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - 处理有效产品
|
||
|
|
private func handleValidProductsInEpalServer(products: [IAPProducts]?) {
|
||
|
|
guard let items = products else {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
datas = items
|
||
|
|
cv.reloadData()
|
||
|
|
|
||
|
|
// 显示加载指示器
|
||
|
|
Hud.showIndicator()
|
||
|
|
IAPCore.shared.requestProducts(items) { [weak self] iapIds in
|
||
|
|
Hud.hideIndicator()
|
||
|
|
var validProducts: [IAPProducts] = []
|
||
|
|
for id in iapIds {
|
||
|
|
for per in items {
|
||
|
|
if id == per.productId {
|
||
|
|
validProducts.append(per)
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
self?.datas = validProducts
|
||
|
|
self?.cv.reloadData()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - 充值按钮点击
|
||
|
|
@objc private func rechargeButtonAction() {
|
||
|
|
guard let selectedProduct = selectProduct else {
|
||
|
|
Hud.toast(str: "Please select a recharge option")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
// 开始购买流程
|
||
|
|
viewModel.purchase(product: selectedProduct) { [weak self] success, tradeNo in
|
||
|
|
if success, let tradeId = tradeNo {
|
||
|
|
selectedProduct.tradeId = tradeId
|
||
|
|
IAPCore.shared.addPayProductId(productId: selectedProduct.productId ?? "", tradeId: tradeId)
|
||
|
|
} else {
|
||
|
|
Hud.toast(str: "Purchase failed, please try again")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
extension CoinsRechargeSheet: UICollectionViewDelegate, UICollectionViewDataSource {
|
||
|
|
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||
|
|
return datas.count
|
||
|
|
}
|
||
|
|
|
||
|
|
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||
|
|
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CoinsRechargeGridCell", for: indexPath) as! CoinsRechargeGridCell
|
||
|
|
let data = datas[indexPath.item]
|
||
|
|
cell.block.backgroundColor = .c.csnn
|
||
|
|
cell.config(data)
|
||
|
|
cell.setupSelected(selected: indexPath.item == selectIndex)
|
||
|
|
return cell
|
||
|
|
}
|
||
|
|
|
||
|
|
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||
|
|
selectIndex = indexPath.item
|
||
|
|
collectionView.reloadData()
|
||
|
|
}
|
||
|
|
}
|