TSPurchaseVC.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. //
  2. // TSPurchaseVC.swift
  3. // TSLiveWallpaper
  4. //
  5. // Created by 100Years on 2025/1/14.
  6. //
  7. import Combine
  8. import SwiftUI
  9. import SwiftUIX
  10. class PurchaseViewModel : ObservableObject{
  11. @Published var selectedType: PremiumPeriod = .year
  12. /// 订阅publisher
  13. let buyPublisher = PassthroughSubject<Bool,Never>()
  14. /// 隐私
  15. let privacyPublisher = PassthroughSubject<Bool, Never>()
  16. /// term
  17. let termPublisher = PassthroughSubject<Bool, Never>()
  18. /// restore
  19. let restorePublisher = PassthroughSubject<Bool, Never>()
  20. }
  21. class TSPurchaseVC: TSBaseVC {
  22. var closePageBlock:(()->Void)?
  23. var viewModel: PurchaseViewModel = .init()
  24. var cancellabel: [AnyCancellable] = []
  25. var buyPeriod:PremiumPeriod = .year
  26. lazy var purchaseManager: PurchaseManager = {
  27. let purchaseManager = PurchaseManager.default
  28. return purchaseManager
  29. }()
  30. var isHandlePurchaseStateChanged = true //是否处理购买状态变化
  31. func createImageScroll(imageName:String,direction:ImagesAnimateScrollView.`Direction`)->ImagesAnimateScrollView{
  32. let imageScroll1: ImagesAnimateScrollView = ImagesAnimateScrollView()
  33. imageScroll1.direction = direction
  34. imageScroll1.animationImageName = imageName
  35. imageScroll1.transform = CGAffineTransform(rotationAngle: CGFloat.pi/12)
  36. return imageScroll1
  37. }
  38. lazy var imageComparisonView: TYCycleImageComparisonView = {
  39. let imageComparisonView = TYCycleImageComparisonView()
  40. imageComparisonView.frame = CGRect(x: 0, y: 0, width: k_ScreenWidth, height: 532*kDesignScale)
  41. imageComparisonView.itemModelArray = [
  42. TSImageComparisonModel(oldImage: UIImage(named: "image_comparison_old_0")!, newImage: UIImage(named: "image_comparison_new_0")!),
  43. TSImageComparisonModel(oldImage: UIImage(named: "image_comparison_old_1")!, newImage: UIImage(named: "image_comparison_new_1")!),
  44. TSImageComparisonModel(oldImage: UIImage(named: "image_comparison_old_2")!, newImage: UIImage(named: "image_comparison_new_2")!),
  45. TSImageComparisonModel(oldImage: UIImage(named: "image_comparison_old_3")!, newImage: UIImage(named: "image_comparison_new_3")!),
  46. TSImageComparisonModel(oldImage: UIImage(named: "image_comparison_old_4")!, newImage: UIImage(named: "image_comparison_new_4")!)
  47. ]
  48. return imageComparisonView
  49. }()
  50. lazy var bgView: UIView = {
  51. let bgView = UIView()
  52. bgView.backgroundColor = .clear
  53. let imageView = UIImageView.createImageView(imageName: "purchase_bj",contentMode: .scaleAspectFill)
  54. bgView.addSubview(imageView)
  55. imageView.snp.makeConstraints { make in
  56. make.edges.equalToSuperview()
  57. }
  58. return bgView
  59. }()
  60. lazy var hostVc: UIHostingController<PurchaseView> = {
  61. //增加月付费
  62. // if PurchaseManager.default.vipType == .none {
  63. // viewModel.selectedType = .month
  64. // }
  65. if PurchaseManager.default.vipType == .none {
  66. viewModel.selectedType = .week(.week)
  67. }
  68. let vc = UIHostingController(rootView: PurchaseView(viewModel: viewModel))
  69. vc.view.backgroundColor = .clear
  70. return vc
  71. }()
  72. override func createView() {
  73. addNormalNavBarView()
  74. _ = setNavigationItem("", imageName: "close_gray", direction: .left, action: #selector(closePage))
  75. view.insertSubview(bgView, at: 0)
  76. bgView.snp.makeConstraints { make in
  77. make.leading.trailing.bottom.top.equalToSuperview()
  78. }
  79. bgView.insertSubview(imageComparisonView, at: 0)
  80. // setViewBgImageNamed(named: "purchase_bj")
  81. contentView.addSubview(hostVc.view)
  82. hostVc.view.snp.makeConstraints { make in
  83. make.leading.trailing.bottom.top.equalToSuperview()
  84. }
  85. }
  86. @objc func closePage(){
  87. TSToastShared.hideLoading()
  88. closePageBlock?()
  89. self.dismiss(animated: true)
  90. kPurchaseCountDownTime.isNeedCheck = true
  91. NotificationCenter.default.post(name: .kCloseTSPurchaseVC, object: nil)
  92. }
  93. override func dealThings() {
  94. //周会员和月会员不自动处理变化,必须点击购买后才处理
  95. let vipType = purchaseManager.vipType
  96. if vipType.isWeekType || vipType == .month {
  97. isHandlePurchaseStateChanged = false
  98. }
  99. addNotifaction()
  100. onPurchaseStateChanged()
  101. NotificationCenter.default.addObserver(forName: .kPurchasePrepared, object: nil, queue: OperationQueue.main) { [weak self] _ in
  102. guard let self = self else { return }
  103. viewModel.selectedType = viewModel.selectedType
  104. }
  105. }
  106. func addNotifaction() {
  107. viewModel.buyPublisher.receive(on: DispatchQueue.main).sink { [weak self] _ in
  108. guard let self = self else {
  109. return
  110. }
  111. isHandlePurchaseStateChanged = true
  112. PurchaseManager.default.pay(for: self.viewModel.selectedType)
  113. }.store(in: &cancellabel)
  114. viewModel.privacyPublisher.receive(on: DispatchQueue.main).sink { [weak self] _ in
  115. guard let self = self else {
  116. return
  117. }
  118. let vc = TSBusinessWebVC(urlType: .privacy)
  119. vc.hidesBottomBarWhenPushed = true
  120. kPresentModalVC(target: self, modelVC: vc)
  121. }.store(in: &cancellabel)
  122. viewModel.termPublisher.receive(on: DispatchQueue.main).sink { [weak self] _ in
  123. guard let self = self else {
  124. return
  125. }
  126. let vc = TSBusinessWebVC(urlType: .terms)
  127. vc.hidesBottomBarWhenPushed = true
  128. kPresentModalVC(target: self, modelVC: vc)
  129. }.store(in: &cancellabel)
  130. viewModel.restorePublisher.receive(on: DispatchQueue.main).sink { [weak self] _ in
  131. guard let self = self else { return }
  132. restorePremium()
  133. }.store(in: &cancellabel)
  134. }
  135. func restorePremium(){
  136. isHandlePurchaseStateChanged = true
  137. PurchaseManager.default.restorePremium()
  138. }
  139. func onPurchaseStateChanged(){
  140. purchaseManager.onPurchaseStateChanged = { [weak self] manager,state,object in
  141. guard let self = self else { return }
  142. if isHandlePurchaseStateChanged == false {
  143. debugPrint("purchaseManager.onPurchaseStateChanged 不处理")
  144. return
  145. }
  146. DispatchQueue.main.async {
  147. switch state {
  148. case .none:
  149. break
  150. case .loading:
  151. TSToastShared.showLoading(text: "Getting price".localized,containerView: self.view)
  152. case .loadSuccess:
  153. TSToastShared.hideLoading()
  154. case .loadFail:
  155. TSToastShared.hideLoading()
  156. let message = "Failed to get the price, will automatically retry in 5 seconds".localized
  157. TSToastShared.showToast(text: message)
  158. DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
  159. PurchaseManager.default.requestProducts()
  160. }
  161. case .paying:
  162. TSToastShared.showLoading(text: "Purchasing now".localized,containerView: self.view)
  163. case .paySuccess:
  164. TSToastShared.hideLoading()
  165. var loadingText = "Finish".localized
  166. if manager.isVip {
  167. loadingText = manager.vipType == .year ? "Congratulations on being VIP of the Year!".localized : "Congratulation you have become VIP".localized
  168. }
  169. TSToastShared.showToast(text:loadingText)
  170. if manager.isVip {
  171. DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
  172. self.closePage()
  173. }
  174. }
  175. case .payFail:
  176. TSToastShared.hideLoading()
  177. if let str = object as? String {
  178. TSToastShared.showToast(text: str)
  179. }
  180. case .restoreing:
  181. TSToastShared.showLoading(text: "Restoring now".localized,containerView: self.view)
  182. case .restoreSuccess:
  183. TSToastShared.hideLoading()
  184. // let loadingText = manager.isVip ? "Congratulation you have become VIP".localized : "Couldn't Restore Subscription".localized
  185. let loadingText = manager.vipType == .year ? "Congratulations on being VIP of the Year!".localized : "Congratulation you have become VIP".localized
  186. debugPrint(loadingText)
  187. TSToastShared.showToast(text:loadingText)
  188. if manager.isVip {
  189. DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
  190. self.closePage()
  191. }
  192. }
  193. case .restoreFail:
  194. TSToastShared.hideLoading()
  195. let loadingText = (object as? String) ?? "Failed to restore subscribe, please try again".localized
  196. debugPrint(loadingText)
  197. TSToastShared.showToast(text: loadingText)
  198. case .verifying:
  199. #if DEBUG
  200. TSToastShared.showLoading(text: "Verifying receipt...".localized,containerView: self.view)
  201. #endif
  202. case .verifySuccess:
  203. break
  204. case .verifyFail:
  205. #if DEBUG
  206. TSToastShared.hideLoading()
  207. let message = (object as? String) ?? "Failed to validate receipt".localized
  208. TSToastShared.showToast(text:message)
  209. #endif
  210. }
  211. }
  212. debugPrint("PurchaseManager onPurchaseStateChanged=\(String(describing: state))")
  213. }
  214. }
  215. override func viewWillAppear(_ animated: Bool) {
  216. super.viewWillAppear(animated)
  217. // 禁用右滑返回手势
  218. navigationController?.interactivePopGestureRecognizer?.isEnabled = false
  219. }
  220. override func viewWillDisappear(_ animated: Bool) {
  221. super.viewWillDisappear(animated)
  222. // 恢复右滑返回手势
  223. navigationController?.interactivePopGestureRecognizer?.isEnabled = true
  224. }
  225. deinit {
  226. cancellabel.removeAll()
  227. }
  228. }
  229. func kJudgeVipFreeType(vipFreeNumType:VipFreeNumType,
  230. vc:UIViewController? = nil,
  231. closePageBlock:(()->Void)? = nil) -> Bool {
  232. //判断 vip
  233. return kJudgeVip(externalBool: kPurchaseDefault.freeNumAvailable(type: vipFreeNumType) == false, vc: vc ,closePageBlock: closePageBlock)
  234. }
  235. func kJudgeVip(externalBool:Bool,
  236. vc:UIViewController? = nil,
  237. closePageBlock:(()->Void)? = nil) -> Bool {
  238. //判断 vip
  239. if externalBool,
  240. PurchaseManager.default.isVip == false
  241. {
  242. if let vc = vc {
  243. TSPurchaseVC.show(target: vc, closePageBlock: nil)
  244. }else if let rootVC = WindowHelper.getRootViewController() {
  245. TSPurchaseVC.show(target: rootVC, closePageBlock: nil)
  246. }
  247. return true
  248. }
  249. return false
  250. }
  251. extension TSPurchaseVC{
  252. static func show(target:UIViewController,closePageBlock:(()->Void)? = nil){
  253. kDelayMainShort {
  254. let vc = TSPurchaseVC()
  255. vc.closePageBlock = closePageBlock
  256. let navi = TSBaseNavigationC(rootViewController: vc)
  257. navi.modalPresentationStyle = .overFullScreen
  258. target.present(navi, animated: true)
  259. }
  260. }
  261. }