视频加锁UI,视频页面支付UI

This commit is contained in:
zjx 2025-06-03 15:57:10 +08:00
parent c10ccac600
commit 05eb92630a
36 changed files with 934 additions and 41 deletions

View File

@ -171,6 +171,13 @@
BFF5AFD42DE9A5FB0044227A /* VPVIPRecordCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF5AFD32DE9A5FB0044227A /* VPVIPRecordCell.swift */; };
BFF5AFD62DE9A8D70044227A /* VPWalletHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF5AFD52DE9A8D70044227A /* VPWalletHeaderView.swift */; };
BFF5AFD82DE9B8010044227A /* VPWalletHeaderItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF5AFD72DE9B8010044227A /* VPWalletHeaderItemView.swift */; };
BFF5AFDA2DEE90350044227A /* VPVideoLockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF5AFD92DEE90350044227A /* VPVideoLockView.swift */; };
BFF5AFDC2DEEA09F0044227A /* VPVideoUnlockModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF5AFDB2DEEA09F0044227A /* VPVideoUnlockModel.swift */; };
BFF5AFDE2DEEBF370044227A /* VPPlayerRechargeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF5AFDD2DEEBF370044227A /* VPPlayerRechargeView.swift */; };
BFF5AFE02DEEC5AB0044227A /* VPPlayerVipBuyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF5AFDF2DEEC5AA0044227A /* VPPlayerVipBuyView.swift */; };
BFF5AFE22DEED2960044227A /* VPPlayerCoinBuyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF5AFE12DEED2960044227A /* VPPlayerCoinBuyView.swift */; };
BFF5AFE62DEEDB7F0044227A /* VPRewardsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF5AFE52DEEDB7F0044227A /* VPRewardsViewController.swift */; };
BFF5B21C2DEEDE130044227A /* VPWebViewController+Script.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF5B21B2DEEDE130044227A /* VPWebViewController+Script.swift */; };
F939C04AD4003BA127F15C28 /* Pods_Veloria.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34F57E87E765BF8D72A43DCA /* Pods_Veloria.framework */; };
/* End PBXBuildFile section */
@ -348,6 +355,13 @@
BFF5AFD32DE9A5FB0044227A /* VPVIPRecordCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPVIPRecordCell.swift; sourceTree = "<group>"; };
BFF5AFD52DE9A8D70044227A /* VPWalletHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPWalletHeaderView.swift; sourceTree = "<group>"; };
BFF5AFD72DE9B8010044227A /* VPWalletHeaderItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPWalletHeaderItemView.swift; sourceTree = "<group>"; };
BFF5AFD92DEE90350044227A /* VPVideoLockView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPVideoLockView.swift; sourceTree = "<group>"; };
BFF5AFDB2DEEA09F0044227A /* VPVideoUnlockModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPVideoUnlockModel.swift; sourceTree = "<group>"; };
BFF5AFDD2DEEBF370044227A /* VPPlayerRechargeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPPlayerRechargeView.swift; sourceTree = "<group>"; };
BFF5AFDF2DEEC5AA0044227A /* VPPlayerVipBuyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPPlayerVipBuyView.swift; sourceTree = "<group>"; };
BFF5AFE12DEED2960044227A /* VPPlayerCoinBuyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPPlayerCoinBuyView.swift; sourceTree = "<group>"; };
BFF5AFE52DEEDB7F0044227A /* VPRewardsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPRewardsViewController.swift; sourceTree = "<group>"; };
BFF5B21B2DEEDE130044227A /* VPWebViewController+Script.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VPWebViewController+Script.swift"; sourceTree = "<group>"; };
E0BDA3570E00C90877E45AA0 /* Pods-VideoPlayer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VideoPlayer.debug.xcconfig"; path = "Target Support Files/Pods-VideoPlayer/Pods-VideoPlayer.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -435,6 +449,7 @@
1B056E362DDAC1E8007EE38D /* Class */ = {
isa = PBXGroup;
children = (
BFF5AFE32DEED86B0044227A /* Rewards */,
BFF5AFBB2DE837710044227A /* Wallet */,
BF0FA7A62DE4437F00C9E5F2 /* Me */,
BF0FA7952DE1948A00C9E5F2 /* MyList */,
@ -698,6 +713,7 @@
BF0FA7012DDC667C00C9E5F2 /* VPVideoInfoModel.swift */,
BF0FA7602DDFFE7100C9E5F2 /* VPVideoDetailModel.swift */,
BF0FA7702DE062EB00C9E5F2 /* VPVideoRateModel.swift */,
BFF5AFDB2DEEA09F0044227A /* VPVideoUnlockModel.swift */,
);
path = Model;
sourceTree = "<group>";
@ -794,6 +810,10 @@
BF0FA76A2DE0533400C9E5F2 /* VPEpisodeMenuView.swift */,
BF0FA76E2DE062A700C9E5F2 /* VPRateSelectedView.swift */,
BF0FA7722DE0671200C9E5F2 /* VPRateSelectedCell.swift */,
BFF5AFD92DEE90350044227A /* VPVideoLockView.swift */,
BFF5AFDD2DEEBF370044227A /* VPPlayerRechargeView.swift */,
BFF5AFDF2DEEC5AA0044227A /* VPPlayerVipBuyView.swift */,
BFF5AFE12DEED2960044227A /* VPPlayerCoinBuyView.swift */,
);
path = View;
sourceTree = "<group>";
@ -890,7 +910,6 @@
children = (
BF0FA7AE2DE443E000C9E5F2 /* VPMeViewController.swift */,
BF5E75AE2DE4632200DE9DFE /* VPAboutUsViewController.swift */,
BF5E75B02DE4656600DE9DFE /* VPCampaignWebViewController.swift */,
);
path = Controller;
sourceTree = "<group>";
@ -924,6 +943,8 @@
children = (
BF0FA7B52DE44E8000C9E5F2 /* VPWebView.swift */,
BF0FA7B72DE44FCC00C9E5F2 /* VPWebViewController.swift */,
BF5E75B02DE4656600DE9DFE /* VPCampaignWebViewController.swift */,
BFF5B21B2DEEDE130044227A /* VPWebViewController+Script.swift */,
);
path = WebView;
sourceTree = "<group>";
@ -1015,6 +1036,22 @@
path = Model;
sourceTree = "<group>";
};
BFF5AFE32DEED86B0044227A /* Rewards */ = {
isa = PBXGroup;
children = (
BFF5AFE42DEED8800044227A /* Controller */,
);
path = Rewards;
sourceTree = "<group>";
};
BFF5AFE42DEED8800044227A /* Controller */ = {
isa = PBXGroup;
children = (
BFF5AFE52DEEDB7F0044227A /* VPRewardsViewController.swift */,
);
path = Controller;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -1180,6 +1217,7 @@
BF0FA73B2DDED1C700C9E5F2 /* VPHomeListCell.swift in Sources */,
BF0FA79F2DE1A29A00C9E5F2 /* VPCollectListCell.swift in Sources */,
BF0FA7592DDF1C2800C9E5F2 /* VPPlayerProgressView.swift in Sources */,
BFF5AFE02DEEC5AB0044227A /* VPPlayerVipBuyView.swift in Sources */,
BFF5AFCA2DE97B7A0044227A /* VPWalletViewController.swift in Sources */,
BF0FA71B2DDC7FF200C9E5F2 /* VPImageView.swift in Sources */,
BF0FA7522DDF134700C9E5F2 /* VPVideoPlayerControlView.swift in Sources */,
@ -1190,6 +1228,7 @@
BFF5AFA42DE6F15E0044227A /* VPMeVipCell.swift in Sources */,
BFF5AFB02DE7F9A80044227A /* VPVipViewController.swift in Sources */,
BF0FA75D2DDF208400C9E5F2 /* VPExplorePlayerControlView.swift in Sources */,
BFF5B21C2DEEDE130044227A /* VPWebViewController+Script.swift in Sources */,
BF5E75CB2DE5692D00DE9DFE /* UINavigationController+JXTransition.swift in Sources */,
BF5E75CC2DE5692D00DE9DFE /* JXTransitionDefine.swift in Sources */,
BF5E75CD2DE5692D00DE9DFE /* JXTransitionDelegateBridge.swift in Sources */,
@ -1201,6 +1240,7 @@
BF5E75D22DE5692D00DE9DFE /* JXNavigationInteractiveTransition.swift in Sources */,
BF5E75D32DE5692D00DE9DFE /* JXBaseAnimatedTransition.swift in Sources */,
1B056E462DDAC370007EE38D /* UIScreen+VPAdd.swift in Sources */,
BFF5AFDE2DEEBF370044227A /* VPPlayerRechargeView.swift in Sources */,
BFF5AFAA2DE7070A0044227A /* VPMeCoinItemView.swift in Sources */,
BF0FA7692DE0502900C9E5F2 /* VPEpisodeCell.swift in Sources */,
BF5E75BE2DE54B2800DE9DFE /* VPAboutUsHeaderView.swift in Sources */,
@ -1223,6 +1263,7 @@
BFF5AFCE2DE99C730044227A /* VPCoinRecordViewController.swift in Sources */,
BF0FA7732DE0671200C9E5F2 /* VPRateSelectedCell.swift in Sources */,
1B056E4D2DDAC7C1007EE38D /* VPTabBarController.swift in Sources */,
BFF5AFDC2DEEA09F0044227A /* VPVideoUnlockModel.swift in Sources */,
BF5E75C02DE5566200DE9DFE /* VPHomePageControlView.swift in Sources */,
BFF5AFC22DE837FC0044227A /* VPPayTemplateItem.swift in Sources */,
BF0FA6DA2DDC5CB600C9E5F2 /* VPLoginManager.swift in Sources */,
@ -1239,6 +1280,7 @@
1B056E3F2DDAC2DB007EE38D /* VPCryptorService.swift in Sources */,
BF0FA7202DDC83AE00C9E5F2 /* JXButton.swift in Sources */,
BF0FA7912DE16CBF00C9E5F2 /* VPSearchHistoryView.swift in Sources */,
BFF5AFE22DEED2960044227A /* VPPlayerCoinBuyView.swift in Sources */,
BF0FA7992DE1951A00C9E5F2 /* VPMyListViewController.swift in Sources */,
BF0FA7452DDF027900C9E5F2 /* VPPlayer.swift in Sources */,
BFF5AFC62DE863C00044227A /* VPGradientButton.swift in Sources */,
@ -1280,6 +1322,7 @@
BF0FA7172DDC78FF00C9E5F2 /* ZKCycleScrollView.swift in Sources */,
BF0FA7612DDFFE7100C9E5F2 /* VPVideoDetailModel.swift in Sources */,
BFF5AFD22DE9A58A0044227A /* VPVIPRecordViewController.swift in Sources */,
BFF5AFDA2DEE90350044227A /* VPVideoLockView.swift in Sources */,
BF5E75B82DE46F7100DE9DFE /* VPNetworkReachabilityManager.swift in Sources */,
BF0FA6D52DDC5B5D00C9E5F2 /* VPApi.swift in Sources */,
BF0FA7C12DE45D5D00C9E5F2 /* VPUserInfo.swift in Sources */,
@ -1289,6 +1332,7 @@
BFF5AFC02DE837D60044227A /* VPPayTemplateModel.swift in Sources */,
BF0FA7B62DE44E8000C9E5F2 /* VPWebView.swift in Sources */,
1B056E492DDAC3DF007EE38D /* VPAppTool.swift in Sources */,
BFF5AFE62DEEDB7F0044227A /* VPRewardsViewController.swift in Sources */,
BF0FA74E2DDF067E00C9E5F2 /* VPVideoPlayViewModel.swift in Sources */,
BF0FA75B2DDF206000C9E5F2 /* VPExplorePlayerCell.swift in Sources */,
BF0FA7412DDEFBC700C9E5F2 /* UIScrollView+VPRefresh.swift in Sources */,

View File

@ -154,4 +154,22 @@ extension VPTabBarController {
viewController?.showHistoryPage()
}
}
func openCollectPage() {
var index: Int?
var viewController: VPMyListViewController?
self.viewControllers?.enumerated().forEach({
guard let nav = $1 as? UINavigationController else { return }
if let vc = nav.viewControllers.first as? VPMyListViewController {
index = $0
viewController = vc
}
})
if let index = index {
self.selectedIndex = index
viewController?.showCollectPage()
}
}
}

View File

@ -11,7 +11,7 @@ import UIKit
extension UIDevice {
//http://theiphonewiki.com/wiki/Models
static func sp_machineModelName() -> String {
static func vp_machineModelName() -> String {
guard let machineModel = UIDevice.current.machineModel else { return "" }
let map = [
"iPhone1,1" : "iPhone",

View File

@ -23,4 +23,19 @@ class VPWalletAPI {
completer?(response.data)
}
}
///
static func requestCoinUnlockVideo(shortPlayId: String, videoId: String, completer: ((_ model: VPVideoUnlockModel?) -> Void)?) {
var param = VPNetworkParameters(path: "/buy_video")
param.isLoding = true
param.parameters = [
"short_play_id" : shortPlayId,
"video_id" : videoId,
]
VPNetwork.request(parameters: param) { (response: VPNetworkResponse<VPVideoUnlockModel>) in
completer?(response.data)
}
}
}

View File

@ -89,7 +89,7 @@ extension VPApi: TargetType {
"app-name" : "",
"system-type" : "ios",
"idfa" : JXUUID.idfa(),
"model" : UIDevice.sp_machineModelName(),
"model" : UIDevice.vp_machineModelName(),
// "security" : "false",
]
//

View File

@ -11,8 +11,9 @@ class VPCampaignWebViewController: VPWebViewController {
var id: String?
///
private var receiveDataCount = 0
var theme: String? = "theme_1"
override func viewDidLoad() {
super.viewDidLoad()
@ -60,8 +61,12 @@ extension VPCampaignWebViewController {
"time_zone" : String.timeZone(),
"lang" : VPLocalizedManager.shared.currentLocalizedKey,
"type" : "ios",
"theme" : "theme_1",
]
if let theme = theme {
dic["theme"] = theme
}
if let id = id {
dic["id"] = id
}

View File

@ -33,16 +33,15 @@ class VPWebView: WKWebView {
weak var delegate: VPWebViewDelegate?
// private(set) var scriptMessageHandlerArray: [SPWebViewMessageName] = [
// WebMessageAPP,
// WebMessageOpenFeedbackList,
// WebMessageOpenFeedbackDetail,
// WebMessageOpenPhotoPicker,
// ]
private(set) var scriptMessageHandlerArray: [VPWebViewMessageName] = [
VPWebMessageAPP,
VPWebMessageOpenFeedbackList,
VPWebMessageOpenFeedbackDetail,
VPWebMessageOpenPhotoPicker,
]
deinit {
self.removeObserver(self, forKeyPath: "estimatedProgress")
self.removeObserver(self, forKeyPath: "title")
@ -50,7 +49,7 @@ class VPWebView: WKWebView {
override init(frame: CGRect, configuration: WKWebViewConfiguration) {
super.init(frame: frame, configuration: configuration)
// addScriptMessageHandler()
addScriptMessageHandler()
_setupInit()
}
required init?(coder: NSCoder) {
@ -78,20 +77,20 @@ class VPWebView: WKWebView {
func load(urlStr: String) {
guard let url = URL(string: urlStr) else { return }
var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 30)
let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 30)
self.load(request)
}
// func removeScriptMessageHandler() {
// self.scriptMessageHandlerArray.forEach{
// configuration.userContentController.removeScriptMessageHandler(forName: $0)
// }
// }
// func addScriptMessageHandler() {
// self.scriptMessageHandlerArray.forEach{
// configuration.userContentController.add(YYWeakProxy(target: self) as! WKScriptMessageHandler, name: $0)
// }
// }
func removeScriptMessageHandler() {
self.scriptMessageHandlerArray.forEach{
configuration.userContentController.removeScriptMessageHandler(forName: $0)
}
}
func addScriptMessageHandler() {
self.scriptMessageHandlerArray.forEach{
configuration.userContentController.add(YYWeakProxy(target: self) as! WKScriptMessageHandler, name: $0)
}
}
}

View File

@ -0,0 +1,32 @@
//
// VPWebViewController+Script.swift
// Veloria
//
// Created by on 2025/6/3.
//
import UIKit
import WebKit
typealias VPWebViewMessageName = String
///APP
let VPWebMessageAPP: VPWebViewMessageName = "js2app"
///
let VPWebMessageOpenFeedbackList: VPWebViewMessageName = "openFeedbackList"
///
let VPWebMessageOpenFeedbackDetail: VPWebViewMessageName = "openFeedbackDetail"
///
let VPWebMessageOpenPhotoPicker: VPWebViewMessageName = "openPhotoPicker"
extension VPWebViewController {
func vp_webViewUserContentController(didReceive message: WKScriptMessage) {
let name = message.name
let body = message.body
}
}

View File

@ -98,6 +98,6 @@ extension VPWebViewController: VPWebViewDelegate {
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
// _webViewUserContentController(didReceive: message)
vp_webViewUserContentController(didReceive: message)
}
}

View File

@ -32,11 +32,13 @@ class VPMeToolCell: VPTableViewCell {
private lazy var rewardsButton: UIButton = {
let button = self.createButton(icon: UIImage(named: "rewards_icon_01"), title: "Rewards".localized)
button.addTarget(self, action: #selector(handleRewardsButton), for: .touchUpInside)
return button
}()
private lazy var favoritesButton: UIButton = {
let button = self.createButton(icon: UIImage(named: "favorites_icon_01"), title: "Favorites".localized)
button.addTarget(self, action: #selector(handleFavoritesButton), for: .touchUpInside)
return button
}()
@ -76,6 +78,16 @@ extension VPMeToolCell {
self.viewController?.navigationController?.pushViewController(vc, animated: true)
}
@objc private func handleRewardsButton() {
let vc = VPRewardsViewController()
self.viewController?.navigationController?.pushViewController(vc, animated: true)
}
@objc private func handleFavoritesButton() {
let tabbarVC = VPAppTool.rootViewController as? VPTabBarController
tabbarVC?.openCollectPage()
}
}
extension VPMeToolCell {

View File

@ -138,6 +138,12 @@ class VPMyListViewController: VPViewController {
self?.pageView.selectMenu(with: 1)
}
}
func showCollectPage() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
self?.pageView.selectMenu(with: 0)
}
}
}

View File

@ -59,8 +59,15 @@ class VPDetailPlayerViewController: VPVideoPlayerViewController {
override func play() {
guard let videoInfo = self.viewModel.currentPlayer?.videoInfo else { return }
super.play()
if videoInfo.is_lock == true {
self.pause()
self.onRecharge()
return
}
super.play()
VPVideoAPI.requestCreatePlayHistory(videoId: videoInfo.short_play_video_id, shortPlayId: videoInfo.short_play_id)
}
@ -92,6 +99,9 @@ extension VPDetailPlayerViewController {
self?.onEpisode()
}
self.viewModel.handleUnlock = { [weak self] in
self?.unlockVideo()
}
}
}
@ -109,6 +119,50 @@ extension VPDetailPlayerViewController {
self.episodeView = view
}
private func unlockVideo() {
guard let videoInfo = self.viewModel.currentPlayer?.videoInfo else { return }
guard let sId = videoInfo.short_play_id, let vId = videoInfo.short_play_video_id else { return }
VPWalletAPI.requestCoinUnlockVideo(shortPlayId: sId, videoId: vId) { [weak self] model in
guard let self = self else { return }
guard let model = model else { return }
switch model.status {
case .jump:
VPToast.show(text: "kLockPreviousEpisodeText".localized)
case .noPlay:
VPToast.show(text: "kLockFailText".localized)
case .notEnough:
self.onRecharge()
break
case .success:
videoInfo.is_lock = false
self.reloadData { [weak self] in
guard let self = self else { return }
self.play()
}
VPLoginManager.manager.updateUserInfo(completer: nil)
default: break
}
}
}
///
private func onRecharge() {
let view = VPPlayerRechargeView()
view.present(in: nil)
}
}
//MARK: -------------- VPPlayerListViewControllerDataSource --------------

View File

@ -0,0 +1,25 @@
//
// VPVideoUnlockModel.swift
// Veloria
//
// Created by on 2025/6/3.
//
import UIKit
import SmartCodable
class VPVideoUnlockModel: VPModel, SmartCodable {
enum ResponseStatus: String, SmartCaseDefaultable {
///
case jump = "jump"
///
case noPlay = "no_play"
///
case notEnough = "not_enough"
///
case success = "success"
}
var status: ResponseStatus?
}

View File

@ -21,6 +21,8 @@ class VPDetailPlayerControlView: VPVideoPlayerControlView {
override var videoInfo: VPVideoInfoModel? {
didSet {
epView.setTitle(String(format: "EP.%@".localized, "\(videoInfo?.episode ?? "0")"), for: .normal)
lockView.isHidden = !(videoInfo?.is_lock ?? false)
lockView.videoInfo = videoInfo
}
}
@ -113,6 +115,15 @@ class VPDetailPlayerControlView: VPVideoPlayerControlView {
return view
}()
private lazy var lockView: VPVideoLockView = {
let view = VPVideoLockView()
view.clickUnlockButton = { [weak self] in
guard let self = self else { return }
self.viewModel?.handleUnlock?()
}
return view
}()
deinit {
self.viewModel?.removeObserver(self, forKeyPath: "rateModel")
}
@ -172,6 +183,7 @@ extension VPDetailPlayerControlView {
epBgView.addSubview(allEpView)
addSubview(rateButton)
addSubview(timeLabel)
addSubview(lockView)
self.sendSubviewToBack(self.bottomView)
@ -216,6 +228,10 @@ extension VPDetailPlayerControlView {
make.left.equalToSuperview().offset(15)
make.bottom.equalTo(epBgView.snp.top).offset(-16)
}
lockView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
}
}

View File

@ -13,22 +13,25 @@ class VPEpisodeCell: VPCollectionViewCell {
var videoInfoModel: VPVideoInfoModel? {
didSet {
numLabel.text = videoInfoModel?.episode
lockBgView.isHidden = !(videoInfoModel?.is_lock ?? false)
numLabel.textColor = videoInfoModel?.is_lock == true ? .colorFFFFFF(alpha: 0.5) : .colorFFFFFF()
}
}
var vp_isSelected: Bool = false {
didSet {
if vp_isSelected {
contentView.vp_setGradientBorder()
borderView.isHidden = false
contentView.backgroundColor = .color05CEA0(alpha: 0.1)
} else {
contentView.vp_removeGradientBorder()
borderView.isHidden = true
contentView.backgroundColor = .colorFFFFFF(alpha: 0.1)
}
}
}
private lazy var bgView: UIView = {
private lazy var borderView: UIView = {
let view = UIView()
view.vp_setGradientBorder()
view.layer.cornerRadius = 6
@ -36,8 +39,6 @@ class VPEpisodeCell: VPCollectionViewCell {
return view
}()
private lazy var numLabel: UILabel = {
let label = UILabel()
label.font = .fontRegular(ofSize: 14)
@ -45,6 +46,16 @@ class VPEpisodeCell: VPCollectionViewCell {
return label
}()
private lazy var lockBgView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "lock_bg_icon_01"))
return imageView
}()
private lazy var lockImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "lock_icon_01"))
return imageView
}()
override init(frame: CGRect) {
super.init(frame: frame)
@ -63,16 +74,26 @@ extension VPEpisodeCell {
contentView.layer.cornerRadius = 6
contentView.layer.masksToBounds = true
// contentView.addSubview(bgView)
contentView.addSubview(numLabel)
contentView.addSubview(lockBgView)
lockBgView.addSubview(lockImageView)
contentView.addSubview(borderView)
// bgView.snp.makeConstraints { make in
// make.edges.equalToSuperview()
// }
borderView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
numLabel.snp.makeConstraints { make in
make.center.equalToSuperview()
}
lockBgView.snp.makeConstraints { make in
make.top.right.equalToSuperview()
}
lockImageView.snp.makeConstraints { make in
make.center.equalToSuperview()
}
}
}

View File

@ -0,0 +1,105 @@
//
// VPPlayerCoinBuyView.swift
// Veloria
//
// Created by on 2025/6/3.
//
import UIKit
class VPPlayerCoinBuyView: UIView {
var dataArr: [VPPayTemplateItem] = [] {
didSet {
self.collectionView.reloadData()
}
}
private lazy var selectedIndex = 0
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 10
layout.minimumInteritemSpacing = 10
layout.itemSize = .init(width: 106, height: 108)
layout.sectionInset = .init(top: 0, left: 15, bottom: 0, right: 15)
layout.scrollDirection = .horizontal
return layout
}()
private lazy var collectionView: VPCollectionView = {
let collectionView = VPCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
collectionView.delegate = self
collectionView.dataSource = self
// collectionView.isScrollEnabled = false
collectionView.showsHorizontalScrollIndicator = false
collectionView.register(VPCoinsBuyCell.self, forCellWithReuseIdentifier: "cell")
return collectionView
}()
private lazy var tipLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 0
label.textColor = .colorFFFFFF(alpha: 0.5)
label.font = .fontRegular(ofSize: 12)
label.text = "kStoreTips".localized
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
vp_setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension VPPlayerCoinBuyView {
private func vp_setupUI() {
addSubview(collectionView)
addSubview(tipLabel)
collectionView.snp.makeConstraints { make in
make.left.right.top.equalToSuperview()
make.height.equalTo(collectionViewLayout.itemSize.height)
// make.bottom.equalToSuperview()
}
tipLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(15)
make.right.lessThanOrEqualToSuperview().offset(-15)
make.top.equalTo(collectionView.snp.bottom).offset(12)
make.bottom.equalToSuperview()
}
}
}
//MARK: -------------- UICollectionViewDelegate UICollectionViewDataSource --------------
extension VPPlayerCoinBuyView: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! VPCoinsBuyCell
cell.item = self.dataArr[indexPath.row]
cell.vp_isSelected = indexPath.row == selectedIndex
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.dataArr.count
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.selectedIndex = indexPath.row
collectionView.reloadData()
}
}

View File

@ -0,0 +1,195 @@
//
// VPPlayerRechargeView.swift
// Veloria
//
// Created by on 2025/6/3.
//
import UIKit
class VPPlayerRechargeView: HWPanModalContentView {
//MARK: UI
private lazy var bgView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "bg_image_01"))
return imageView
}()
private lazy var closeButton: UIButton = {
let button = UIButton(type: .custom)
button.setImage(UIImage(named: "close_icon_01"), for: .normal)
button.addTarget(self, action: #selector(handleCloseButton), for: .touchUpInside)
return button
}()
private lazy var coinLabel: UILabel = {
let label = UILabel()
label.font = .fontRegular(ofSize: 13)
return label
}()
private lazy var coinIconImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "coin_icon_07"))
return imageView
}()
private lazy var scrollView: VPScrollView = {
let scrollView = VPScrollView()
return scrollView
}()
private lazy var vipView: VPPlayerVipBuyView = {
let view = VPPlayerVipBuyView()
return view
}()
private lazy var coinView: VPPlayerCoinBuyView = {
let view = VPPlayerCoinBuyView()
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
vp_setupUI()
updateCoin()
requestRechargeData()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc private func handleCloseButton() {
self.dismiss(animated: true) {
}
}
//MARK: HWPanModalPresentable
override func panScrollable() -> UIScrollView? {
return scrollView
}
override func longFormHeight() -> PanModalHeight {
return PanModalHeightMake(.content, UIScreen.height * (2 / 3))
}
override func showDragIndicator() -> Bool {
return false
}
override func backgroundConfig() -> HWBackgroundConfig {
let config = HWBackgroundConfig()
config.backgroundAlpha = 0.6
return config
}
override func allowsDragToDismiss() -> Bool {
return false
}
override func allowsTapBackgroundToDismiss() -> Bool {
return false
}
override func allowsPullDownWhenShortState() -> Bool {
return false
}
override func minVerticalVelocityToTriggerDismiss() -> CGFloat {
return 0
}
override func showsScrollableVerticalScrollIndicator() -> Bool {
return false
}
}
extension VPPlayerRechargeView {
private func updateCoin() {
let coinCountStr = "\(VPLoginManager.manager.userInfo?.totalCoin ?? 0)"
let text = String(format: "Coins: %@".localized, coinCountStr)
let coinRange = text.ocString().range(of: coinCountStr)
let string = NSMutableAttributedString(string: text)
string.color = .colorFFFFFF()
string.setColor(.color05CEA0(), range: coinRange)
coinLabel.attributedText = string
}
}
extension VPPlayerRechargeView {
private func vp_setupUI() {
addSubview(bgView)
addSubview(closeButton)
addSubview(coinLabel)
addSubview(coinIconImageView)
addSubview(scrollView)
scrollView.addSubview(vipView)
scrollView.addSubview(coinView)
bgView.snp.makeConstraints { make in
make.left.right.top.equalToSuperview()
}
closeButton.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-5)
make.top.equalToSuperview().offset(5)
make.width.equalTo(40)
make.height.equalTo(40)
}
coinLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(15)
make.centerY.equalTo(coinIconImageView)
}
coinIconImageView.snp.makeConstraints { make in
make.left.equalTo(coinLabel.snp.right).offset(3)
make.top.equalToSuperview().offset(40)
}
scrollView.snp.makeConstraints { make in
make.left.right.equalToSuperview()
make.top.equalToSuperview().offset(56)
make.bottom.equalToSuperview()
}
vipView.snp.makeConstraints { make in
make.left.equalToSuperview()
make.centerX.equalToSuperview()
make.top.equalToSuperview()
}
coinView.snp.makeConstraints { make in
make.left.equalToSuperview()
make.centerX.equalToSuperview()
make.top.equalTo(vipView.snp.bottom).offset(20)
make.bottom.equalToSuperview().offset(-(UIScreen.tabbarSafeBottomMargin + 10))
}
}
}
extension VPPlayerRechargeView {
private func requestRechargeData() {
VPWalletAPI.requestPayTemplate { [weak self] model in
guard let self = self else { return }
if let model = model {
self.vipView.dataArr = model.list_sub_vip ?? []
self.coinView.dataArr = model.list_coins ?? []
}
}
}
}

View File

@ -0,0 +1,144 @@
//
// VPPlayerVipBuyView.swift
// Veloria
//
// Created by on 2025/6/3.
//
import UIKit
class VPPlayerVipBuyView: UIView {
var dataArr: [VPPayTemplateItem] = [] {
didSet {
self.collectionView.reloadData()
}
}
private lazy var currentIndex: Int = 0
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.minimumLineSpacing = 10
layout.sectionInset = .init(top: 0, left: 15, bottom: 0, right: 15)
layout.itemSize = .init(width: 332, height: 138)
return layout
}()
private lazy var collectionView: VPCollectionView = {
let collectionView = VPCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.showsHorizontalScrollIndicator = false
collectionView.register(VPVipBuyCell.self, forCellWithReuseIdentifier: "cell")
return collectionView
}()
private lazy var tipBgView: UIView = {
let view = VPGradientView()
view.colors = [UIColor.color05CEA0(alpha: 0.2).cgColor, UIColor.color05CEA0(alpha: 0).cgColor]
view.locations = [0, 1]
view.startPoint = .init(x: 0, y: 0.5)
view.endPoint = .init(x: 1, y: 0.5)
view.layer.cornerRadius = 8
view.layer.masksToBounds = true
return view
}()
private lazy var tipLabel: UILabel = {
let hText1 = "1 week".localized
let text1 = "· " + String(format: "kVipPrivilegeText4".localized, hText1)
let hText2 = "8 days".localized
let text2 = "· " + String(format: "kVipPrivilegeText5".localized, hText2)
let text3 = "· " + String(format: "kVipPrivilegeText6".localized)
let text = text1 + "\n" + text2 + "\n" + text3
let hRange1 = text.ocString().range(of: hText1)
let hRange2 = text.ocString().range(of: hText2)
let string = NSMutableAttributedString(string: text)
string.lineSpacing = 5
string.color = .colorFFFFFF(alpha: 0.8)
string.setColor(.color05CEA0(), range: hRange1)
string.setColor(.color05CEA0(), range: hRange2)
let label = UILabel()
label.font = .fontRegular(ofSize: 12)
label.attributedText = string
label.numberOfLines = 0
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
vp_setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension VPPlayerVipBuyView {
private func vp_setupUI() {
addSubview(collectionView)
addSubview(tipBgView)
tipBgView.addSubview(tipLabel)
collectionView.snp.makeConstraints { make in
make.left.right.equalToSuperview()
make.top.equalToSuperview()
make.height.equalTo(collectionViewLayout.itemSize.height)
}
tipBgView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(15)
make.right.equalToSuperview().offset(-15)
make.top.equalTo(collectionView.snp.bottom).offset(10)
make.bottom.equalToSuperview()
}
tipLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(14)
make.right.lessThanOrEqualToSuperview().offset(-14)
make.top.equalToSuperview().offset(10)
make.centerY.equalToSuperview()
}
}
}
//MARK: -------------- UICollectionViewDelegate UICollectionViewDataSource --------------
extension VPPlayerVipBuyView: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! VPVipBuyCell
cell.item = dataArr[indexPath.row]
cell.vp_isSelected = indexPath.row == currentIndex
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dataArr.count
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if currentIndex != indexPath.row {
currentIndex = indexPath.row
self.collectionView.reloadData()
}
}
}

View File

@ -0,0 +1,92 @@
//
// VPVideoLockView.swift
// Veloria
//
// Created by on 2025/6/3.
//
import UIKit
class VPVideoLockView: UIView {
var videoInfo: VPVideoInfoModel? {
didSet {
unlockButton.setNeedsUpdateConfiguration()
}
}
var clickUnlockButton: (() -> Void)?
private lazy var unlockButton: UIButton = {
var config = UIButton.Configuration.plain()
config.imagePlacement = .leading
config.imagePadding = 10
config.image = UIImage(named: "lock_icon_02")
let button = VPGradientButton(configuration: config)
button.bt_setGradientBorder()
button.colors = [UIColor.color05CEA0(alpha: 0.3).cgColor, UIColor.color7C174F(alpha: 0.3).cgColor]
button.locations = [0, 1]
button.startPoint = .init(x: 0, y: 0.3)
button.endPoint = .init(x: 1, y: 0.8)
button.layer.cornerRadius = 24
button.layer.masksToBounds = true
button.configurationUpdateHandler = { [weak self] button in
guard let self = self else { return }
let title = String(format: "Unlocking costs %@ coins".localized, "\(videoInfo?.coins ?? 0)")
let string = AttributedString.createAttributedString(string: title, color: .colorFFFFFF(), font: .fontRegular(ofSize: 14))
button.configuration?.attributedTitle = string
}
button.addTarget(self, action: #selector(handleUnlockButton), for: .touchUpInside)
return button
}()
private lazy var coinCountLabel: UILabel = {
let userInfo = VPLoginManager.manager.userInfo
let label = UILabel()
label.font = .fontRegular(ofSize: 12)
label.textColor = .colorB5B5B5()
label.text = String(format: "Balance: %@ Coins | %@ Bonus".localized, "\(userInfo?.coin_left_total ?? 0)", "\(userInfo?.send_coin_left_total ?? 0)")
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .color000000(alpha: 0.8)
vp_setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc private func handleUnlockButton() {
self.clickUnlockButton?()
}
}
extension VPVideoLockView {
private func vp_setupUI() {
addSubview(unlockButton)
addSubview(coinCountLabel)
unlockButton.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.left.equalToSuperview().offset(40)
make.centerY.equalToSuperview()
make.height.equalTo(48)
}
coinCountLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(unlockButton.snp.bottom).offset(10)
}
}
}

View File

@ -17,9 +17,6 @@ class VPVideoPlayViewModel: NSObject {
oldValue?.isCurrent = false
oldValue?.pause()
// self.currentPlayer?.playerFinishHadle = { [weak self] in
// self?.handlePlayFinish?()
// }
self.currentPlayer?.isCurrent = true
self.currentPlayer?.rate = rateModel.rate.getRate()
}
@ -45,4 +42,6 @@ class VPVideoPlayViewModel: NSObject {
var handlePlayTimeDidChange: ((_ time: Int) -> Void)?
///
var handleEpisode: (() -> Void)?
var handleUnlock: (() -> Void)?
}

View File

@ -0,0 +1,20 @@
//
// VPRewardsViewController.swift
// Veloria
//
// Created by on 2025/6/3.
//
import UIKit
class VPRewardsViewController: VPCampaignWebViewController {
override func viewDidLoad() {
self.urlStr = kVPRewardsWebUrl
super.viewDidLoad()
self.theme = nil
}
}

View File

@ -174,7 +174,6 @@ extension VPCoinsViewController: UICollectionViewDelegate, UICollectionViewDataS
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
vpLog(message: indexPath.row)
self.selectedIndex = indexPath.row
collectionView.reloadData()
}

View File

@ -16,7 +16,6 @@ class VPWalletHeaderView: UIView {
coinCountLabel.text = "\(userInfo?.totalCoin ?? 0)"
rechargeCoinView.count = userInfo?.coin_left_total ?? 0
sendCoinView.count = userInfo?.send_coin_left_total ?? 0
// coinCountLabel.text = "1234567890"
}
}

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "金币1 2@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "金币1 2@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Rectangle 26@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Rectangle 26@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Frame@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Frame@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 B

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Frame@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Frame@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 697 B

View File

@ -39,7 +39,6 @@
"Donate" = "Donate";
"Wallet" = "Wallet";
"Store" = "Store";
"Rewards" = "Rewards";
"Favorites" = "Favorites";
"Language" = "Language";
"VIP" = "VIP";
@ -63,6 +62,8 @@
"VIP Record" = "VIP Record";
"Total Coins" = "Total Coins";
"Recharge" = "Recharge";
"Unlocking costs %@ coins" = "Unlocking costs %@ coins";
"Balance: %@ Coins | %@ Bonus" = "Balance: %@ Coins | %@ Bonus";
"kHomeTitleText" = "10,000+ addictive shorts await!";
@ -78,6 +79,10 @@
"kVipPrivilegeText6" = "Auto renew, cancel anytime";
//无网提示
"kNetworkToast01" = "The service is abnormal. Check the network.";
//解锁上一集提示
"kLockPreviousEpisodeText" = "The prequel to this series is not unlocked. Please unlock the prequel before unlocking this series";
//解锁失败
"kLockFailText" = "Purchase failed, please try again later!";
"kStoreTips" = "1. Coins are virtual items and cannot be refunded. Use it for this product.