From b710eb9ac15129a4ee4eab160130a8f15c582399 Mon Sep 17 00:00:00 2001 From: zeng Date: Tue, 6 May 2025 19:52:12 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=A3=E9=94=81=E8=A7=86=E9=A2=91=E5=BC=80?= =?UTF-8?q?=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Base/Networking/API/SPWalletAPI.swift | 15 ++ .../Class/Mine/View/SPMineMemberNoView.swift | 2 +- .../Class/Mine/View/SPMineMemberYesView.swift | 2 +- .../SPPlayerDetailViewController.swift | 62 ++++++- .../SPPlayerListViewController.swift | 6 +- .../Class/Player/View/SPPlayLockView.swift | 175 ++++++++++++++++++ .../Player/View/SPPlayerControlView.swift | 3 - .../Player/View/SPPlayerDetailCell.swift | 17 ++ .../View/SPPlayerDetailControlView.swift | 36 ++++ .../Class/Player/View/SPPlayerListCell.swift | 2 +- .../Wallet/Model/SPVideoUnlockModel.swift | 26 +++ .../icon/lock_icon_02.imageset/Contents.json | 22 +++ .../lock_icon_02.imageset/Frame 125@2x.png | Bin 0 -> 2615 bytes .../lock_icon_02.imageset/Frame 125@3x.png | Bin 0 -> 3848 bytes .../unlock_icon_01.imageset/Contents.json | 22 +++ .../icon/unlock_icon_01.imageset/Frame@2x.png | Bin 0 -> 400 bytes .../icon/unlock_icon_01.imageset/Frame@3x.png | Bin 0 -> 534 bytes MoviaBox/Source/en.lproj/Localizable.strings | 7 + MoviaBox/Thirdparty/JXButton/JXButton.swift | 4 +- 19 files changed, 388 insertions(+), 13 deletions(-) create mode 100644 MoviaBox/Class/Player/View/SPPlayLockView.swift create mode 100644 MoviaBox/Class/Wallet/Model/SPVideoUnlockModel.swift create mode 100644 MoviaBox/Source/Assets.xcassets/icon/lock_icon_02.imageset/Contents.json create mode 100644 MoviaBox/Source/Assets.xcassets/icon/lock_icon_02.imageset/Frame 125@2x.png create mode 100644 MoviaBox/Source/Assets.xcassets/icon/lock_icon_02.imageset/Frame 125@3x.png create mode 100644 MoviaBox/Source/Assets.xcassets/icon/unlock_icon_01.imageset/Contents.json create mode 100644 MoviaBox/Source/Assets.xcassets/icon/unlock_icon_01.imageset/Frame@2x.png create mode 100644 MoviaBox/Source/Assets.xcassets/icon/unlock_icon_01.imageset/Frame@3x.png diff --git a/MoviaBox/Base/Networking/API/SPWalletAPI.swift b/MoviaBox/Base/Networking/API/SPWalletAPI.swift index 92a49b5..022bbdd 100644 --- a/MoviaBox/Base/Networking/API/SPWalletAPI.swift +++ b/MoviaBox/Base/Networking/API/SPWalletAPI.swift @@ -101,4 +101,19 @@ class SPWalletAPI: NSObject { } } + ///金币解锁视频 + static func requestCoinUnlockVideo(shortPlayId: String, videoId: String, completer: ((_ model: SPVideoUnlockModel?) -> Void)?) { + + var param = SPNetworkParameters(path: "/buy_video") + param.isLoding = true + param.parameters = [ + "short_play_id" : shortPlayId, + "video_id" : videoId, + ] + + SPNetwork.request(parameters: param) { (response: SPNetworkResponse) in + completer?(response.data) + } + } + } diff --git a/MoviaBox/Class/Mine/View/SPMineMemberNoView.swift b/MoviaBox/Class/Mine/View/SPMineMemberNoView.swift index f8370a4..5df4efc 100644 --- a/MoviaBox/Class/Mine/View/SPMineMemberNoView.swift +++ b/MoviaBox/Class/Mine/View/SPMineMemberNoView.swift @@ -27,7 +27,7 @@ class SPMineMemberNoView: UIView { private lazy var activateButton: UIButton = { let button = JXButton(type: .custom) - button.leftAnyRightmargin = 13 + button.leftAndRightMargin = 13 button.setTitle("Activate".localized, for: .normal) button.setTitleColor(.colorFFD791(), for: .normal) button.jx_font = .fontMedium(ofSize: 14) diff --git a/MoviaBox/Class/Mine/View/SPMineMemberYesView.swift b/MoviaBox/Class/Mine/View/SPMineMemberYesView.swift index b94413d..320ed69 100644 --- a/MoviaBox/Class/Mine/View/SPMineMemberYesView.swift +++ b/MoviaBox/Class/Mine/View/SPMineMemberYesView.swift @@ -48,7 +48,7 @@ class SPMineMemberYesView: UIView { button.setTitle("Stream Unlimited".localized, for: .normal) button.setTitleColor(.color321704(), for: .normal) button.jx_font = .fontRegular(ofSize: 12) - button.leftAnyRightmargin = 12 + button.leftAndRightMargin = 12 button.colors = [UIColor.colorF2A3A3().cgColor, UIColor.colorFEE095().cgColor] button.locations = [0, 1] button.startPoint = .init(x: 0, y: 0.5) diff --git a/MoviaBox/Class/Player/Controller/SPPlayerDetailViewController.swift b/MoviaBox/Class/Player/Controller/SPPlayerDetailViewController.swift index c834acb..df91136 100644 --- a/MoviaBox/Class/Player/Controller/SPPlayerDetailViewController.swift +++ b/MoviaBox/Class/Player/Controller/SPPlayerDetailViewController.swift @@ -81,8 +81,7 @@ class SPPlayerDetailViewController: SPPlayerListViewController { if videoInfo.is_lock == true { self.pause() - let view = SPPlayBuyView() - view.present(in: nil) + self.onPlayBuy() return } @@ -130,13 +129,12 @@ extension SPPlayerDetailViewController { self.viewModel.handleEpisode = { [weak self] in self?.onEpisode() } + } } extension SPPlayerDetailViewController { - - private func onEpisode() { let view = SPEpisodeView() view.dataArr = detailModel?.episodeList ?? [] @@ -149,6 +147,48 @@ extension SPPlayerDetailViewController { self.episodeView = view } + ///打开支付页面 + private func onPlayBuy() { + let view = SPPlayBuyView() + view.present(in: nil) + } + + ///解锁视频 + private func unlockVideo(indexPath: IndexPath) { + + guard let videoInfo = detailModel?.episodeList?[indexPath.row] else { return } + + guard let shortPlayId = videoInfo.short_play_id, let videoId = videoInfo.short_play_video_id else { return } + + SPWalletAPI.requestCoinUnlockVideo(shortPlayId: shortPlayId, videoId: videoId) { [weak self] model in + guard let self = self else { return } + guard let model = model else { return } + + switch model.status { + case .jump: + SPToast.show(text: "kAlertMessage_01".localized) + + case .noPlay: + SPToast.show(text: "kAlertMessage_02".localized) + + case .notEnough: + self.onPlayBuy() + + case .success: + videoInfo.is_lock = false + self.reloadData { [weak self] in + guard let self = self else { return } + self.play() + } + + default: + break + } + + + } + + } } //MARK: -------------- SPPlayerListViewControllerDataSource -------------- @@ -158,6 +198,20 @@ extension SPPlayerDetailViewController: SPPlayerListViewControllerDataSource, SP cell.shortModel = detailModel?.shortPlayInfo cell.videoInfo = detailModel?.episodeList?[indexPath.row] cell.isLoop = false + + let upRow = indexPath.row - 1 + if upRow >= 0, let videoInfo = detailModel?.episodeList?[upRow], videoInfo.is_lock == true { + cell.hasLockUpEpisode = true + } else { + cell.hasLockUpEpisode = false + } + + cell.clickUnlockButton = { [weak self] (cell) in + guard let self = self else { return } + guard let indexPath = self.collectionView.indexPath(for: cell) else { return } + self.unlockVideo(indexPath: indexPath) + } + } return oldCell } diff --git a/MoviaBox/Class/Player/Controller/SPPlayerListViewController.swift b/MoviaBox/Class/Player/Controller/SPPlayerListViewController.swift index 67f1d2b..c14b555 100644 --- a/MoviaBox/Class/Player/Controller/SPPlayerListViewController.swift +++ b/MoviaBox/Class/Player/Controller/SPPlayerListViewController.swift @@ -186,7 +186,11 @@ class SPPlayerListViewController: SPViewController { } func reloadData(completion: (() -> Void)? = nil) { - CATransaction.setCompletionBlock { + CATransaction.setCompletionBlock { [weak self] in + guard let self = self else { return } + let cell = self.collectionView.cellForItem(at: self.currentIndexPath) as? SPPlayerListCell + self.viewModel.currentPlayer = cell + completion?() } CATransaction.begin() diff --git a/MoviaBox/Class/Player/View/SPPlayLockView.swift b/MoviaBox/Class/Player/View/SPPlayLockView.swift new file mode 100644 index 0000000..3c162f1 --- /dev/null +++ b/MoviaBox/Class/Player/View/SPPlayLockView.swift @@ -0,0 +1,175 @@ +// +// SPPlayLockView.swift +// MoviaBox +// +// Created by 佳尔 on 2025/5/6. +// + +import UIKit + +class SPPlayLockView: UIView { + + ///是否是解锁上一集 + var isUnlockUpEpisode: Bool = false { + didSet { + updateUnlockButton() + } + } + + var videoInfo: SPVideoInfoModel? { + didSet { + coinView.setTitle("\(videoInfo?.coins ?? 0)", for: .normal) + } + } + + ///点击解锁按钮 + var clickUnlockButton: (() -> Void)? + + //MARK: UI属性 + private lazy var containerView: UIView = { + let view = UIView() + return view + }() + + private lazy var lockImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "lock_icon_02")) + return imageView + }() + + private lazy var lockTextLabel: UILabel = { + let label = UILabel() + label.font = .fontRegular(ofSize: 18) + label.textColor = .colorFFFFFF() + label.text = "This episode is locked".localized + return label + }() + + private lazy var unlockButton: UIButton = { + let button = UIButton() + button.backgroundColor = .colorFF3232() + button.layer.cornerRadius = 27 + button.layer.masksToBounds = true + button.addTarget(self, action: #selector(handleUnlockButton), for: .touchUpInside) + return button + }() + + private lazy var unlockStackView: UIStackView = { + let view = UIStackView() + view.isUserInteractionEnabled = false + view.axis = .horizontal + view.spacing = 6 + view.alignment = .center + return view + }() + + private lazy var unlockImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "unlock_icon_01")) + return imageView + }() + + private lazy var unlockTitleLabel: UILabel = { + let label = UILabel() + label.font = .fontRegular(ofSize: 16) + label.textColor = .colorFFFFFF() + return label + }() + + private lazy var coinView: UIButton = { + let view = JXButton(type: .custom) + view.isUserInteractionEnabled = false + view.backgroundColor = .color000000(alpha: 0.2) + view.layer.cornerRadius = 19 + view.layer.masksToBounds = true + view.jx_font = .fontRegular(ofSize: 16) + view.setTitleColor(.colorFFFFFF(), for: .normal) + view.setImage(UIImage(named: "coin_icon_04"), for: .normal) + view.space = 8 + view.leftAndRightMargin = 8 + view.snp.makeConstraints { make in + make.height.equalTo(38) + } + return view + }() + + + override init(frame: CGRect) { + super.init(frame: frame) + backgroundColor = .color000000(alpha: 0.75) + updateUnlockButton() + + _setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + + @objc private func handleUnlockButton() { + if isUnlockUpEpisode { + SPToast.show(text: "kAlertMessage_01".localized) + return + } + + self.clickUnlockButton?() + + } + + private func updateUnlockButton() { + unlockStackView.removeAllArrangedSubview() + + unlockStackView.addArrangedSubview(unlockImageView) + unlockStackView.addArrangedSubview(unlockTitleLabel) + + if isUnlockUpEpisode { + unlockTitleLabel.text = "Unlock the previous episode".localized + + } else { + unlockTitleLabel.text = "Unlock now for".localized + + unlockStackView.addArrangedSubview(coinView) + } + } + +} + +extension SPPlayLockView { + + private func _setupUI() { + addSubview(containerView) + containerView.addSubview(lockImageView) + containerView.addSubview(lockTextLabel) + containerView.addSubview(unlockButton) + unlockButton.addSubview(unlockStackView) + + containerView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.right.equalToSuperview() + } + + lockImageView.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalToSuperview() + } + + lockTextLabel.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalTo(lockImageView.snp.bottom).offset(13) + } + + unlockButton.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.left.equalToSuperview().offset(22) + make.bottom.equalToSuperview() + make.top.equalTo(lockTextLabel.snp.bottom).offset(35) + make.height.equalTo(54) + } + + unlockStackView.snp.makeConstraints { make in + make.top.bottom.equalToSuperview() + make.centerX.equalToSuperview() + } + + } + +} diff --git a/MoviaBox/Class/Player/View/SPPlayerControlView.swift b/MoviaBox/Class/Player/View/SPPlayerControlView.swift index b7cadc3..8a5fb5e 100644 --- a/MoviaBox/Class/Player/View/SPPlayerControlView.swift +++ b/MoviaBox/Class/Player/View/SPPlayerControlView.swift @@ -107,9 +107,6 @@ class SPPlayerControlView: UIView { return button }() - ///锁定视图 -// private lazy var lock - deinit { viewModel?.removeObserver(self, forKeyPath: "isPlaying") NotificationCenter.default.removeObserver(self) diff --git a/MoviaBox/Class/Player/View/SPPlayerDetailCell.swift b/MoviaBox/Class/Player/View/SPPlayerDetailCell.swift index ee16d19..5cbe70e 100644 --- a/MoviaBox/Class/Player/View/SPPlayerDetailCell.swift +++ b/MoviaBox/Class/Player/View/SPPlayerDetailCell.swift @@ -13,9 +13,26 @@ class SPPlayerDetailCell: SPPlayerListCell { return SPPlayerDetailControlView.self } + ///点击解锁按钮 + var clickUnlockButton: ((_ cell: SPPlayerDetailCell) -> Void)? + + ///上一集是否加锁 + var hasLockUpEpisode = false { + didSet { + guard let controlView = self.controlView as? SPPlayerDetailControlView else { return } + controlView.hasLockUpEpisode = hasLockUpEpisode + } + } override init(frame: CGRect) { super.init(frame: frame) + if let controlView = self.controlView as? SPPlayerDetailControlView { + controlView.clickUnlockButton = { [weak self] in + guard let self = self else { return } + self.clickUnlockButton?(self) + } + + } } @MainActor required init?(coder: NSCoder) { diff --git a/MoviaBox/Class/Player/View/SPPlayerDetailControlView.swift b/MoviaBox/Class/Player/View/SPPlayerDetailControlView.swift index 55198fe..9687081 100644 --- a/MoviaBox/Class/Player/View/SPPlayerDetailControlView.swift +++ b/MoviaBox/Class/Player/View/SPPlayerDetailControlView.swift @@ -72,6 +72,27 @@ class SPPlayerDetailControlView: SPPlayerControlView { } } + override var videoInfo: SPVideoInfoModel? { + didSet { + if videoInfo?.is_lock == true { + lockView.isHidden = false + lockView.videoInfo = videoInfo + } else { + lockView.isHidden = true + } + } + } + + ///上一集是否加锁 + var hasLockUpEpisode = false { + didSet { + lockView.isUnlockUpEpisode = hasLockUpEpisode + } + } + + ///点击解锁按钮 + var clickUnlockButton: (() -> Void)? + ///暂停按钮倒计时 private var timer: Timer? @@ -129,6 +150,15 @@ class SPPlayerDetailControlView: SPPlayerControlView { return button }() + ///锁定视图 + private lazy var lockView: SPPlayLockView = { + let view = SPPlayLockView() + view.clickUnlockButton = { [weak self] in + self?.clickUnlockButton?() + } + return view + }() + deinit { self.viewModel?.removeObserver(self, forKeyPath: "speedModel") } @@ -219,6 +249,8 @@ extension SPPlayerDetailControlView { toolView.addSubview(progressTimeLabel) addSubview(retreatButton) addSubview(advanceButton) + addSubview(lockView) +// self.bringSubviewToFront(rightFeatureView) self.progressTimeLabel.snp.makeConstraints { make in make.centerY.equalToSuperview() @@ -235,6 +267,10 @@ extension SPPlayerDetailControlView { make.left.equalTo(playImageView.snp.right).offset(kSPMainW(50)) } + lockView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + } } diff --git a/MoviaBox/Class/Player/View/SPPlayerListCell.swift b/MoviaBox/Class/Player/View/SPPlayerListCell.swift index cd49b9b..c7ce1ad 100644 --- a/MoviaBox/Class/Player/View/SPPlayerListCell.swift +++ b/MoviaBox/Class/Player/View/SPPlayerListCell.swift @@ -43,7 +43,7 @@ class SPPlayerListCell: SPCollectionViewCell, SPPlayerProtocol { return imageView }() - private lazy var controlView: SPPlayerControlView = { + private(set) lazy var controlView: SPPlayerControlView = { let view = PlayerControlViewClass.init() view.panProgressFinishBlock = { [weak self] progress in guard let self = self else { return } diff --git a/MoviaBox/Class/Wallet/Model/SPVideoUnlockModel.swift b/MoviaBox/Class/Wallet/Model/SPVideoUnlockModel.swift new file mode 100644 index 0000000..a6c00da --- /dev/null +++ b/MoviaBox/Class/Wallet/Model/SPVideoUnlockModel.swift @@ -0,0 +1,26 @@ +// +// SPVideoUnlockModel.swift +// MoviaBox +// +// Created by 佳尔 on 2025/5/6. +// + +import UIKit +import SmartCodable + +class SPVideoUnlockModel: SPModel, SmartCodable { + + enum ResponseStatus: String, SmartCaseDefaultable { + ///前面还有没购买的剧 + case jump = "jump" + ///没找到视频 + case noPlay = "no_play" + ///金币不足跳充值 + case notEnough = "not_enough" + ///购买成功 + case success = "success" + } + + var status: ResponseStatus? + +} diff --git a/MoviaBox/Source/Assets.xcassets/icon/lock_icon_02.imageset/Contents.json b/MoviaBox/Source/Assets.xcassets/icon/lock_icon_02.imageset/Contents.json new file mode 100644 index 0000000..597a9e4 --- /dev/null +++ b/MoviaBox/Source/Assets.xcassets/icon/lock_icon_02.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame 125@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame 125@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/MoviaBox/Source/Assets.xcassets/icon/lock_icon_02.imageset/Frame 125@2x.png b/MoviaBox/Source/Assets.xcassets/icon/lock_icon_02.imageset/Frame 125@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..48f1088838b539bdf837043e0798f3db8aa27477 GIT binary patch literal 2615 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 ZJ_X}t*IxGop>zNM002ovPDHLkV1has4K)A& literal 0 HcmV?d00001 diff --git a/MoviaBox/Source/Assets.xcassets/icon/lock_icon_02.imageset/Frame 125@3x.png b/MoviaBox/Source/Assets.xcassets/icon/lock_icon_02.imageset/Frame 125@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..238213793ee77173cb1612d4c2e0f6b852a470a8 GIT binary patch literal 3848 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