diff --git a/XSeri.xcodeproj/project.pbxproj b/XSeri.xcodeproj/project.pbxproj index b925139..e9932e2 100644 --- a/XSeri.xcodeproj/project.pbxproj +++ b/XSeri.xcodeproj/project.pbxproj @@ -186,6 +186,8 @@ F3F389372F6E35EA001B0E15 /* XSCoinsPackConfirmView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389362F6E35EA001B0E15 /* XSCoinsPackConfirmView.swift */; }; F3F389392F6E4268001B0E15 /* XSCoinsPackConfirmItem1View.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389382F6E4268001B0E15 /* XSCoinsPackConfirmItem1View.swift */; }; F3F3893B2F6E47E6001B0E15 /* XSCoinsPackConfirmItem2View.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F3893A2F6E47E6001B0E15 /* XSCoinsPackConfirmItem2View.swift */; }; + F3F3893D2F6E6FA4001B0E15 /* XSCoinsPackClaimListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F3893C2F6E6FA4001B0E15 /* XSCoinsPackClaimListView.swift */; }; + F3F3893F2F6E7141001B0E15 /* XSCoinsPackClaimListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F3893E2F6E7141001B0E15 /* XSCoinsPackClaimListCell.swift */; }; F3F683ED2F56C380008AF250 /* XSHomeHistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F683EC2F56C380008AF250 /* XSHomeHistoryView.swift */; }; /* End PBXBuildFile section */ @@ -373,6 +375,8 @@ F3F389362F6E35EA001B0E15 /* XSCoinsPackConfirmView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSCoinsPackConfirmView.swift; sourceTree = ""; }; F3F389382F6E4268001B0E15 /* XSCoinsPackConfirmItem1View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSCoinsPackConfirmItem1View.swift; sourceTree = ""; }; F3F3893A2F6E47E6001B0E15 /* XSCoinsPackConfirmItem2View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSCoinsPackConfirmItem2View.swift; sourceTree = ""; }; + F3F3893C2F6E6FA4001B0E15 /* XSCoinsPackClaimListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSCoinsPackClaimListView.swift; sourceTree = ""; }; + F3F3893E2F6E7141001B0E15 /* XSCoinsPackClaimListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSCoinsPackClaimListCell.swift; sourceTree = ""; }; F3F683EC2F56C380008AF250 /* XSHomeHistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSHomeHistoryView.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -924,6 +928,8 @@ F3F389362F6E35EA001B0E15 /* XSCoinsPackConfirmView.swift */, F3F389382F6E4268001B0E15 /* XSCoinsPackConfirmItem1View.swift */, F3F3893A2F6E47E6001B0E15 /* XSCoinsPackConfirmItem2View.swift */, + F3F3893C2F6E6FA4001B0E15 /* XSCoinsPackClaimListView.swift */, + F3F3893E2F6E7141001B0E15 /* XSCoinsPackClaimListCell.swift */, ); path = View; sourceTree = ""; @@ -1136,6 +1142,7 @@ F3F388D52F67DDBC001B0E15 /* XSMineWalletView.swift in Sources */, F3F389272F6A9597001B0E15 /* XSBaseAlert.swift in Sources */, F3F388D92F67F9E1001B0E15 /* XSMineVipView.swift in Sources */, + F3F3893D2F6E6FA4001B0E15 /* XSCoinsPackClaimListView.swift in Sources */, F347D2D42F04BF3D00786648 /* XSHomeNewTitleView.swift in Sources */, F304E6122F67D74A00E9B0A6 /* XSVideoUnlockResult.swift in Sources */, F3F388FE2F6931D1001B0E15 /* XSConsumptionRecordsViewController.swift in Sources */, @@ -1200,6 +1207,7 @@ F3F388E12F680090001B0E15 /* XSScrollView.swift in Sources */, F35547E92F3DDBDD006F28CD /* XSWebView.swift in Sources */, F3F389042F6939C7001B0E15 /* XSOrderRecordsViewController.swift in Sources */, + F3F3893F2F6E7141001B0E15 /* XSCoinsPackClaimListCell.swift in Sources */, F347D31A2F0A545000786648 /* XSDiscoverViewController.swift in Sources */, F355483D2F52BC81006F28CD /* XSSettingAPI.swift in Sources */, F3B312A42F30AC9B0093B180 /* XSSearchData.swift in Sources */, diff --git a/XSeri/Base/Networking/API/XSStoreAPI.swift b/XSeri/Base/Networking/API/XSStoreAPI.swift index 701ae8a..1a465e2 100644 --- a/XSeri/Base/Networking/API/XSStoreAPI.swift +++ b/XSeri/Base/Networking/API/XSStoreAPI.swift @@ -152,4 +152,24 @@ struct XSStoreAPI { let response: XSNetwork.Response = await XSNetwork.request(parameters: param) return response.data } + + static func requestReceiveCoinsPackCoins(id: String?) async -> Bool { + + + var param = XSNetwork.Parameters(path: "/receiveDayCoin") + param.method = .post + param.isLoding = true + param.isToast = true + if let id = id { + param.parameters = [ + "id" : id + ] + } + let response: XSNetwork.Response = await XSNetwork.request(parameters: param) + if response.isSuccess { + return true + } else { + return false + } + } } diff --git a/XSeri/Class/Mine/View/XSMineHeaderView.swift b/XSeri/Class/Mine/View/XSMineHeaderView.swift index b676988..8eab9f1 100644 --- a/XSeri/Class/Mine/View/XSMineHeaderView.swift +++ b/XSeri/Class/Mine/View/XSMineHeaderView.swift @@ -75,7 +75,9 @@ class XSMineHeaderView: UITableViewHeaderFooterView { stackView.addArrangedSubview(walletView) stackView.addArrangedSubview(coinsPackView) - stackView.addArrangedSubview(vipView) + if userInfo?.is_vip == true { + stackView.addArrangedSubview(vipView) + } if !historyDataArr.isEmpty { stackView.addArrangedSubview(historyView) diff --git a/XSeri/Class/Store/Controller/XSCoinsPackViewController.swift b/XSeri/Class/Store/Controller/XSCoinsPackViewController.swift index 308fe9a..3299f86 100644 --- a/XSeri/Class/Store/Controller/XSCoinsPackViewController.swift +++ b/XSeri/Class/Store/Controller/XSCoinsPackViewController.swift @@ -14,7 +14,7 @@ class XSCoinsPackViewController: XSViewController { private var model: XSCoinsPackModel? { didSet { self.headerView.model = model -// claimView.dataArr = model?.receive_list ?? [] + claimView.dataArr = model?.receive_list ?? [] } } @@ -57,6 +57,9 @@ class XSCoinsPackViewController: XSViewController { private lazy var headerView: XSCoinsPackHeaderView = { let view = XSCoinsPackHeaderView() + view.clickClaimButton = { [weak self] in + self?.requestReceiveCoins(nil) + } return view }() @@ -75,6 +78,15 @@ class XSCoinsPackViewController: XSViewController { return view }() + private lazy var claimView: XSCoinsPackClaimListView = { + let view = XSCoinsPackClaimListView() + view.clickClaimButton = { [weak self] id in + guard let self = self else { return } + self.requestReceiveCoins(id) + } + return view + }() + private lazy var tipView: UIView = { let view = UIView() view.addSubview(tipTitleLabel) @@ -149,11 +161,11 @@ class XSCoinsPackViewController: XSViewController { private func reloadData() { stackView.xs_removeAllArrangedSubview() -// if self.claimView.dataArr.count > 0 { -// stackView.addArrangedSubview(self.claimView) -// } else if self.buyView.dataArr.count > 0 { + if self.claimView.dataArr.count > 0 { + stackView.addArrangedSubview(self.claimView) + } else if self.buyView.dataArr.count > 0 { stackView.addArrangedSubview(self.buyView) -// } + } stackView.addArrangedSubview(tipView) } @@ -231,15 +243,16 @@ extension XSCoinsPackViewController { } } -// private func requestReceiveCoins(_ id: String?) { -// FAStoreAPI.requestReceiveCoinsPackCoins(id: id) { [weak self] finish in -// guard let self = self else { return } -// self.requestCoinsPackData() -// if finish { + private func requestReceiveCoins(_ id: String?) { + + Task { + let result = await XSStoreAPI.requestReceiveCoinsPackCoins(id: id) + self.requestCoinsPackData() +// if result { // self.showVideoRecommand() // } -// } -// } + } + } } diff --git a/XSeri/Class/Store/Controller/XSStoreViewController.swift b/XSeri/Class/Store/Controller/XSStoreViewController.swift index dfe8631..833a491 100644 --- a/XSeri/Class/Store/Controller/XSStoreViewController.swift +++ b/XSeri/Class/Store/Controller/XSStoreViewController.swift @@ -6,6 +6,7 @@ // import UIKit +import SnapKit class XSStoreViewController: XSViewController { @@ -41,16 +42,59 @@ class XSStoreViewController: XSViewController { return view }() + private lazy var tipView: UIView = { + let view = UIView() + view.addSubview(tipTitleLabel) + view.addSubview(tipTextLabel) + + tipTitleLabel.snp.makeConstraints { make in + make.top.equalToSuperview() + make.left.equalToSuperview().offset(16) + } + + tipTextLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(16) + make.right.lessThanOrEqualToSuperview().offset(-16) + make.top.equalTo(tipTitleLabel.snp.bottom).offset(4) + make.bottom.equalToSuperview() + } + return view + }() + + private lazy var tipTitleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .bold) + label.textColor = .white.withAlphaComponent(0.5) + label.text = "Tips".localized + return label + }() + + private lazy var tipTextLabel: UILabel = { + let att = NSMutableAttributedString(string: "store_tips".localized) + att.yy_lineSpacing = 3 + + let label = UILabel() + label.font = .font(ofSize: 12, weight: .regular) + label.textColor = .white.withAlphaComponent(0.5) + label.attributedText = att + label.numberOfLines = 0 + return label + }() + override func viewDidLoad() { super.viewDidLoad() self.title = "Store".localized - + let barButtonItem = UIBarButtonItem(title: "Restore".localized, style: .plain, target: self, action: #selector(handleRestore)) + barButtonItem.tintColor = .white + self.navigationItem.rightBarButtonItem = barButtonItem xs_setupUI() reloadData() requestPayData() + + restore(isLoding: false) } @@ -76,7 +120,7 @@ class XSStoreViewController: XSViewController { self.addVipView() self.addCoinsView() } -// self.stackView.addArrangedSubview(tipView) + self.stackView.addArrangedSubview(tipView) } private func addCoinsView() { @@ -103,6 +147,12 @@ class XSStoreViewController: XSViewController { private func buyFinish() { self.requestPayData() } + + @objc private func handleRestore() { + restore(isLoding: true) + } + + } @@ -142,4 +192,15 @@ extension XSStoreViewController { } } + + private func restore(isLoding: Bool) { + XSIapManager.manager.restore(isLoding: isLoding) { [weak self] isFinish, buyType in + if isFinish { + Task { + await XSLoginManager.manager.updateUserInfo() + } + self?.buyFinish() + } + } + } } diff --git a/XSeri/Class/Store/View/XSCoinsPackBuyView.swift b/XSeri/Class/Store/View/XSCoinsPackBuyView.swift index 07651df..c56081f 100644 --- a/XSeri/Class/Store/View/XSCoinsPackBuyView.swift +++ b/XSeri/Class/Store/View/XSCoinsPackBuyView.swift @@ -22,7 +22,7 @@ class XSCoinsPackBuyView: UIView { let label = UILabel() label.font = .font(ofSize: 14, weight: .bold).withBoldItalic() label.textColor = .FFDAA_4 - label.text = "Rewards Overview".localized + label.text = "Weekly Refill".localized return label }() diff --git a/XSeri/Class/Store/View/XSCoinsPackClaimListCell.swift b/XSeri/Class/Store/View/XSCoinsPackClaimListCell.swift new file mode 100644 index 0000000..a3ed846 --- /dev/null +++ b/XSeri/Class/Store/View/XSCoinsPackClaimListCell.swift @@ -0,0 +1,246 @@ +// +// XSCoinsPackClaimListCell.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/21. +// + +import UIKit +import SnapKit + +class XSCoinsPackClaimListCell: UICollectionViewCell { + + var clickClaimButton: ((_ id: String?) -> Void)? + + var model: XSCoinsPackReceiveModel? { + didSet { + coinsView1.coins = model?.week_max_total + coinsView2.coins = model?.week_remaining_total + + claimButton.isEnabled = (model?.receive_coins ?? 0) > 0 + claimButton.setNeedsUpdateConfiguration() + + let titleAtt = NSMutableAttributedString(string: "\(model?.title ?? "")") + titleAtt.yy_color = .black + titleAtt.yy_font = .font(ofSize: 14, weight: .bold) + + let day = "Day" + let dayAtt = NSMutableAttributedString(string: " (\(day) \(model?.day_text ?? ""))") + dayAtt.yy_color = ._946_A_37 + dayAtt.yy_font = .font(ofSize: 14, weight: .regular) + titleAtt.append(dayAtt) + + titleLabel.attributedText = titleAtt + + } + } + + private lazy var bgView: UIImageView = { + let view = UIImageView(image: UIImage(named: "coins_pack_bg_image_02")) + view.layer.cornerRadius = 12 + view.layer.masksToBounds = true + return view + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.numberOfLines = 0 + return label + }() + + private lazy var lineView1 = UIImageView(image: UIImage(named: "line_image_03")) + private lazy var lineView2 = UIImageView(image: UIImage(named: "line_image_04")) + + private lazy var coinsView1: CoinsView = { + let view = CoinsView() + view.title = "Total Reward".localized + return view + }() + + private lazy var coinsView2: CoinsView = { + let view = CoinsView() + view.title = "Remaining".localized + return view + }() + + private lazy var claimButton: UIButton = { + var config = UIButton.Configuration.plain() + config.titleAlignment = .center + config.background.backgroundColor = .white + config.background.cornerRadius = 24 + + let button = UIButton(configuration: config, primaryAction: UIAction(handler: { [weak self] _ in + guard let self = self else { return } + self.clickClaimButton?(self.model?.id) + })) + button.isEnabled = false + + button.configurationUpdateHandler = { [weak self] button in + guard let self = self else { return } + var config = button.configuration + + if button.isEnabled { + config?.background.backgroundColor = .white + + let coinImage = UIImage(named: "coins_icon_05")! + let coinText = NSTextAttachment(image: coinImage) + coinText.bounds = .init(x: 0, y: -2.5, width: coinImage.size.width, height: coinImage.size.height) + let coinAtt = AttributedString(NSAttributedString(attachment: coinText)) + let countAtt = AttributedString(" \(self.model?.receive_coins ?? 0)", attributes: AttributeContainer([ + .font : UIFont.font(ofSize: 12, weight: .bold), + .foregroundColor : UIColor._946_A_37 + ])) + + config?.attributedTitle = AttributedString("Claim".localized, attributes: AttributeContainer([ + .font : UIFont.font(ofSize: 14, weight: .bold), + .foregroundColor : UIColor.black + ])) + + config?.attributedSubtitle = coinAtt + countAtt + + } else { + config?.background.backgroundColor = ._616161 + + + config?.attributedTitle = AttributedString("Claimed".localized, attributes: AttributeContainer([ + .font : UIFont.font(ofSize: 14, weight: .bold), + .foregroundColor : UIColor.white.withAlphaComponent(0.5) + ])) + + config?.attributedSubtitle = nil + } + + button.configuration = config + + } + return button + }() + + override init(frame: CGRect) { + super.init(frame: frame) + xs_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + + +extension XSCoinsPackClaimListCell { + + private func xs_setupUI() { + contentView.addSubview(bgView) + bgView.addSubview(titleLabel) + bgView.addSubview(lineView1) + bgView.addSubview(lineView2) + bgView.addSubview(coinsView1) + bgView.addSubview(coinsView2) + bgView.addSubview(claimButton) + + bgView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(12) + make.centerY.equalTo(self.bgView.snp.top).offset(25) + make.right.lessThanOrEqualToSuperview().offset(-12) + } + + lineView1.snp.makeConstraints { make in + make.left.equalToSuperview().offset(12) + make.centerX.equalToSuperview() + make.top.equalToSuperview().offset(48) + } + + coinsView1.snp.makeConstraints { make in + make.left.equalToSuperview().offset(12) + make.bottom.equalToSuperview().offset(-17) + } + + lineView2.snp.makeConstraints { make in + make.centerY.equalTo(coinsView1) + make.left.equalTo(coinsView1.snp.right).offset(12) + } + + coinsView2.snp.makeConstraints { make in + make.centerY.equalTo(coinsView1) + make.left.equalTo(lineView2.snp.right).offset(12) + } + + claimButton.snp.makeConstraints { make in + make.right.equalToSuperview().offset(-12) + make.centerY.equalTo(coinsView1) + make.height.equalTo(48) + make.width.equalTo(120) + } + } + +} + +extension XSCoinsPackClaimListCell { + + class CoinsView: UIView { + + var title: String? { + didSet { + titleLabel.text = title + } + } + + var coins: Int? { + didSet { + coinsLabel.text = "\(coins ?? 0)" + } + } + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .bold) + label.textColor = .black + return label + }() + + private lazy var iconImageView = UIImageView(image: UIImage(named: "coins_icon_05")) + + private lazy var coinsLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 14, weight: .bold) + label.textColor = ._946_A_37 + label.text = "0" + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + addSubview(titleLabel) + addSubview(iconImageView) + addSubview(coinsLabel) + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview() + make.top.equalToSuperview() + make.right.lessThanOrEqualToSuperview() + } + + iconImageView.snp.makeConstraints { make in + make.left.equalToSuperview() + make.bottom.equalToSuperview() + make.top.equalTo(titleLabel.snp.bottom).offset(8) + } + + coinsLabel.snp.makeConstraints { make in + make.centerY.equalTo(iconImageView) + make.left.equalTo(iconImageView.snp.right).offset(4) + make.right.lessThanOrEqualToSuperview() + } + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + } + +} diff --git a/XSeri/Class/Store/View/XSCoinsPackClaimListView.swift b/XSeri/Class/Store/View/XSCoinsPackClaimListView.swift new file mode 100644 index 0000000..89d9eca --- /dev/null +++ b/XSeri/Class/Store/View/XSCoinsPackClaimListView.swift @@ -0,0 +1,116 @@ +// +// XSCoinsPackClaimListView.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/21. +// + +import UIKit +import SnapKit + +class XSCoinsPackClaimListView: UIView { + + var clickClaimButton: ((_ id: String?) -> Void)? + + var dataArr: [XSCoinsPackReceiveModel] = [] { + didSet { + collectionView.reloadData() + } + } + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 14, weight: .bold).withBoldItalic() + label.textColor = .FFDAA_4 + label.text = "Active Refills".localized + return label + }() + + private lazy var titleLineView: UIView = { + let view = UIImageView(image: UIImage(named: "coins_pack_line_image")) + return view + }() + + private lazy var collectionViewLayout: UICollectionViewFlowLayout = { + let layout = UICollectionViewFlowLayout() + layout.itemSize = .init(width: XSScreen.width - 32, height: 122) + layout.minimumLineSpacing = 12 + return layout + }() + + private lazy var collectionView: XSCollectionView = { + let collectionView = XSCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) + collectionView.delegate = self + collectionView.dataSource = self + collectionView.addObserver(self, forKeyPath: "contentSize", context: nil) + collectionView.register(XSCoinsPackClaimListCell.self, forCellWithReuseIdentifier: "cell") + return collectionView + }() + + deinit { + collectionView.removeObserver(self, forKeyPath: "contentSize") + } + + override init(frame: CGRect) { + super.init(frame: frame) + xs_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + if keyPath == "contentSize" { + let height = self.collectionView.contentSize.height + 1 + collectionView.snp.updateConstraints { make in + make.height.equalTo(height) + } + } + } +} + +extension XSCoinsPackClaimListView { + + private func xs_setupUI() { + addSubview(titleLabel) + addSubview(titleLineView) + addSubview(collectionView) + + titleLabel.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalToSuperview() + } + + titleLineView.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(28) + make.top.equalTo(titleLabel.snp.bottom).offset(6) + } + + collectionView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalTo(titleLabel.snp.bottom).offset(16) + make.height.equalTo(1) + make.bottom.equalToSuperview() + } + } + +} + +//MARK: UICollectionViewDelegate UICollectionViewDataSource +extension XSCoinsPackClaimListView: UICollectionViewDelegate, UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! XSCoinsPackClaimListCell + cell.model = self.dataArr[indexPath.row] + cell.clickClaimButton = { [weak self] id in + self?.clickClaimButton?(id) + } + return cell + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return dataArr.count + } + +} diff --git a/XSeri/Class/Store/View/XSCoinsPackConfirmView.swift b/XSeri/Class/Store/View/XSCoinsPackConfirmView.swift index 5b8fb2a..6575160 100644 --- a/XSeri/Class/Store/View/XSCoinsPackConfirmView.swift +++ b/XSeri/Class/Store/View/XSCoinsPackConfirmView.swift @@ -9,29 +9,71 @@ import UIKit import SnapKit import HWPanModal +private final class XSCoinsPackConfirmButtonBackgroundView: XSView { + + private let borderWidth: CGFloat = 1 + + private lazy var fillView: XSView = { + let view = XSView() + view.xs_colors = [UIColor.F_5_BD_7_E.cgColor, UIColor.FFEABC.cgColor, UIColor.FFCF_99.cgColor] + view.xs_startPoint = .init(x: 0, y: 0.5) + view.xs_endPoint = .init(x: 1, y: 0.5) + return view + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + xs_colors = [ + UIColor.E_43_B_7_F.withAlphaComponent(0.3).cgColor, + UIColor.B_4054_B.withAlphaComponent(0.3).cgColor + ] + xs_startPoint = .init(x: 0, y: 0.5) + xs_endPoint = .init(x: 1, y: 0.5) + + addSubview(fillView) + fillView.snp.makeConstraints { make in + make.edges.equalToSuperview().inset(borderWidth) + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func layoutSubviews() { + super.layoutSubviews() + + layer.cornerRadius = bounds.height / 2 + layer.masksToBounds = true + fillView.layer.cornerRadius = max(0, (bounds.height - borderWidth * 2) / 2) + fillView.layer.masksToBounds = true + } +} + class XSCoinsPackConfirmView: XSPanModalContentView { - + var buyFinishHandle: (() -> Void)? - + var shortPlayId: String? var videoId: String? - + var item: XSPayItem? { didSet { item2View.list = item?.ext_info?.sub_coins_txt_list - + var price = item?.price ?? "" if let discount_price = item?.discount_price { price = discount_price } - + priceLabel.text = "\(item?.currency ?? "")\(price)/\(item?.getTimeString() ?? "")" - + } } - + private let bgBorderImage = UIImage(named: "gradient_color_image_03")?.xs_flippedVertically() - + private lazy var bgView: UIImageView = { let view = UIImageView(image: UIImage(named: "coins_pack_bg_03")) view.isUserInteractionEnabled = true @@ -42,7 +84,7 @@ class XSCoinsPackConfirmView: XSPanModalContentView { view.addObserver(self, forKeyPath: "bounds", context: nil) return view }() - + private lazy var titleLabel: UILabel = { let label = UILabel() label.font = .font(ofSize: 24, weight: .bold) @@ -50,29 +92,29 @@ class XSCoinsPackConfirmView: XSPanModalContentView { label.text = "What You Get".localized return label }() - + private lazy var closeButton: UIButton = { let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in guard let self = self else { return } self.dismiss(animated: true) { - + } })) button.setImage(UIImage(named: "close_icon_03"), for: .normal) return button }() - + private lazy var scrollView: XSScrollView = { let scrollView = XSScrollView() scrollView.addObserver(self, forKeyPath: "contentSize", context: nil) return scrollView }() - + private lazy var contentTitleView: UIView = { let view = UIImageView(image: UIImage(named: "coins_pack_confirm_title_bg")) return view }() - + private lazy var contentTitleLabel: UILabel = { let label = UILabel() label.font = .font(ofSize: 14, weight: .bold).withBoldItalic() @@ -80,7 +122,7 @@ class XSCoinsPackConfirmView: XSPanModalContentView { label.text = "Bonus You Get".localized return label }() - + private lazy var contentView: UIView = { let view = UIView() view.layer.cornerRadius = 18 @@ -88,32 +130,26 @@ class XSCoinsPackConfirmView: XSPanModalContentView { view.backgroundColor = .F_08_FFF.withAlphaComponent(0.1) return view }() - + private lazy var item1View: XSCoinsPackConfirmItem1View = { let view = XSCoinsPackConfirmItem1View() return view }() - + private lazy var item2View: XSCoinsPackConfirmItem2View = { let view = XSCoinsPackConfirmItem2View() return view }() - + private lazy var priceLabel: UILabel = { let label = UILabel() label.font = .font(ofSize: 18, weight: .bold) label.textColor = .FFE_17_D return label }() - - private lazy var continueBgView: UIView = { - let view = XSView() - view.xs_colors = [UIColor.F_5_BD_7_E.cgColor, UIColor.FFEABC.cgColor, UIColor.FFCF_99.cgColor] - view.xs_startPoint = .init(x: 0, y: 0.5) - view.xs_endPoint = .init(x: 1, y: 0.5) - return view - }() - + + private lazy var continueBgView = XSCoinsPackConfirmButtonBackgroundView() + private lazy var continueButton: UIButton = { var configuration = UIButton.Configuration.plain() configuration.background.customView = continueBgView @@ -124,49 +160,57 @@ class XSCoinsPackConfirmView: XSPanModalContentView { ])) let button = UIButton(configuration: configuration, primaryAction: UIAction(handler: { [weak self] _ in guard let self = self else { return } + guard let item = self.item else { return } + + XSIapManager.manager.start(model: item, shortPlayId: self.shortPlayId, videoId: videoId) { [weak self] finish in + guard let self = self else { return } + guard finish else { return } + self.buyFinishHandle?() + self.dismiss(animated: true) { } + } })) return button }() - + deinit { self.bgView.removeObserver(self, forKeyPath: "bounds") self.scrollView.removeObserver(self, forKeyPath: "contentSize") } - + override init(frame: CGRect) { super.init(frame: frame) // self.contentHeight = 300 - + xs_setupUI() } - + @MainActor required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "bounds" { guard let borderImage = bgBorderImage?.xs_resized(to: bgView.bounds.size) else { return } bgView.layer.borderColor = UIColor(patternImage: borderImage).cgColor - + } else if keyPath == "contentSize" { self.setNeedsLayoutUpdate() - + let height = self.scrollView.contentSize.height scrollView.snp.updateConstraints { make in make.height.equalTo(height + 1) } } } - + override func allowsTapBackgroundToDismiss() -> Bool { return false } - + override func cornerRadius() -> CGFloat { return 0 } - + override func longFormHeight() -> PanModalHeight { let height = 78 + scrollView.contentSize.height + 92 + 10 return PanModalHeightMake(.content, height + XSScreen.safeBottom) @@ -174,7 +218,7 @@ class XSCoinsPackConfirmView: XSPanModalContentView { } extension XSCoinsPackConfirmView { - + private func xs_setupUI() { addSubview(bgView) addSubview(titleLabel) @@ -187,61 +231,61 @@ extension XSCoinsPackConfirmView { contentView.addSubview(item2View) addSubview(priceLabel) addSubview(continueButton) - + bgView.snp.makeConstraints { make in make.edges.equalToSuperview() } - + titleLabel.snp.makeConstraints { make in make.centerX.equalToSuperview() make.top.equalToSuperview().offset(30) } - + closeButton.snp.makeConstraints { make in make.right.equalToSuperview().offset(-16) make.top.equalToSuperview().offset(24) } - + scrollView.snp.makeConstraints { make in make.left.right.equalToSuperview() make.top.equalToSuperview().offset(78) make.height.equalTo(1) } - + contentView.snp.makeConstraints { make in make.left.equalToSuperview().offset(16) make.centerX.equalToSuperview() make.top.equalToSuperview().offset(7) make.bottom.equalToSuperview() } - + contentTitleView.snp.makeConstraints { make in make.centerX.equalToSuperview() make.top.equalToSuperview() } - + contentTitleLabel.snp.makeConstraints { make in make.centerX.equalToSuperview() make.left.equalToSuperview().offset(28) make.top.equalToSuperview().offset(9) } - + item1View.snp.makeConstraints { make in make.left.right.equalToSuperview() make.top.equalToSuperview().offset(46) } - + item2View.snp.makeConstraints { make in make.left.right.equalToSuperview() make.top.equalTo(item1View.snp.bottom).offset(24) make.bottom.equalToSuperview().offset(-40) } - + priceLabel.snp.makeConstraints { make in make.centerX.equalToSuperview() make.centerY.equalTo(scrollView.snp.bottom).offset(22) } - + continueButton.snp.makeConstraints { make in make.left.right.equalToSuperview().inset(16) make.bottom.equalToSuperview().offset(-(10 + XSScreen.safeBottom)) diff --git a/XSeri/Class/Store/View/XSCoinsPackHeaderView.swift b/XSeri/Class/Store/View/XSCoinsPackHeaderView.swift index b7d1287..c26f54a 100644 --- a/XSeri/Class/Store/View/XSCoinsPackHeaderView.swift +++ b/XSeri/Class/Store/View/XSCoinsPackHeaderView.swift @@ -10,12 +10,13 @@ import SnapKit class XSCoinsPackHeaderView: UIView { + var clickClaimButton: (() -> Void)? + var model: XSCoinsPackModel? { didSet { coinsView1.coins = model?.week_max_total coinsView2.coins = model?.week_total - let activeRefillsStr = NSMutableAttributedString(string: "Active Refills:".localized + " ") activeRefillsStr.yy_font = .font(ofSize: 12, weight: .regular) activeRefillsStr.yy_color = .white.withAlphaComponent(0.5) @@ -79,8 +80,7 @@ class XSCoinsPackHeaderView: UIView { config.background.cornerRadius = 24 let button = UIButton(configuration: config, primaryAction: UIAction(handler: { [weak self] _ in -// self?.clickClaimButton?() - self?.claimButton.isEnabled = false + self?.clickClaimButton?() })) button.isEnabled = false button.configurationUpdateHandler = { [weak self] button in @@ -98,11 +98,7 @@ class XSCoinsPackHeaderView: UIView { .foregroundColor : UIColor._763200 ])) -// let countAtt = AttributedString(" \(self.model?.receive_coins ?? 0)".localized, attributes: AttributeContainer([ -// .font : UIFont.font(ofSize: 14, weight: .bold), -// .foregroundColor : UIColor.FFFFFF -// ])) - let countAtt = AttributedString(" 10".localized, attributes: AttributeContainer([ + let countAtt = AttributedString(" \(self.model?.receive_coins ?? 0)", attributes: AttributeContainer([ .font : UIFont.font(ofSize: 14, weight: .bold), .foregroundColor : UIColor.FF_7700 ])) diff --git a/XSeri/Class/Store/View/XSStoreCoinsView.swift b/XSeri/Class/Store/View/XSStoreCoinsView.swift index 2d26474..00e7a57 100644 --- a/XSeri/Class/Store/View/XSStoreCoinsView.swift +++ b/XSeri/Class/Store/View/XSStoreCoinsView.swift @@ -199,23 +199,25 @@ extension XSStoreCoinsView: UICollectionViewDelegate, UICollectionViewDataSource } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - let model = dataArr[indexPath.section][indexPath.row] + let item = dataArr[indexPath.section][indexPath.row] self.selectedIndexPath = indexPath collectionView.reloadData() - if model.buy_type == .subCoins { -// let view = FACoinPackConfirmView() -// view.shortPlayId = self.shortPlayId -// view.videoId = self.videoId -// view.model = model -// view.buyFinishHandle = { [weak self] in -// guard let self = self else { return } -// FALogin.manager.requestUserInfo(completer: nil) -// self.buyFinishHandle?() -// } -// view.present(in: nil) + if item.buy_type == .subCoins { + let view = XSCoinsPackConfirmView() + view.shortPlayId = self.shortPlayId + view.videoId = self.videoId + view.item = item + view.buyFinishHandle = { [weak self] in + guard let self = self else { return } + Task { + await XSLoginManager.manager.updateUserInfo() + } + self.buyFinishHandle?() + } + view.present(in: nil) } else { - XSIapManager.manager.start(model: model, shortPlayId: self.shortPlayId, videoId: self.videoId) { [weak self] finish in + XSIapManager.manager.start(model: item, shortPlayId: self.shortPlayId, videoId: self.videoId) { [weak self] finish in guard let self = self else { return } if finish { Task { diff --git a/XSeri/Source/Assets.xcassets/Image/icon/coins_pack_bg_image_02.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/coins_pack_bg_image_02.imageset/Contents.json new file mode 100644 index 0000000..209e3e1 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/coins_pack_bg_image_02.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "金币订阅包-bg@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "金币订阅包-bg@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/coins_pack_bg_image_02.imageset/金币订阅包-bg@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/coins_pack_bg_image_02.imageset/金币订阅包-bg@2x.png new file mode 100644 index 0000000..69a273a Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/coins_pack_bg_image_02.imageset/金币订阅包-bg@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/coins_pack_bg_image_02.imageset/金币订阅包-bg@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/coins_pack_bg_image_02.imageset/金币订阅包-bg@3x.png new file mode 100644 index 0000000..e705ad7 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/coins_pack_bg_image_02.imageset/金币订阅包-bg@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/line_image_03.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/line_image_03.imageset/Contents.json new file mode 100644 index 0000000..b77519f --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/line_image_03.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "虚线@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "虚线@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/line_image_03.imageset/虚线@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/line_image_03.imageset/虚线@2x.png new file mode 100644 index 0000000..39e22a6 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/line_image_03.imageset/虚线@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/line_image_03.imageset/虚线@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/line_image_03.imageset/虚线@3x.png new file mode 100644 index 0000000..b091de3 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/line_image_03.imageset/虚线@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/line_image_04.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/line_image_04.imageset/Contents.json new file mode 100644 index 0000000..d6e2944 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/line_image_04.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "line_image_04@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "line_image_04@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/line_image_04.imageset/line_image_04@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/line_image_04.imageset/line_image_04@2x.png new file mode 100644 index 0000000..c454e5e Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/line_image_04.imageset/line_image_04@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/line_image_04.imageset/line_image_04@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/line_image_04.imageset/line_image_04@3x.png new file mode 100644 index 0000000..8d03224 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/line_image_04.imageset/line_image_04@3x.png differ diff --git a/XSeri/Source/en.lproj/Localizable.strings b/XSeri/Source/en.lproj/Localizable.strings index 0290ca1..a68cffa 100644 --- a/XSeri/Source/en.lproj/Localizable.strings +++ b/XSeri/Source/en.lproj/Localizable.strings @@ -80,11 +80,16 @@ "Claim All" = "Claim All"; "Get a Refill to Claim" = "Get a Refill to Claim"; "Active Refills:" = "Active Refills:"; +"Active Refills" = "Active Refills"; "What You Get" = "What You Get"; "Bonus You Get" = "Bonus You Get"; "Weekly Refill Package" = "Weekly Refill Package"; "Daily Bonuses" = "Daily Bonuses"; "Continue" = "Continue"; +"Total Reward" = "Total Reward"; +"Remaining" = "Remaining"; +"Claim" = "Claim"; +"Restore" = "Restore"; "me_daily_1" = "Daily reward ready!"; "me_daily_2" = "Claim your rewards now."; @@ -107,3 +112,6 @@ "pay_error_4" = "Purchase Failed"; "pay_error_5" = "There are no recoverable in-app purchases."; "pay_error_6" = "You have unfinished in-app purchases, please restore them first."; + +"Tips" = "Tips"; +"store_tips" = "1. Coins are virtual items and cannot be refunded. Use it for this product.
2. Both the coins and the reward coins will never expire.
3. Coins will be used first when unlocking episodes. If the amount is insufficient, reward coins will automatically be used.
4. The purchase has not been credited, click torefresh.
5. For other questions, contact us via Portfolio > Feedback.";