Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
b02f655fb1 | ||
|
ab478455fd | ||
|
c1ad644d81 | ||
|
01eb1c6c6a | ||
|
ee36da46d3 | ||
|
d6042c7786 | ||
|
ac09b0612b |
@ -7,6 +7,7 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
1B222BCF2E2B80DD002F5A68 /* SPPayTemplateRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B222BCE2E2B80DD002F5A68 /* SPPayTemplateRequest.swift */; };
|
||||
1BB91D102E04FD6A00A2C715 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BB91BBD2E04FD6A00A2C715 /* AppDelegate.swift */; };
|
||||
1BB91D112E04FD6A00A2C715 /* AppDelegate+APNS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BB91BBE2E04FD6A00A2C715 /* AppDelegate+APNS.swift */; };
|
||||
1BB91D122E04FD6A00A2C715 /* AppDelegate+Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BB91BBF2E04FD6A00A2C715 /* AppDelegate+Config.swift */; };
|
||||
@ -316,7 +317,7 @@
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
0538826A0638D33FEF3A2E38 /* Pods-ThimraTV.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ThimraTV.debug.xcconfig"; path = "Target Support Files/Pods-ThimraTV/Pods-ThimraTV.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
109EB01BE447EE135493CA38 /* Pods-MoviaBox.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MoviaBox.release.xcconfig"; path = "Target Support Files/Pods-MoviaBox/Pods-MoviaBox.release.xcconfig"; sourceTree = "<group>"; };
|
||||
1B222BCE2E2B80DD002F5A68 /* SPPayTemplateRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPPayTemplateRequest.swift; sourceTree = "<group>"; };
|
||||
1BB91BBD2E04FD6A00A2C715 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
1BB91BBE2E04FD6A00A2C715 /* AppDelegate+APNS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+APNS.swift"; sourceTree = "<group>"; };
|
||||
1BB91BBF2E04FD6A00A2C715 /* AppDelegate+Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Config.swift"; sourceTree = "<group>"; };
|
||||
@ -606,11 +607,8 @@
|
||||
1BF513202E2662DC009750EA /* SPAdmobBannerAd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPAdmobBannerAd.swift; sourceTree = "<group>"; };
|
||||
1BF513222E273479009750EA /* SPApplovinBannerAd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPApplovinBannerAd.swift; sourceTree = "<group>"; };
|
||||
1DBC40592DA4EDFC0093FCB0 /* ThimraTV.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ThimraTV.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
1F666DE0B12C863F26BE5027 /* Pods-MoviaBox.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MoviaBox.debug.xcconfig"; path = "Target Support Files/Pods-MoviaBox/Pods-MoviaBox.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
A1174E10BCF2C606F7818792 /* Pods-ThimraTV.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ThimraTV.release.xcconfig"; path = "Target Support Files/Pods-ThimraTV/Pods-ThimraTV.release.xcconfig"; sourceTree = "<group>"; };
|
||||
B64805795B479324EB764157 /* Pods_ThimraTV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ThimraTV.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
F7763FEFB6BEB1A75D6FBA0A /* Pods-Thimra.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Thimra.debug.xcconfig"; path = "Target Support Files/Pods-Thimra/Pods-Thimra.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
FEA583158A7C05D8D7C5A9FC /* Pods-Thimra.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Thimra.release.xcconfig"; path = "Target Support Files/Pods-Thimra/Pods-Thimra.release.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@ -640,10 +638,6 @@
|
||||
0061C3496D158807460301A9 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F7763FEFB6BEB1A75D6FBA0A /* Pods-Thimra.debug.xcconfig */,
|
||||
FEA583158A7C05D8D7C5A9FC /* Pods-Thimra.release.xcconfig */,
|
||||
1F666DE0B12C863F26BE5027 /* Pods-MoviaBox.debug.xcconfig */,
|
||||
109EB01BE447EE135493CA38 /* Pods-MoviaBox.release.xcconfig */,
|
||||
0538826A0638D33FEF3A2E38 /* Pods-ThimraTV.debug.xcconfig */,
|
||||
A1174E10BCF2C606F7818792 /* Pods-ThimraTV.release.xcconfig */,
|
||||
);
|
||||
@ -1275,6 +1269,7 @@
|
||||
1BB91CC82E04FD6A00A2C715 /* SPIAPOrderModel.swift */,
|
||||
1BB91CC92E04FD6A00A2C715 /* SPIAPVerifyModel.swift */,
|
||||
1BB91CCA2E04FD6A00A2C715 /* SPWaitRestoreModel.swift */,
|
||||
1B222BCE2E2B80DD002F5A68 /* SPPayTemplateRequest.swift */,
|
||||
);
|
||||
path = SPIAPManager;
|
||||
sourceTree = "<group>";
|
||||
@ -1882,6 +1877,7 @@
|
||||
1BB91D872E04FD6A00A2C715 /* SPAboutUsViewController.swift in Sources */,
|
||||
1BB91D882E04FD6A00A2C715 /* SPDeleteAccountViewController.swift in Sources */,
|
||||
1BB91D892E04FD6A00A2C715 /* SPFeedbackViewController.swift in Sources */,
|
||||
1B222BCF2E2B80DD002F5A68 /* SPPayTemplateRequest.swift in Sources */,
|
||||
1BB91D8A2E04FD6A00A2C715 /* SPLanguageViewController.swift in Sources */,
|
||||
1BB91D8B2E04FD6A00A2C715 /* SPMineViewController.swift in Sources */,
|
||||
1BB91D8C2E04FD6A00A2C715 /* SPSettingsViewController.swift in Sources */,
|
||||
|
@ -23,7 +23,7 @@ class SPVideoAPI: NSObject {
|
||||
var param = SPNetworkParameters(path: "/getVideoDetails")
|
||||
param.method = .get
|
||||
param.parameters = parameters
|
||||
param.isLoding = true
|
||||
param.isLoding = false
|
||||
param.isToast = false
|
||||
|
||||
SPNetwork.request(parameters: param) { (response: SPNetworkResponse<SPVideoDetailModel>) in
|
||||
|
@ -14,15 +14,20 @@ class SPWalletAPI: NSObject {
|
||||
}
|
||||
|
||||
///获取支付模版
|
||||
static func requestPayTemplate(completer: ((_ model: SPPayTemplateModel?) -> Void)?) {
|
||||
static func requestPayTemplate(isLoding: Bool = false, isToast: Bool = true, completer: ((_ model: SPPayTemplateModel?) -> Void)?) {
|
||||
|
||||
var param = SPNetworkParameters(path: "/paySettingsV3")
|
||||
param.method = .get
|
||||
param.isToast = isToast
|
||||
param.isLoding = false
|
||||
|
||||
SPNetwork.request(parameters: param) { (response: SPNetworkResponse<SPPayTemplateModel>) in
|
||||
completer?(response.data)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
///创建内购订单
|
||||
static func requestCreateOrder(payId: String, shortPlayId: String, videoId: String, completer: ((_ orderModel: SPIAPOrderModel?) -> Void)?) {
|
||||
var param = SPNetworkParameters(path: "/createOrder")
|
||||
|
@ -90,7 +90,7 @@ extension SPApi: TargetType {
|
||||
"system-type" : "ios",
|
||||
"idfa" : SPAPPTool.getIdfa(),
|
||||
"model" : UIDevice.sp_machineModelName(),
|
||||
// "security" : "false",
|
||||
"device-gaid" : JXUUID.idfv()
|
||||
]
|
||||
//登录信息
|
||||
dic["authorization"] = userToken
|
||||
|
@ -28,6 +28,8 @@ class SPMineViewController: SPViewController {
|
||||
private var needShowRewardedAd: Bool = false
|
||||
|
||||
weak var vipAlertView: SPVipAlertView?
|
||||
|
||||
private var payTemplateRequest: SPPayTemplateRequest?
|
||||
//MARK: UI 属性
|
||||
private lazy var headerView: SPMineHeaderView = {
|
||||
let view = SPMineHeaderView()
|
||||
@ -101,7 +103,10 @@ extension SPMineViewController {
|
||||
guard SPLoginManager.manager.userInfo?.is_vip != true else { return }
|
||||
guard SPVipAlertView.isShowAlert else { return }
|
||||
|
||||
SPWalletAPI.requestPayTemplate { model in
|
||||
self.payTemplateRequest = SPPayTemplateRequest()
|
||||
self.payTemplateRequest?.requestProducts(isToast: false) { [weak self] model in
|
||||
guard let self = self else { return }
|
||||
|
||||
guard let list = model?.list_sub_vip, list.count > 0 else { return }
|
||||
if !self.isDidAppear { return }
|
||||
if self.vipAlertView != nil { return }
|
||||
|
@ -23,7 +23,9 @@ class SPPlayerDetailViewController: SPPlayerListViewController {
|
||||
var activityId: String?
|
||||
var playHistoryModel: SPShortModel?
|
||||
|
||||
private var detailModel: SPVideoDetailModel?
|
||||
// private var detailModel: SPVideoDetailModel?
|
||||
|
||||
private var detailDataArr: [Any] = []
|
||||
|
||||
///上一次上报播放时长的节点
|
||||
private var lastUploadTime: Int = 0
|
||||
@ -160,6 +162,7 @@ class SPPlayerDetailViewController: SPPlayerListViewController {
|
||||
|
||||
self.pause()
|
||||
let view = SPPlayerDetailRecommandView()
|
||||
view.currentVideoInfo = self.viewModel.currentPlayer?.videoInfo
|
||||
view.clickCloseButton = { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self._handleBack()
|
||||
@ -246,12 +249,16 @@ extension SPPlayerDetailViewController {
|
||||
extension SPPlayerDetailViewController {
|
||||
|
||||
private func onEpisode() {
|
||||
guard detailDataArr.count > 0 else { return }
|
||||
guard let detailModel = detailDataArr[self.viewModel.currentIndexPath.section] as? SPVideoDetailModel else { return }
|
||||
|
||||
|
||||
let view = SPEpisodeView()
|
||||
view.dataArr = detailModel?.episodeList ?? []
|
||||
view.shortModel = detailModel?.shortPlayInfo
|
||||
view.dataArr = detailModel.episodeList ?? []
|
||||
view.shortModel = detailModel.shortPlayInfo
|
||||
view.currentIndex = self.viewModel.currentIndexPath.row
|
||||
view.didSelectedIndex = { [weak self] (index) in
|
||||
self?.scrollToItem(indexPath: IndexPath(row: index, section: 0), animated: false)
|
||||
self?.scrollToItem(indexPath: IndexPath(row: index, section: self?.viewModel.currentIndexPath.section ?? 0), animated: false)
|
||||
}
|
||||
view.present(in: nil)
|
||||
self.episodeView = view
|
||||
@ -320,7 +327,7 @@ extension SPPlayerDetailViewController {
|
||||
}
|
||||
///网络切换通知
|
||||
@objc private func reachabilityDidChangeNotification() {
|
||||
if SPNetworkReachabilityManager.manager.isReachable == true && self.detailModel == nil {
|
||||
if SPNetworkReachabilityManager.manager.isReachable == true && self.detailDataArr.isEmpty {
|
||||
self.requestDetailData()
|
||||
}
|
||||
}
|
||||
@ -344,6 +351,8 @@ extension SPPlayerDetailViewController {
|
||||
//MARK: -------------- SPPlayerListViewControllerDataSource --------------
|
||||
extension SPPlayerDetailViewController: SPPlayerListViewControllerDataSource, SPPlayerListViewControllerDelegate {
|
||||
func sp_playerListViewController(_ viewController: SPPlayerListViewController, _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath, oldCell: UICollectionViewCell) -> UICollectionViewCell {
|
||||
let detailModel = self.detailDataArr[indexPath.section] as? SPVideoDetailModel
|
||||
|
||||
if let cell = oldCell as? SPPlayerDetailCell {
|
||||
cell.shortModel = detailModel?.shortPlayInfo
|
||||
cell.videoInfo = detailModel?.episodeList?[indexPath.row]
|
||||
@ -371,15 +380,40 @@ extension SPPlayerDetailViewController: SPPlayerListViewControllerDataSource, SP
|
||||
}
|
||||
|
||||
func sp_playerListViewController(_ viewController: SPPlayerListViewController, _ collectionView: UICollectionView, numberOfItemsInSection section: Int, oldNumber: Int) -> Int {
|
||||
return detailModel?.episodeList?.count ?? 0
|
||||
if let model = self.detailDataArr[section] as? SPVideoDetailModel {
|
||||
return model.episodeList?.count ?? 0
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
func sp_playerListViewController(_ viewController: SPPlayerListViewController, didChangeIndexPathForVisible indexPath: IndexPath) {
|
||||
self.episodeView?.currentIndex = indexPath.row
|
||||
let videoInfo = detailModel?.episodeList?[indexPath.row]
|
||||
// self.episodeView?.currentIndex = indexPath.row
|
||||
|
||||
if let detailModel = self.detailDataArr[indexPath.section] as? SPVideoDetailModel {
|
||||
let videoInfo = detailModel.episodeList?[indexPath.row]
|
||||
|
||||
titleLabel.text = detailModel.shortPlayInfo?.name
|
||||
episodeLabel.text = "\(videoInfo?.episode ?? "0")/\(detailModel.shortPlayInfo?.episode_total ?? 0)"
|
||||
} else {
|
||||
titleLabel.text = ""
|
||||
episodeLabel.text = ""
|
||||
}
|
||||
}
|
||||
|
||||
func sp_numberOfSections(in viewController: SPPlayerListViewController) -> Int {
|
||||
return self.detailDataArr.count
|
||||
}
|
||||
|
||||
func sp_shouldAutoScrollNextEpisode(_ viewController: SPPlayerListViewController) -> Bool {
|
||||
if episodeView != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func sp_playerViewControllerLoadMoreData(playerViewController: SPPlayerListViewController) {
|
||||
|
||||
titleLabel.text = detailModel?.shortPlayInfo?.name
|
||||
episodeLabel.text = "\(videoInfo?.episode ?? "0")/\(detailModel?.shortPlayInfo?.episode_total ?? 0)"
|
||||
}
|
||||
}
|
||||
|
||||
@ -402,7 +436,10 @@ extension SPPlayerDetailViewController {
|
||||
recommandTimer = nil
|
||||
recommandTimer = Timer.scheduledTimer(timeInterval: 6, target: YYWeakProxy(target: self), selector: #selector(handleRecommandTimer), userInfo: nil, repeats: false)
|
||||
|
||||
|
||||
SPHUD.show(containerView: self.view)
|
||||
SPVideoAPI.requestVideoDetail(videoId: videoId, shortPlayId: shortPlayId, activityId: activityId) { [weak self] model, code, msg in
|
||||
SPHUD.dismiss()
|
||||
guard let self = self else { return }
|
||||
if code == 10014 {
|
||||
self.navigationController?.popViewController(animated: true)
|
||||
@ -410,16 +447,19 @@ extension SPPlayerDetailViewController {
|
||||
}
|
||||
guard let model = model else { return }
|
||||
|
||||
self.detailModel = model
|
||||
self.detailDataArr.removeAll()
|
||||
self.detailDataArr.append(model)
|
||||
|
||||
|
||||
self.reloadData { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
||||
if let indexPath = indexPath, indexPath.row < (model.episodeList?.count ?? 0) {
|
||||
self.scrollToItem(indexPath: indexPath, animated: false)
|
||||
|
||||
} else if let videoInfo = self.detailModel?.video_info {
|
||||
} else if let videoInfo = model.video_info {
|
||||
var row: Int?
|
||||
self.detailModel?.episodeList?.enumerated().forEach({
|
||||
model.episodeList?.enumerated().forEach({
|
||||
if $1.id == videoInfo.id {
|
||||
row = $0
|
||||
}
|
||||
|
@ -17,15 +17,14 @@ import AVKit
|
||||
@objc optional func sp_playerViewControllerShouldLoadMoreData(playerViewController: SPPlayerListViewController) -> Bool
|
||||
///加载更多数据
|
||||
@objc optional func sp_playerViewControllerLoadMoreData(playerViewController: SPPlayerListViewController)
|
||||
///向上加载更多数据
|
||||
@objc optional func sp_playerViewControllerLoadUpMoreData(playerViewController: SPPlayerListViewController)
|
||||
|
||||
///当前展示的发生变化
|
||||
@objc optional func sp_playerListViewController(_ viewController: SPPlayerListViewController, didChangeIndexPathForVisible indexPath: IndexPath)
|
||||
|
||||
// @objc optional func sp_playerListViewController(_ viewController: SPPlayerListViewController, didScrollFromIndex fromIndex: Int, toIndex: Int)
|
||||
///新页面展示完成
|
||||
// @objc optional func yd_playerViewController(playerListViewController: BCListPlayerViewController, didShowPlayerPage playerViewController: YDBasePlayerViewController)
|
||||
///即将自动滑至下一级
|
||||
@objc optional func sp_shouldAutoScrollNextEpisode(_ viewController: SPPlayerListViewController) -> Bool
|
||||
|
||||
|
||||
}
|
||||
|
||||
@objc protocol SPPlayerListViewControllerDataSource {
|
||||
@ -35,7 +34,7 @@ import AVKit
|
||||
|
||||
func sp_playerListViewController(_ viewController: SPPlayerListViewController, _ collectionView: UICollectionView, numberOfItemsInSection section: Int, oldNumber: Int) -> Int
|
||||
|
||||
|
||||
@objc optional func sp_numberOfSections(in viewController: SPPlayerListViewController) -> Int
|
||||
|
||||
}
|
||||
|
||||
@ -59,9 +58,6 @@ class SPPlayerListViewController: SPViewController {
|
||||
///自动下一级
|
||||
var autoNextEpisode = false
|
||||
|
||||
///是否为首次播放
|
||||
private(set) var isFirstPlay = true
|
||||
|
||||
private(set) var viewModel = SPPlayerListViewModel()
|
||||
|
||||
|
||||
@ -173,20 +169,10 @@ class SPPlayerListViewController: SPViewController {
|
||||
|
||||
self.viewModel.isPlaying = true
|
||||
|
||||
if getDataCount() - self.viewModel.currentIndexPath.row <= 2 {
|
||||
if (self.collectionView.contentSize.height - self.collectionView.contentOffset.y) / self.contentSize.height <= 3 {
|
||||
self.loadMoreData()
|
||||
}
|
||||
|
||||
if isFirstPlay {
|
||||
isFirstPlay = false
|
||||
let offset = self.collectionView.contentOffset.y + 0.2
|
||||
self.collectionView.setContentOffset(CGPoint(x: 0, y: offset), animated: false)
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
let offset = self.collectionView.contentOffset.y
|
||||
self.collectionView.setContentOffset(CGPoint(x: 0, y: floor(offset)), animated: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func pause() {
|
||||
@ -210,7 +196,8 @@ class SPPlayerListViewController: SPViewController {
|
||||
}
|
||||
|
||||
func getDataCount() -> Int {
|
||||
return self.collectionView(self.collectionView, numberOfItemsInSection: 0)
|
||||
return Int(self.collectionView.contentSize.height / self.contentSize.height)
|
||||
// return self.collectionView(self.collectionView, numberOfItemsInSection: 0)
|
||||
}
|
||||
|
||||
func scrollToItem(indexPath: IndexPath, animated: Bool = true, completer: (() -> Void)? = nil) {
|
||||
@ -232,10 +219,19 @@ class SPPlayerListViewController: SPViewController {
|
||||
|
||||
///当前播放完成 子类可重写
|
||||
func currentPlayFinish() {
|
||||
if self.autoNextEpisode {
|
||||
scrollToNextEpisode()
|
||||
}
|
||||
self.viewModel.currentPlayer?.videoInfo?.play_seconds = 0
|
||||
|
||||
var autoNextEpisode = self.autoNextEpisode
|
||||
|
||||
if let result = self.delegate?.sp_shouldAutoScrollNextEpisode?(self) {
|
||||
autoNextEpisode = result
|
||||
}
|
||||
|
||||
if autoNextEpisode {
|
||||
scrollToNextEpisode()
|
||||
} else {
|
||||
viewModel.currentPlayer?.replay()
|
||||
}
|
||||
}
|
||||
|
||||
///当前播放进度变更 子类可重写
|
||||
@ -356,6 +352,10 @@ extension SPPlayerListViewController: UICollectionViewDelegate, UICollectionView
|
||||
}
|
||||
}
|
||||
|
||||
func numberOfSections(in collectionView: UICollectionView) -> Int {
|
||||
return self.dataSource?.sp_numberOfSections?(in: self) ?? 1
|
||||
}
|
||||
|
||||
//滑动停止
|
||||
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
||||
scrollDidEnd(scrollView)
|
||||
@ -415,13 +415,6 @@ extension SPPlayerListViewController {
|
||||
}
|
||||
}
|
||||
|
||||
private func loadUpMoreData() {
|
||||
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
|
||||
// guard let self = self else { return }
|
||||
// }
|
||||
self.delegate?.sp_playerViewControllerLoadUpMoreData?(playerViewController: self)
|
||||
}
|
||||
|
||||
private func didChangeIndexPathForVisible() {
|
||||
self.delegate?.sp_playerListViewController?(self, didChangeIndexPathForVisible: self.viewModel.currentIndexPath)
|
||||
}
|
||||
|
@ -24,6 +24,8 @@ class SPPlayBuyView: HWPanModalContentView {
|
||||
}
|
||||
}
|
||||
|
||||
private var payTemplateRequest: SPPayTemplateRequest?
|
||||
|
||||
//MARK: UI属性
|
||||
private lazy var bgView: UIImageView = {
|
||||
let view = UIImageView(image: UIImage(named: "buy_bg_image_01"))
|
||||
@ -246,27 +248,28 @@ extension SPPlayBuyView {
|
||||
|
||||
///请求支付模版
|
||||
private func requestPayTemplate() {
|
||||
SPWalletAPI.requestPayTemplate { [weak self] templateModel in
|
||||
|
||||
self.payTemplateRequest = SPPayTemplateRequest()
|
||||
self.payTemplateRequest?.requestProducts { [weak self] model in
|
||||
guard let self = self else { return }
|
||||
self.stackView.removeAllArrangedSubview()
|
||||
|
||||
if let sort = templateModel?.sort, sort.count > 0 {
|
||||
if let sort = model?.sort, sort.count > 0 {
|
||||
sort.forEach {
|
||||
if $0 == .vip {
|
||||
self.addMemberView(list: templateModel?.list_sub_vip)
|
||||
self.addMemberView(list: model?.list_sub_vip)
|
||||
} else if $0 == .coin {
|
||||
self.addCoinView(list: templateModel?.list_coins)
|
||||
self.addCoinView(list: model?.list_coins)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.addMemberView(list: templateModel?.list_sub_vip)
|
||||
self.addCoinView(list: templateModel?.list_coins)
|
||||
self.addMemberView(list: model?.list_sub_vip)
|
||||
self.addCoinView(list: model?.list_coins)
|
||||
}
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
self.panModalSetNeedsLayoutUpdate()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,14 @@ class SPPlayerDetailRecommandView: HWPanModalContentView {
|
||||
var clickCloseButton: (() -> Void)?
|
||||
var clickPlayButton: ((_ model: SPShortModel) -> Void)?
|
||||
|
||||
var currentVideoInfo: SPVideoInfoModel? {
|
||||
didSet {
|
||||
if SPLoginManager.manager.userInfo?.user_level == .ad {
|
||||
bannerAd.videoInfo = currentVideoInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private var _currentCell: SPPlayerDetailRecommandCell?
|
||||
private var currentCell: SPPlayerDetailRecommandCell? {
|
||||
|
@ -9,6 +9,8 @@ import UIKit
|
||||
|
||||
class SPStoreViewController: SPViewController {
|
||||
|
||||
private var payTemplateRequest: SPPayTemplateRequest?
|
||||
|
||||
//MARK: UI属性
|
||||
private lazy var scrollView: SPScrollView = {
|
||||
let scrollView = SPScrollView()
|
||||
@ -184,21 +186,23 @@ extension SPStoreViewController {
|
||||
|
||||
///请求支付模版
|
||||
private func requestPayTemplate() {
|
||||
SPWalletAPI.requestPayTemplate { [weak self] templateModel in
|
||||
|
||||
self.payTemplateRequest = SPPayTemplateRequest()
|
||||
self.payTemplateRequest?.requestProducts { [weak self] model in
|
||||
guard let self = self else { return }
|
||||
self.stackView.removeAllArrangedSubview()
|
||||
|
||||
if let sort = templateModel?.sort, sort.count > 0 {
|
||||
if let sort = model?.sort, sort.count > 0 {
|
||||
sort.forEach {
|
||||
if $0 == .vip {
|
||||
self.addMemberView(list: templateModel?.list_sub_vip)
|
||||
self.addMemberView(list: model?.list_sub_vip)
|
||||
} else if $0 == .coin {
|
||||
self.addCoinView(list: templateModel?.list_coins)
|
||||
self.addCoinView(list: model?.list_coins)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.addMemberView(list: templateModel?.list_sub_vip)
|
||||
self.addCoinView(list: templateModel?.list_coins)
|
||||
self.addMemberView(list: model?.list_sub_vip)
|
||||
self.addCoinView(list: model?.list_coins)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,9 @@ class SPApplovinBannerAd: NSObject, SPBannerAd {
|
||||
|
||||
var delegate: (any SPBannerAdDelegate)?
|
||||
|
||||
///是否加载过
|
||||
private var isLoaded = false
|
||||
|
||||
private(set) lazy var _adView: MAAdView = {
|
||||
let view = MAAdView(adUnitIdentifier: adUnitID)
|
||||
view.frame = .init(x: 0, y: 0, width: size.width, height: size.height)
|
||||
@ -53,12 +56,18 @@ extension SPApplovinBannerAd: MAAdViewAdDelegate {
|
||||
}
|
||||
|
||||
func didLoad(_ ad: MAAd) {
|
||||
self.delegate?.bannerAdDidLoadFinish?(bannerAd: self)
|
||||
if !isLoaded {
|
||||
isLoaded = true
|
||||
self.delegate?.bannerAdDidLoadFinish?(bannerAd: self)
|
||||
}
|
||||
}
|
||||
|
||||
func didFailToLoadAd(forAdUnitIdentifier adUnitIdentifier: String, withError error: MAError) {
|
||||
let nsError = NSError(domain: error.message, code: error.code.rawValue)
|
||||
self.delegate?.bannerAd?(bannerAd: self, didLoadFail: nsError)
|
||||
if !isLoaded {
|
||||
isLoaded = true
|
||||
let nsError = NSError(domain: error.message, code: error.code.rawValue)
|
||||
self.delegate?.bannerAd?(bannerAd: self, didLoadFail: nsError)
|
||||
}
|
||||
}
|
||||
|
||||
func didDisplay(_ ad: MAAd) {
|
||||
|
@ -57,6 +57,8 @@ class SPBannerAdManager: NSObject {
|
||||
|
||||
weak var delegate: SPBannerAdManagerDelegate?
|
||||
|
||||
var videoInfo: SPVideoInfoModel?
|
||||
|
||||
private lazy var bannerAd: SPBannerAd = {
|
||||
let ad = SPAdmobBannerAd()
|
||||
ad.delegate = self
|
||||
@ -122,6 +124,8 @@ extension SPBannerAdManager {
|
||||
model.ad_platform_key = SPAdPlatformKey(rawValue: bannerAd.adPlatformKey)
|
||||
model.error_msg = errorMsg
|
||||
model.scene = .banner
|
||||
model.short_play_id = self.videoInfo?.short_play_id
|
||||
model.short_play_video_id = self.videoInfo?.short_play_video_id
|
||||
|
||||
SPStatAPI.requestStatAd(model: model)
|
||||
}
|
||||
|
@ -12,9 +12,9 @@ class SPHUD: NSObject {
|
||||
SVProgressHUD.setDefaultMaskType(.clear)
|
||||
}
|
||||
|
||||
static func show(status: String? = nil) {
|
||||
static func show(status: String? = nil, containerView: UIView? = nil) {
|
||||
SVProgressHUD.setContainerView(containerView)
|
||||
SVProgressHUD.setDefaultMaskType(.clear)
|
||||
// SVProgressHUD.show()
|
||||
SVProgressHUD.show(withStatus: status)
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import StoreKit
|
||||
|
||||
class SPIAPManager: NSObject {
|
||||
typealias CompletionHandler = ((_ finish: Bool) -> Void)
|
||||
@ -18,6 +19,8 @@ class SPIAPManager: NSObject {
|
||||
///成功回调
|
||||
private var completionHandler: CompletionHandler?
|
||||
|
||||
private var productListHandler: ((_ products: [SKProduct]) -> Void)?
|
||||
|
||||
private lazy var iapManager: JXIAPManager = {
|
||||
let manager = JXIAPManager()
|
||||
manager.delegate = self
|
||||
@ -119,6 +122,17 @@ class SPIAPManager: NSObject {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func requestProductList(productIdArr: [String], completer: ((_ products: [SKProduct]) -> Void)?) {
|
||||
self.productListHandler = completer
|
||||
self.iapManager.requestProductList(productIdArr: productIdArr)
|
||||
}
|
||||
|
||||
|
||||
func getProductId(templateId: String?) -> String? {
|
||||
guard let templateId = templateId else { return nil }
|
||||
return SPIAPManager.IAPPrefix + templateId
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: -------------- JXIAPManagerDelegate --------------
|
||||
@ -182,6 +196,13 @@ extension SPIAPManager: JXIAPManagerDelegate {
|
||||
self.completionHandler?(false)
|
||||
}
|
||||
|
||||
func jx_iapPayGotProducts(products: [SKProduct]) {
|
||||
|
||||
self.productListHandler?(products)
|
||||
|
||||
self.productListHandler = nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension SPIAPManager {
|
||||
|
101
ThimraTV/Libs/SPIAPManager/SPPayTemplateRequest.swift
Normal file
101
ThimraTV/Libs/SPIAPManager/SPPayTemplateRequest.swift
Normal file
@ -0,0 +1,101 @@
|
||||
//
|
||||
// SPPayTemplateRequest.swift
|
||||
// ThimraTV
|
||||
//
|
||||
// Created by 长沙佳儿 on 2025/7/19.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import StoreKit
|
||||
|
||||
class SPPayTemplateRequest: NSObject {
|
||||
|
||||
private var oldTemplateModel: SPPayTemplateModel?
|
||||
|
||||
private var completerBlock: ((_ model: SPPayTemplateModel?) -> Void)?
|
||||
|
||||
private var isLoding = false
|
||||
private var isToast = false
|
||||
|
||||
|
||||
func requestProducts(isLoding: Bool = false, isToast: Bool = true, completer: ((_ model: SPPayTemplateModel?) -> Void)?) {
|
||||
self.completerBlock = completer
|
||||
self.isLoding = isLoding
|
||||
self.isToast = isToast
|
||||
|
||||
if isLoding {
|
||||
SPHUD.show()
|
||||
}
|
||||
|
||||
SPWalletAPI.requestPayTemplate(isToast: isToast) { [weak self] model in
|
||||
guard let self = self else { return }
|
||||
guard let model = model else {
|
||||
if isLoding {
|
||||
SPHUD.dismiss()
|
||||
}
|
||||
self.completerBlock?(nil)
|
||||
return
|
||||
}
|
||||
self.oldTemplateModel = model
|
||||
|
||||
var productIdArr: [String] = []
|
||||
model.list_sub_vip?.forEach { item in
|
||||
productIdArr.append(SPIAPManager.manager.getProductId(templateId: item.ios_template_id) ?? "")
|
||||
}
|
||||
model.list_coins?.forEach { item in
|
||||
productIdArr.append(SPIAPManager.manager.getProductId(templateId: item.ios_template_id) ?? "")
|
||||
}
|
||||
|
||||
let set = Set(productIdArr)
|
||||
let productsRequest = SKProductsRequest(productIdentifiers: set)
|
||||
productsRequest.delegate = self
|
||||
productsRequest.start()
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: -------------- SKProductsRequestDelegate --------------
|
||||
extension SPPayTemplateRequest: SKProductsRequestDelegate {
|
||||
|
||||
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
|
||||
if isLoding {
|
||||
SPHUD.dismiss()
|
||||
}
|
||||
|
||||
guard let templateModel = self.oldTemplateModel else { return }
|
||||
let products = response.products
|
||||
|
||||
var newCoinList: [SPPayTemplateItem] = []
|
||||
var newVipList: [SPPayTemplateItem] = []
|
||||
|
||||
templateModel.list_coins?.forEach { item in
|
||||
let productId = SPIAPManager.manager.getProductId(templateId: item.ios_template_id) ?? ""
|
||||
for product in products {
|
||||
if productId == product.productIdentifier {
|
||||
item.price = product.price.stringValue
|
||||
item.currency = product.priceLocale.currencySymbol
|
||||
newCoinList.append(item)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
templateModel.list_sub_vip?.forEach { item in
|
||||
let productId = SPIAPManager.manager.getProductId(templateId: item.ios_template_id) ?? ""
|
||||
for product in products {
|
||||
if productId == product.productIdentifier {
|
||||
item.price = product.price.stringValue
|
||||
item.currency = product.priceLocale.currencySymbol
|
||||
newVipList.append(item)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
templateModel.list_coins = newCoinList
|
||||
templateModel.list_sub_vip = newVipList
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.completerBlock?(templateModel)
|
||||
}
|
||||
}
|
||||
}
|
109
ThimraTV/Thirdparty/JXIAPManager/JXIAPManager.swift
vendored
109
ThimraTV/Thirdparty/JXIAPManager/JXIAPManager.swift
vendored
@ -10,7 +10,7 @@ import StoreKit
|
||||
|
||||
@objc protocol JXIAPManagerDelegate {
|
||||
/// 获取到可购买商品列表
|
||||
@objc optional func jx_iapPayGotProducts(productIds: [String])
|
||||
@objc optional func jx_iapPayGotProducts(products: [SKProduct])
|
||||
/// 购买成功
|
||||
@objc optional func jx_iapPaySuccess(productId: String, receipt: String, transactionIdentifier: String?)
|
||||
/// 购买失败
|
||||
@ -34,19 +34,29 @@ import StoreKit
|
||||
case cancelled
|
||||
///没有商品
|
||||
case noProduct
|
||||
|
||||
}
|
||||
|
||||
class JXIAPManager: NSObject {
|
||||
|
||||
enum OperationType {
|
||||
case idle
|
||||
case buy
|
||||
case request
|
||||
}
|
||||
|
||||
static let manager: JXIAPManager = JXIAPManager()
|
||||
|
||||
weak var delegate: JXIAPManagerDelegate?
|
||||
|
||||
///当前操作状态
|
||||
private var operationType = OperationType.idle
|
||||
|
||||
private var payment: SKPayment?
|
||||
|
||||
private var product: SKProduct?
|
||||
private var productId: String?
|
||||
|
||||
private var discount: SKPaymentDiscount?
|
||||
private var orderId: String?
|
||||
private var applicationUsername: String? {
|
||||
get {
|
||||
@ -80,10 +90,47 @@ class JXIAPManager: NSObject {
|
||||
SKPaymentQueue.default().add(self)
|
||||
}
|
||||
|
||||
func start(productId: String, orderId: String) {
|
||||
func showCodeRedemption() {
|
||||
SKPaymentQueue.default().presentCodeRedemptionSheet()
|
||||
}
|
||||
|
||||
func requestProductList(productIdArr: [String]) {
|
||||
// guard self.operationType == .idle else { return }
|
||||
self.operationType = .request
|
||||
|
||||
let set = Set(productIdArr)
|
||||
let productsRequest = SKProductsRequest(productIdentifiers: set)
|
||||
productsRequest.delegate = self
|
||||
productsRequest.start()
|
||||
}
|
||||
|
||||
// ///发起购买
|
||||
// func buyProduct(product: SKProduct, orderId: String, discount: SKPaymentDiscount? = nil) {
|
||||
// guard self.operationType == .idle else { return }
|
||||
// self.operationType = .buy
|
||||
//
|
||||
// self.product = product
|
||||
// self.orderId = orderId
|
||||
//
|
||||
// // 要购买商品,开个小票
|
||||
// let payment = SKMutablePayment(product: product)
|
||||
// payment.applicationUsername = applicationUsername
|
||||
// if let discount = discount {
|
||||
// payment.paymentDiscount = discount
|
||||
// }
|
||||
//
|
||||
// // 去收银台排队,准备购买
|
||||
// SKPaymentQueue.default().add(payment)
|
||||
// }
|
||||
|
||||
func start(productId: String, orderId: String, discount: SKPaymentDiscount? = nil) {
|
||||
guard self.operationType == .idle else { return }
|
||||
self.operationType = .buy
|
||||
|
||||
self.product = nil
|
||||
self.productId = productId
|
||||
self.orderId = orderId
|
||||
self.discount = discount
|
||||
|
||||
let set = Set([productId])
|
||||
let productsRequest = SKProductsRequest(productIdentifiers: set)
|
||||
@ -98,9 +145,13 @@ class JXIAPManager: NSObject {
|
||||
// 要购买商品,开个小票
|
||||
let payment = SKMutablePayment(product: product)
|
||||
payment.applicationUsername = applicationUsername
|
||||
spLog(message: payment.applicationUsername)
|
||||
if let discount = self.discount {
|
||||
payment.paymentDiscount = discount
|
||||
self.discount = nil
|
||||
}
|
||||
|
||||
self.payment = payment
|
||||
|
||||
// 去收银台排队,准备购买
|
||||
SKPaymentQueue.default().add(payment)
|
||||
}
|
||||
@ -110,18 +161,27 @@ class JXIAPManager: NSObject {
|
||||
//MARK: -------------- SKProductsRequestDelegate --------------
|
||||
extension JXIAPManager: SKProductsRequestDelegate {
|
||||
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
|
||||
guard let product = response.products.first else {
|
||||
if self.operationType == .request {
|
||||
DispatchQueue.main.async {
|
||||
if let productId = self.productId {
|
||||
self.productId = nil
|
||||
self.delegate?.jx_iapPayFailed?(productId: productId, code: .noProduct)
|
||||
}
|
||||
self.delegate?.jx_iapPayGotProducts?(products: response.products)
|
||||
}
|
||||
return
|
||||
}
|
||||
self.product = product
|
||||
self.operationType = .idle
|
||||
} else if self.operationType == .buy {
|
||||
|
||||
guard let product = response.products.first else {
|
||||
DispatchQueue.main.async {
|
||||
if let productId = self.productId {
|
||||
self.productId = nil
|
||||
self.delegate?.jx_iapPayFailed?(productId: productId, code: .noProduct)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
self.product = product
|
||||
|
||||
self.buyProduct()
|
||||
}
|
||||
|
||||
self.buyProduct()
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,7 +191,6 @@ extension JXIAPManager: SKPaymentTransactionObserver {
|
||||
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
|
||||
|
||||
for transaction in transactions {
|
||||
spLog(message: "transactionState = \(transaction.transactionState)")
|
||||
switch transaction.transactionState {
|
||||
case .purchased:
|
||||
DispatchQueue.main.async {
|
||||
@ -171,33 +230,21 @@ extension JXIAPManager: SKPaymentTransactionObserver {
|
||||
|
||||
extension JXIAPManager {
|
||||
private func completeTransaction(transaction: SKPaymentTransaction) {
|
||||
//自动续费
|
||||
// if let _ = transaction.original, transaction.payment.applicationUsername == nil {
|
||||
// return
|
||||
// }
|
||||
//重新开通自动续费
|
||||
// if let _ = transaction.original, transaction.payment.applicationUsername != nil {
|
||||
// self.delegate?.jx_iapPayFailed?(productId: productId, code: .unknown)
|
||||
// return
|
||||
// }
|
||||
|
||||
spLog(message: "transactionDate = \(String(describing: transaction.transactionDate))")
|
||||
spLog(message: "nowDate = \(Date())")
|
||||
spLog(message: "productIdentifier = \(transaction.payment.productIdentifier)")
|
||||
|
||||
guard let productId = self.productId, productId == transaction.payment.productIdentifier else { return }
|
||||
self.productId = nil
|
||||
|
||||
guard let receiptURL = Bundle.main.appStoreReceiptURL else { return }
|
||||
let receiptData = NSData(contentsOf: receiptURL)
|
||||
guard let encodeStr = receiptData?.base64EncodedString(options: .endLineWithLineFeed) else { return }
|
||||
guard let transactionIdentifier = transaction.transactionIdentifier else { return }
|
||||
|
||||
|
||||
guard let productId = self.productId, productId == transaction.payment.productIdentifier else { return }
|
||||
self.operationType = .idle
|
||||
self.productId = nil
|
||||
self.delegate?.jx_iapPaySuccess?(productId: productId, receipt: encodeStr, transactionIdentifier: transactionIdentifier)
|
||||
|
||||
}
|
||||
|
||||
private func failedTransaction(transaction: SKPaymentTransaction) {
|
||||
self.operationType = .idle
|
||||
let error = transaction.error as? SKError
|
||||
guard let productId = self.productId else { return }
|
||||
self.productId = nil
|
||||
|
2
ThimraTV/Thirdparty/JXUUID/JXUUID.h
vendored
2
ThimraTV/Thirdparty/JXUUID/JXUUID.h
vendored
@ -15,6 +15,6 @@
|
||||
/**
|
||||
重新安装app后,会发生变化
|
||||
*/
|
||||
+ (nonnull NSString *)systemUUID;
|
||||
+ (nonnull NSString *)idfv;
|
||||
|
||||
@end
|
||||
|
2
ThimraTV/Thirdparty/JXUUID/JXUUID.m
vendored
2
ThimraTV/Thirdparty/JXUUID/JXUUID.m
vendored
@ -39,7 +39,7 @@ static NSString *const uuidKey = @"com.JXUUID";
|
||||
return idfa;
|
||||
}
|
||||
|
||||
+ (nonnull NSString *)systemUUID
|
||||
+ (nonnull NSString *)idfv
|
||||
{
|
||||
return [UIDevice currentDevice].identifierForVendor.UUIDString;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user