diff --git a/ReaderHive.xcodeproj/project.pbxproj b/ReaderHive.xcodeproj/project.pbxproj index e25d087..b316ccc 100644 --- a/ReaderHive.xcodeproj/project.pbxproj +++ b/ReaderHive.xcodeproj/project.pbxproj @@ -87,6 +87,15 @@ 85606A9A2EEBC26F005D989D /* NRCoinsPackViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85606A992EEBC26F005D989D /* NRCoinsPackViewController.swift */; }; 85606A9C2EEBE243005D989D /* NRCoinsPackHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85606A9B2EEBE243005D989D /* NRCoinsPackHeaderView.swift */; }; 85606A9F2EEBE95A005D989D /* NRDashedLineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85606A9E2EEBE95A005D989D /* NRDashedLineView.swift */; }; + 85CF941D2EEBFEA6006467E3 /* NRCoinsPackModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF941C2EEBFEA6006467E3 /* NRCoinsPackModel.swift */; }; + 85CF941F2EEBFECF006467E3 /* NRCoinsPackReceiveModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF941E2EEBFECF006467E3 /* NRCoinsPackReceiveModel.swift */; }; + 85CF94212EEC050D006467E3 /* NRCoinsPackBuyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF94202EEC050D006467E3 /* NRCoinsPackBuyView.swift */; }; + 85CF94232EEC08F4006467E3 /* NRCoinsPackClaimView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF94222EEC08F4006467E3 /* NRCoinsPackClaimView.swift */; }; + 85CF94252EEC09AE006467E3 /* NRCoinsPackClaimCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF94242EEC09AE006467E3 /* NRCoinsPackClaimCell.swift */; }; + 85CF94272EED1734006467E3 /* NRHomeCoinsPackButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF94262EED1734006467E3 /* NRHomeCoinsPackButton.swift */; }; + 85CF94292EED4664006467E3 /* NRVipRetainAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF94282EED4664006467E3 /* NRVipRetainAlert.swift */; }; + 85CF942B2EED47F2006467E3 /* NRVipRetainItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF942A2EED47F2006467E3 /* NRVipRetainItemView.swift */; }; + 85CF942D2EED5204006467E3 /* NRPayAlertModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF942C2EED5204006467E3 /* NRPayAlertModel.swift */; }; F34348AF2ED5B85300AA7E70 /* NRExploreViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34348AE2ED5B85300AA7E70 /* NRExploreViewController.swift */; }; F34348B12ED5B9A400AA7E70 /* NRExploreNovelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34348B02ED5B9A400AA7E70 /* NRExploreNovelViewController.swift */; }; F34348B32ED5BB6100AA7E70 /* NRExploreNovelMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34348B22ED5BB6100AA7E70 /* NRExploreNovelMenuView.swift */; }; @@ -309,6 +318,15 @@ 85606A992EEBC26F005D989D /* NRCoinsPackViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRCoinsPackViewController.swift; sourceTree = ""; }; 85606A9B2EEBE243005D989D /* NRCoinsPackHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRCoinsPackHeaderView.swift; sourceTree = ""; }; 85606A9E2EEBE95A005D989D /* NRDashedLineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRDashedLineView.swift; sourceTree = ""; }; + 85CF941C2EEBFEA6006467E3 /* NRCoinsPackModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRCoinsPackModel.swift; sourceTree = ""; }; + 85CF941E2EEBFECF006467E3 /* NRCoinsPackReceiveModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRCoinsPackReceiveModel.swift; sourceTree = ""; }; + 85CF94202EEC050D006467E3 /* NRCoinsPackBuyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRCoinsPackBuyView.swift; sourceTree = ""; }; + 85CF94222EEC08F4006467E3 /* NRCoinsPackClaimView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRCoinsPackClaimView.swift; sourceTree = ""; }; + 85CF94242EEC09AE006467E3 /* NRCoinsPackClaimCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRCoinsPackClaimCell.swift; sourceTree = ""; }; + 85CF94262EED1734006467E3 /* NRHomeCoinsPackButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRHomeCoinsPackButton.swift; sourceTree = ""; }; + 85CF94282EED4664006467E3 /* NRVipRetainAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRVipRetainAlert.swift; sourceTree = ""; }; + 85CF942A2EED47F2006467E3 /* NRVipRetainItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRVipRetainItemView.swift; sourceTree = ""; }; + 85CF942C2EED5204006467E3 /* NRPayAlertModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRPayAlertModel.swift; sourceTree = ""; }; C3BEE224CB3F55939653D26D /* Pods-NovelReader.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NovelReader.debug.xcconfig"; path = "Target Support Files/Pods-NovelReader/Pods-NovelReader.debug.xcconfig"; sourceTree = ""; }; F34348AE2ED5B85300AA7E70 /* NRExploreViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRExploreViewController.swift; sourceTree = ""; }; F34348B02ED5B9A400AA7E70 /* NRExploreNovelViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRExploreNovelViewController.swift; sourceTree = ""; }; @@ -501,6 +519,7 @@ F34349042ED9442300AA7E70 /* NRReadPageModel.swift */, F34990BE2EDEDDCF0039E939 /* NRCategoryModel.swift */, F34991282EE285660039E939 /* NRShowRecommendPop.swift */, + 85CF942C2EED5204006467E3 /* NRPayAlertModel.swift */, ); path = M; sourceTree = ""; @@ -515,6 +534,8 @@ F34348E02ED70A2200AA7E70 /* NRNovelDetailHeaderView+NovelCoverInfo.swift */, F34348E22ED70D2300AA7E70 /* NRNovelDetailHeaderView+Data.swift */, F3B859302EE66B950095A9CC /* NRDetailRechargeView.swift */, + 85CF94282EED4664006467E3 /* NRVipRetainAlert.swift */, + 85CF942A2EED47F2006467E3 /* NRVipRetainItemView.swift */, ); path = V; sourceTree = ""; @@ -797,6 +818,7 @@ 0373D95D2ED59C430017DCC7 /* NRSearchResultView.swift */, 0373D95F2ED59DA10017DCC7 /* NRSearchResultCell.swift */, F34990BC2EDEC24E0039E939 /* NRStarGradeView.swift */, + 85CF94262EED1734006467E3 /* NRHomeCoinsPackButton.swift */, ); path = V; sourceTree = ""; @@ -1091,6 +1113,9 @@ 85606A932EEBB3FE005D989D /* NRCoinsPackConfirmTitleView.swift */, 85606A952EEBB733005D989D /* NRCoinsPackConfirmTipView.swift */, 85606A9B2EEBE243005D989D /* NRCoinsPackHeaderView.swift */, + 85CF94202EEC050D006467E3 /* NRCoinsPackBuyView.swift */, + 85CF94222EEC08F4006467E3 /* NRCoinsPackClaimView.swift */, + 85CF94242EEC09AE006467E3 /* NRCoinsPackClaimCell.swift */, ); path = V; sourceTree = ""; @@ -1102,6 +1127,8 @@ F3B859802EE9716E0095A9CC /* NRBuyRecordsModel.swift */, F3B8598A2EEA51540095A9CC /* NRRewardCoinsModel.swift */, F3B859922EEA63CD0095A9CC /* NROrderRecordsModel.swift */, + 85CF941C2EEBFEA6006467E3 /* NRCoinsPackModel.swift */, + 85CF941E2EEBFECF006467E3 /* NRCoinsPackReceiveModel.swift */, ); path = M; sourceTree = ""; @@ -1250,10 +1277,14 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-ReaderHive/Pods-ReaderHive-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); + inputPaths = ( + ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-ReaderHive/Pods-ReaderHive-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); + outputPaths = ( + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReaderHive/Pods-ReaderHive-frameworks.sh\"\n"; @@ -1283,6 +1314,7 @@ F34349142EDA9AE900AA7E70 /* NRNovelReadSettingView.swift in Sources */, F3B859632EE91B850095A9CC /* NRStoreCoinsView.swift in Sources */, F34348BD2ED68F2B00AA7E70 /* NRExploreNovelViewModel.swift in Sources */, + 85CF94212EEC050D006467E3 /* NRCoinsPackBuyView.swift in Sources */, F3B8595F2EE910020095A9CC /* NRPayDataRequest.swift in Sources */, F3B859692EE91BD70095A9CC /* NRStoreCoinsPackCell.swift in Sources */, F34991102EE1708C0039E939 /* NRWebViewController+Script.swift in Sources */, @@ -1290,6 +1322,8 @@ F34348BF2ED691C100AA7E70 /* NRExploreNovelGenresViewController.swift in Sources */, 039810B02ED3F50C0006E317 /* NRHomeNovelMustReadTodayCell.swift in Sources */, 0373D9452ED57B7B0017DCC7 /* NRNovelDetailViewModel.swift in Sources */, + 85CF942D2EED5204006467E3 /* NRPayAlertModel.swift in Sources */, + 85CF942B2EED47F2006467E3 /* NRVipRetainItemView.swift in Sources */, 0398107C2ED055260006E317 /* AppDelegate+Config.swift in Sources */, F34348B52ED5C6F800AA7E70 /* NRTableView.swift in Sources */, F34348DB2ED6F80A00AA7E70 /* NRNovelReaderViewController.swift in Sources */, @@ -1299,6 +1333,7 @@ 85606A922EEBB336005D989D /* NRCoinsPackConfirmItemView.swift in Sources */, 0398106A2ED0505D0006E317 /* NRNetworkReachableManager.swift in Sources */, F34348C32ED6A20700AA7E70 /* NRExploreNovelContentViewController.swift in Sources */, + 85CF94232EEC08F4006467E3 /* NRCoinsPackClaimView.swift in Sources */, F34991292EE285660039E939 /* NRShowRecommendPop.swift in Sources */, 0373D9642ED5ABBC0017DCC7 /* NREmpty.swift in Sources */, F3B859572EE9072C0095A9CC /* NRIapManager.swift in Sources */, @@ -1309,6 +1344,7 @@ F3B859862EE972F70095A9CC /* NRConsumptionRecordsCell.swift in Sources */, F34990C72EDFCE500039E939 /* NRNovelReadGradeView.swift in Sources */, 039810B42ED428F20006E317 /* NRHomeNovelNewArrivalsView.swift in Sources */, + 85CF94272EED1734006467E3 /* NRHomeCoinsPackButton.swift in Sources */, F34991012EE1593A0039E939 /* NRMeHeaderView.swift in Sources */, F3B8598F2EEA5B1C0095A9CC /* NROrderRecordsPageViewController.swift in Sources */, F34991052EE165EA0039E939 /* NRMeItem.swift in Sources */, @@ -1331,6 +1367,7 @@ 039810D02ED54D370006E317 /* NRHomeCategoryTagView.swift in Sources */, F34348E32ED70D2F00AA7E70 /* NRNovelDetailHeaderView+Data.swift in Sources */, 0398107F2ED055D10006E317 /* NRLoginManager.swift in Sources */, + 85CF941D2EEBFEA6006467E3 /* NRCoinsPackModel.swift in Sources */, F34348D52ED6E16500AA7E70 /* NRMyListNovelViewController.swift in Sources */, F34991162EE176640039E939 /* NRNovelHistoryViewController.swift in Sources */, F34348F52ED848EC00AA7E70 /* NRNovelReadPageViewController.swift in Sources */, @@ -1338,10 +1375,12 @@ F349910E2EE1707C0039E939 /* NRWebViewController.swift in Sources */, F34990BB2EDEB2080039E939 /* NRHomeNovelViewModel.swift in Sources */, F34990BD2EDEC24E0039E939 /* NRStarGradeView.swift in Sources */, + 85CF941F2EEBFECF006467E3 /* NRCoinsPackReceiveModel.swift in Sources */, F34348C12ED693E900AA7E70 /* NRExploreNovelGenresCell.swift in Sources */, 0373D94B2ED582EE0017DCC7 /* NRNovelAPI.swift in Sources */, 039810832ED0563D0006E317 /* NRUserInfo.swift in Sources */, 0373D9472ED57F3F0017DCC7 /* UINavigationBar+NRAdd.swift in Sources */, + 85CF94252EEC09AE006467E3 /* NRCoinsPackClaimCell.swift in Sources */, 0373D94D2ED583A80017DCC7 /* NRNovelModel.swift in Sources */, F34348AF2ED5B85300AA7E70 /* NRExploreViewController.swift in Sources */, F3B859732EE94A760095A9CC /* NRAppWebViewController.swift in Sources */, @@ -1375,6 +1414,7 @@ F343492C2EDE72F300AA7E70 /* NRHomeAPI.swift in Sources */, F3B859372EE6750B0095A9CC /* NRLanguageViewController.swift in Sources */, F34348E12ED70A2700AA7E70 /* NRNovelDetailHeaderView+NovelCoverInfo.swift in Sources */, + 85CF94292EED4664006467E3 /* NRVipRetainAlert.swift in Sources */, F3B859522EE906A90095A9CC /* JXIAPManager.swift in Sources */, F34349012ED93A9B00AA7E70 /* NRReadChapterModel.swift in Sources */, F34348DF2ED7049E00AA7E70 /* NRNovelDetailHeaderView.swift in Sources */, diff --git a/ReaderHive/Base/Networking/API/NRStoreAPI.swift b/ReaderHive/Base/Networking/API/NRStoreAPI.swift index facac2b..11a5a10 100644 --- a/ReaderHive/Base/Networking/API/NRStoreAPI.swift +++ b/ReaderHive/Base/Networking/API/NRStoreAPI.swift @@ -127,4 +127,52 @@ struct NRStoreAPI { } } + + ///金币包数据 + static func requestCoinsPackData(completer: ((_ model: NRCoinsPackModel?) -> Void)?) { + + var param = NRNetwork.Parameters(path: "/getReceiveDayCoinInfo") + param.method = .get + + NRNetwork.request(parameters: param) { (response: NRNetwork.Response) in + completer?(response.data) + } + } + + ///领取金币包金币 + static func requestReceiveCoinsPackCoins(id: String?, completer: ((_ finish: Bool) -> Void)?) { + + var param = NRNetwork.Parameters(path: "/receiveDayCoin") + param.method = .post + param.isLoding = true + if let id = id { + param.parameters = [ + "id" : id + ] + } + NRNetwork.request(parameters: param) { (response: NRNetwork.Response) in + if response.isSuccess { + completer?(true) + } else { + completer?(false) + } + } + } + + ///挽留支付项 + static func requestVipRetainPayInfo(completer: ((_ model: NRPayAlertModel?) -> Void)?) { + + var param = NRNetwork.Parameters(path: "/getRetainVipPaySetting") + param.method = .get + param.isLoding = true + + NRNetwork.request(parameters: param) { (response: NRNetwork.Response) in + if let _ = response.data?.info { + completer?(response.data) + } else { + completer?(nil) + } + } + } + } diff --git a/ReaderHive/Base/Networking/NRUrlPath.swift b/ReaderHive/Base/Networking/NRUrlPath.swift index f14fa7d..11ad872 100644 --- a/ReaderHive/Base/Networking/NRUrlPath.swift +++ b/ReaderHive/Base/Networking/NRUrlPath.swift @@ -11,7 +11,7 @@ let NRBaseURL = "https://api-readerhive.readerhive.net" let NRWebBaseURL = "https://www.readerhive.net" -let NRCampaignWebURL = "https://campaign.readerhive.com" +let NRCampaignWebURL = "https://campaign.readerhive.net" diff --git a/ReaderHive/Base/View/NRDashedLineView.swift b/ReaderHive/Base/View/NRDashedLineView.swift index 03f5fb7..f2f41b0 100644 --- a/ReaderHive/Base/View/NRDashedLineView.swift +++ b/ReaderHive/Base/View/NRDashedLineView.swift @@ -8,12 +8,57 @@ import UIKit class NRDashedLineView: UIView { - - var isHorizontal: Bool = true // 控制是横向还是纵向绘制 - var lineWidth: CGFloat = 1.0 + enum Direction : Int { + case vertical = 0 + case horizontal = 1 + } - var lineColor: UIColor = .black.withAlphaComponent(0.25) + var direction: Direction = .horizontal { + didSet { + setNeedsDisplay() + } + } + + var lineColor: UIColor = .black.withAlphaComponent(0.25) { + didSet { + setNeedsDisplay() + } + } + + var lineWidth: CGFloat = 1.0 { + didSet { + invalidateIntrinsicContentSize() + setNeedsDisplay() + } + } + + // 控制虚线线段长度的属性 + var dashLength: CGFloat = 2 { + didSet { + setNeedsDisplay() + } + } + + // 控制虚线间隔长度的属性 + var dashGap: CGFloat = 2 { + didSet { + setNeedsDisplay() + } + } + + override var intrinsicContentSize: CGSize { + return CGSize(width: lineWidth, height: lineWidth) + } + + override init(frame: CGRect) { + super.init(frame: frame) + self.backgroundColor = .clear + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } override func draw(_ rect: CGRect) { super.draw(rect) @@ -22,21 +67,22 @@ class NRDashedLineView: UIView { guard let context = UIGraphicsGetCurrentContext() else { return } // 设置线条颜色和宽度 - context.setStrokeColor(UIColor.red.cgColor) + context.setStrokeColor(lineColor.cgColor) context.setLineWidth(lineWidth) // 设置虚线样式 - context.setLineDash(phase: 0, lengths: [2, 2]) // 长度为6的线段和间隔为3的空白 + context.setLineDash(phase: 0, lengths: [dashLength, dashGap]) // 根据方向绘制路径 - if isHorizontal { - // 横向绘制 - context.move(to: CGPoint(x: 0, y: self.bounds.midY)) - context.addLine(to: CGPoint(x: self.bounds.width, y: self.bounds.midY)) - } else { - // 纵向绘制 - context.move(to: CGPoint(x: self.bounds.midX, y: 0)) - context.addLine(to: CGPoint(x: self.bounds.midX, y: self.bounds.height)) + switch direction { + case .horizontal: + let y = self.bounds.midY + context.move(to: CGPoint(x: 0, y: y)) + context.addLine(to: CGPoint(x: self.bounds.width, y: y)) + case .vertical: + let x = self.bounds.midX + context.move(to: CGPoint(x: x, y: 0)) + context.addLine(to: CGPoint(x: x, y: self.bounds.height)) } // 绘制路径 diff --git a/ReaderHive/Base/WebView/NRAppWebViewController.swift b/ReaderHive/Base/WebView/NRAppWebViewController.swift index a31c573..4714865 100644 --- a/ReaderHive/Base/WebView/NRAppWebViewController.swift +++ b/ReaderHive/Base/WebView/NRAppWebViewController.swift @@ -14,7 +14,7 @@ class NRAppWebViewController: NRWebViewController { private var receiveDataCount = 0 - var theme: String? = "theme_3" + var theme: String? = "theme_2" override func viewDidLoad() { super.viewDidLoad() @@ -45,29 +45,30 @@ extension NRAppWebViewController { receiveDataCount += 1 if receiveDataCount > 10 { return } - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in - guard let self = self else { return } - var dic = [ - "token" : NRLoginManager.manager.token?.token ?? "", - "time_zone" : NRTargetType.timeZone(), - "lang" : NRLocalizedManager.shared.currentLocalizedKey, - "type" : "ios", - "device-id" : NRDeviceId.shared.id - ] - - if let theme = theme { - dic["theme"] = theme - } - - if let id = id { - dic["id"] = id - } - - if let json = dic.toJsonString() { - let js = "receiveDataFromNative(\(json))" - self.webView.evaluateJavaScript(js) { [weak self] _, error in - guard let self = self else { return } - if error != nil { + + var dic = [ + "token" : NRLoginManager.manager.token?.token ?? "", + "time_zone" : NRTargetType.timeZone(), + "lang" : NRLocalizedManager.shared.currentLocalizedKey, + "type" : "ios", + "device-id" : NRDeviceId.shared.id + ] + + if let theme = theme { + dic["theme"] = theme + } + + if let id = id { + dic["id"] = id + } + + if let json = dic.toJsonString() { + let js = "receiveDataFromNative(\(json))" + self.webView.evaluateJavaScript(js) { [weak self] _, error in + guard let self = self else { return } + if error != nil { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in + guard let self = self else { return } self.receiveDataFromNative() } } diff --git a/ReaderHive/Class/Home/C/NRHomeViewController.swift b/ReaderHive/Class/Home/C/NRHomeViewController.swift index 60747d1..1526cdc 100644 --- a/ReaderHive/Class/Home/C/NRHomeViewController.swift +++ b/ReaderHive/Class/Home/C/NRHomeViewController.swift @@ -26,6 +26,16 @@ class NRHomeViewController: NRViewController { lazy var novelVC = NRHomeNovelViewController() + lazy var coinsPackButton: NRHomeCoinsPackButton = { + let button = NRHomeCoinsPackButton() + button.addAction(UIAction(handler: { [weak self] _ in + guard let self = self else { return } + let vc = NRCoinsPackViewController() + self.navigationController?.pushViewController(vc, animated: true) + }), for: .touchUpInside) + return button + }() + override func viewDidLoad() { super.viewDidLoad() @@ -47,6 +57,7 @@ extension NRHomeViewController { view.addSubview(titleView) addChild(novelVC) view.addSubview(novelVC.view) + view.addSubview(coinsPackButton) searchButton.snp.makeConstraints { make in make.height.equalTo(44) @@ -63,6 +74,11 @@ extension NRHomeViewController { make.left.right.bottom.equalToSuperview() make.top.equalToSuperview().offset(UIScreen.navBarHeight) } + + coinsPackButton.snp.makeConstraints { make in + make.right.equalToSuperview().offset(-16) + make.bottom.equalToSuperview().offset(-50) + } } } diff --git a/ReaderHive/Class/Home/C/NRSearchViewController.swift b/ReaderHive/Class/Home/C/NRSearchViewController.swift index d5343a2..2b84bac 100644 --- a/ReaderHive/Class/Home/C/NRSearchViewController.swift +++ b/ReaderHive/Class/Home/C/NRSearchViewController.swift @@ -50,7 +50,6 @@ class NRSearchViewController: NRViewController { override func viewDidLoad() { super.viewDidLoad() self.backgroundImageView.isHidden = true - textView.becomeFirstResponder() nr_setupUI() @@ -61,6 +60,11 @@ class NRSearchViewController: NRViewController { self.navigationController?.setNavigationBarHidden(true, animated: true) } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + textView.becomeFirstResponder() + } + private func search(_ text: String) { if text.isEmpty { homeView.isHidden = false diff --git a/ReaderHive/Class/Home/V/NRHomeCoinsPackButton.swift b/ReaderHive/Class/Home/V/NRHomeCoinsPackButton.swift new file mode 100644 index 0000000..a2787e1 --- /dev/null +++ b/ReaderHive/Class/Home/V/NRHomeCoinsPackButton.swift @@ -0,0 +1,85 @@ +// +// NRHomeCoinsPackButton.swift +// ReaderHive +// +// Created by 澜声世纪 on 2025/12/13. +// + +import UIKit +import SnapKit +import YYCategories + +class NRHomeCoinsPackButton: UIControl { + + private lazy var bgImageView = UIImageView(image: UIImage(named: "calendar_icon_03")) + + private lazy var borderView: UIView = { + let view = UIView() + view.layer.cornerRadius = 14 + view.layer.masksToBounds = true + view.backgroundColor = UIColor.black.withAlphaComponent(0.25) + return view + }() + + private lazy var textBgView: UIImageView = { + let view = UIImageView(image: UIImage(named: "gradient_color_01")) + view.layer.cornerRadius = 12 + view.layer.masksToBounds = true + return view + }() + + private lazy var textLabel: UILabel = { + let label = NRLabel() + label.font = .font(ofSize: 12, weight: .bold).withBoldItalic() + label.textColors = [UIColor.FFEECA.cgColor, UIColor.FECE_62.cgColor] + label.textStartPoint = .init(x: 0, y: 0.5) + label.textEndPoint = .init(x: 1, y: 0.5) + label.text = "Daily Coins".localized + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + nr_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension NRHomeCoinsPackButton { + + private func nr_setupUI() { + addSubview(bgImageView) + addSubview(borderView) + borderView.addSubview(textBgView) + textBgView.addSubview(textLabel) + + bgImageView.snp.makeConstraints { make in + make.left.right.top.equalToSuperview() + make.bottom.equalToSuperview().offset(-1) + } + + borderView.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.bottom.equalToSuperview() + } + + textBgView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(2) + make.top.equalToSuperview().offset(2) + make.center.equalToSuperview() + make.height.equalTo(24) + make.width.equalTo(84) + } + + textLabel.snp.makeConstraints { make in + make.center.equalToSuperview() + make.left.equalToSuperview().offset(8) + } + } + +} diff --git a/ReaderHive/Class/Home/V/NRSearchResultCell.swift b/ReaderHive/Class/Home/V/NRSearchResultCell.swift index b23fc34..7add587 100644 --- a/ReaderHive/Class/Home/V/NRSearchResultCell.swift +++ b/ReaderHive/Class/Home/V/NRSearchResultCell.swift @@ -13,7 +13,8 @@ class NRSearchResultCell: UICollectionViewCell { var model: NRNovelModel? { didSet { coverImageView.nr_setImage(model?.image_url) - desLabel.text = model?.name + nameLabel.text = model?.name + desLabel.text = model?.nr_description tagStackView.nr_removeAllArrangedSubview() @@ -85,13 +86,6 @@ class NRSearchResultCell: UICollectionViewCell { override init(frame: CGRect) { super.init(frame: frame) - categoryView.text = "Satisfying" - nameLabel.text = "My Dark Romeo" - desLabel.text = "Haunted by fading memories, a man navigates a labyrinth of dreams and reality, uncovering truths that blur the line between past and present." - - tagStackView.addArrangedSubview(categoryView) - tagStackView.addArrangedSubview(hotView) -// categoryView.addSubview(hotView) nr_setupUI() } diff --git a/ReaderHive/Class/Novel/M/NRPayAlertModel.swift b/ReaderHive/Class/Novel/M/NRPayAlertModel.swift new file mode 100644 index 0000000..288d347 --- /dev/null +++ b/ReaderHive/Class/Novel/M/NRPayAlertModel.swift @@ -0,0 +1,22 @@ +// +// NRPayAlertModel.swift +// ReaderHive +// +// Created by 澜声世纪 on 2025/12/13. +// + +import UIKit +import SmartCodable + +class NRPayAlertModel: NSObject, SmartCodable { + + required override init() { } + + var coins_modal_easy_close: Bool? + + var info: NRPayItem? + + var forced_recharge: Bool? + + var close_label: String? +} diff --git a/ReaderHive/Class/Novel/V/NRDetailRechargeView.swift b/ReaderHive/Class/Novel/V/NRDetailRechargeView.swift index af2a7f3..e2f96b4 100644 --- a/ReaderHive/Class/Novel/V/NRDetailRechargeView.swift +++ b/ReaderHive/Class/Novel/V/NRDetailRechargeView.swift @@ -7,17 +7,218 @@ import UIKit import HWPanModal +import SnapKit class NRDetailRechargeView: NRPanModalContentView { - + + var buyFinishHandle: (() -> Void)? + var didDismissHandle: (() -> Void)? + + var payModel: NRPayDateModel? { + didSet { + self.stackView.nr_removeAllArrangedSubview() + self.vipView.dataArr = payModel?.list_sub_vip ?? [] + self.coinsView.setDataArr(payModel?.list_coins ?? []) + + if let sort = payModel?.sort, sort.count > 0 { + sort.forEach { + if $0 == .vip, payModel?.list_sub_vip?.isEmpty == false { + self.stackView.addArrangedSubview(self.vipView) + } else if $0 == .coin, payModel?.list_coins?.isEmpty == false { + self.stackView.addArrangedSubview(self.coinsView) + } + } + } else { + if payModel?.list_sub_vip?.isEmpty == false { + self.stackView.addArrangedSubview(self.vipView) + } + if payModel?.list_coins?.isEmpty == false { + self.stackView.addArrangedSubview(self.coinsView) + } + } + +// self.stackView.addArrangedSubview(self.tipView) + + self.setNeedsLayoutUpdate() + } + } + + var worksId: String? { + didSet { + coinsView.shortPlayId = worksId + vipView.shortPlayId = worksId + } + } + + var chapterId: String? { + didSet { + coinsView.videoId = chapterId + vipView.videoId = chapterId + } + } + + + private lazy var bgView: UIView = { + let view = UIImageView(image: UIImage(named: "bg_image_01")) + return view + }() + + private lazy var coinsBgView: UIView = { + let view = UIView() + view.backgroundColor = .white + view.layer.cornerRadius = 12 + view.layer.masksToBounds = true + return view + }() + + private lazy var coinsTitleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 14, weight: .regular) + label.textColor = .black + label.text = "Balance".localized + ":" + return label + }() + + private lazy var coinsIconView: UIImageView = UIImageView(image: UIImage(named: "coins_icon_04")) + + private lazy var coinsLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 14, weight: .bold) + label.textColor = .F_9710_D + label.text = "\(NRLoginManager.manager.userInfo?.totalCoins ?? 0)" + return label + }() + + private lazy var closeButton: UIButton = { + let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in + guard let self = self else { return } + Task { + await self.dismiss(animated: true) + } + self.didDismissHandle?() + })) + button.setImage(UIImage(named: "close_icon_02"), for: .normal) + return button + }() + + private lazy var scrollView: NRScrollView = { + let scrollView = NRScrollView() + return scrollView + }() + + private lazy var stackView: UIStackView = { + let view = UIStackView() + view.axis = .vertical + view.spacing = 12 + return view + }() + + private lazy var coinsView: NRStoreCoinsView = { + let view = NRStoreCoinsView() + view.isShowTitle = false + view.buyFinishHandle = { [weak self] in + self?.buyFinish() + } + return view + }() + + private lazy var vipView: NRStoreVipView = { + let view = NRStoreVipView() + view.isShowTitle = false + view.buyFinishHandle = { [weak self] in + self?.buyFinish() + } + return view + }() + + @MainActor deinit { + NotificationCenter.default.removeObserver(self) + } + override init(frame: CGRect) { super.init(frame: frame) - self.backgroundColor = .red + contentHeight = UIScreen.height - UIScreen.navBarHeight + mainScrollView = self.scrollView + NotificationCenter.default.addObserver(self, selector: #selector(userInfoUpdateNotification), name: NRLoginManager.userInfoUpdateNotification, object: nil) + + nr_setupUI() } @MainActor required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + @objc private func userInfoUpdateNotification() { + coinsLabel.text = "\(NRLoginManager.manager.userInfo?.totalCoins ?? 0)" + } + + private func buyFinish() { + Task { + await self.dismiss(animated: true) + } + Task { + await NRLoginManager.manager.updateUserInfo() + self.buyFinishHandle?() + } + } + +} + +extension NRDetailRechargeView { + + private func nr_setupUI() { + addSubview(bgView) + addSubview(coinsBgView) + coinsBgView.addSubview(coinsTitleLabel) + coinsBgView.addSubview(coinsIconView) + coinsBgView.addSubview(coinsLabel) + addSubview(closeButton) + addSubview(scrollView) + scrollView.addSubview(stackView) + + bgView.snp.makeConstraints { make in + make.left.right.top.equalToSuperview() + } + + coinsBgView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(16) + make.top.equalToSuperview().offset(16) + make.height.equalTo(24) + } + + coinsTitleLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().offset(12) + } + + coinsIconView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalTo(coinsTitleLabel.snp.right).offset(8) + } + + coinsLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalTo(coinsIconView.snp.right).offset(4) + make.right.equalToSuperview().offset(-12) + } + + closeButton.snp.makeConstraints { make in + make.centerY.equalTo(coinsBgView) + make.right.equalToSuperview().offset(-6) + make.width.height.equalTo(44) + } + + scrollView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalToSuperview().offset(52) + } + + stackView.snp.makeConstraints { make in + make.left.centerX.equalToSuperview() + make.top.equalToSuperview() + make.bottom.equalToSuperview().offset(-(UIScreen.safeBottom + 10)) + } + } + } diff --git a/ReaderHive/Class/Novel/V/NRVipRetainAlert.swift b/ReaderHive/Class/Novel/V/NRVipRetainAlert.swift new file mode 100644 index 0000000..7408258 --- /dev/null +++ b/ReaderHive/Class/Novel/V/NRVipRetainAlert.swift @@ -0,0 +1,150 @@ +// +// NRVipRetainAlert.swift +// ReaderHive +// +// Created by 澜声世纪 on 2025/12/13. +// + +import UIKit +import SnapKit +import YYCategories + +class NRVipRetainAlert: NRBaseAlert { + + var worksId: String? + + var chapterId: String? + + var buyFinishHandle: (() -> Void)? + + var model: NRPayAlertModel? { + didSet { + let payItem = model?.info + titleLabel.text = payItem?.getVipTitle() + itemView.model = payItem + } + } + + private lazy var titleLabel: UILabel = { + let label = NRLabel() + label.font = .font(ofSize: 24, weight: .black) + label.textColorImage = UIImage(named: "gradient_color_01") + return label + }() + + private lazy var itemView: NRVipRetainItemView = { + let view = NRVipRetainItemView() + return view + }() + + private lazy var textLabel: UILabel = { + let label = NRLabel() + label.font = .font(ofSize: 18, weight: .black).withBoldItalic() + label.textColors = [UIColor.FFEECA.cgColor, UIColor.FECE_62.cgColor] + label.textStartPoint = .init(x: 0, y: 0.5) + label.textEndPoint = .init(x: 1, y: 0.5) + label.text = "retain_alert_text".localized + label.numberOfLines = 0 + label.textAlignment = .center + return label + }() + + private lazy var buyButton: UIButton = { + var configuration = UIButton.Configuration.plain() + configuration.background.cornerRadius = 18 + configuration.background.image = UIImage(named: "gradient_color_01") + configuration.attributedTitle = AttributedString("Buy Now".localized, attributes: AttributeContainer([ + .font : UIFont.font(ofSize: 14, weight: .medium), + .foregroundColor : UIColor.white + ])) + + let button = UIButton(configuration: configuration, primaryAction: UIAction(handler: { [weak self] _ in + guard let self = self else { return } + self.buyVip() + })) + return button + }() + + private lazy var closeButton: UIButton = { + let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in + guard let self = self else { return } + self.dismiss() + })) + button.setImage(UIImage(named: "close_icon_03"), for: .normal) + return button + }() + + override init(frame: CGRect) { + super.init(frame: frame) + titleLabel.text = "Weekly VIP" + + + nr_setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func buyVip() { + guard let payItem = self.model?.info else { return } + + NRIapManager.manager.start(model: payItem, shortPlayId: worksId, videoId: chapterId) { [weak self] finish in + guard let self = self else { return } + if finish { + Task { + await NRLoginManager.manager.updateUserInfo() + } + self.dismiss() + self.buyFinishHandle?() + } + } + } + +} + +extension NRVipRetainAlert { + + private func nr_setupUI() { + contentView.addSubview(titleLabel) + contentView.addSubview(itemView) + contentView.addSubview(textLabel) + contentView.addSubview(buyButton) + contentView.addSubview(closeButton) + + + titleLabel.snp.makeConstraints { make in + make.top.equalToSuperview() + make.centerX.equalToSuperview() + } + + itemView.snp.makeConstraints { make in + make.top.equalToSuperview().offset(35) + make.left.equalToSuperview().offset(28) + make.right.equalToSuperview().offset(-28) + make.width.equalTo(UIScreen.width - 56) + make.height.equalTo(84) + } + + textLabel.snp.makeConstraints { make in + make.top.equalTo(itemView.snp.bottom).offset(12) + make.centerX.equalToSuperview() + make.right.lessThanOrEqualToSuperview().offset(-45) + } + + buyButton.snp.makeConstraints { make in + make.left.equalToSuperview().offset(45) + make.centerX.equalToSuperview() + make.top.equalTo(textLabel.snp.bottom).offset(12) + make.height.equalTo(36) + } + + closeButton.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalTo(buyButton.snp.bottom).offset(36) + make.bottom.equalToSuperview() + } + } + + +} diff --git a/ReaderHive/Class/Novel/V/NRVipRetainItemView.swift b/ReaderHive/Class/Novel/V/NRVipRetainItemView.swift new file mode 100644 index 0000000..0348ae9 --- /dev/null +++ b/ReaderHive/Class/Novel/V/NRVipRetainItemView.swift @@ -0,0 +1,177 @@ +// +// NRVipRetainItemView.swift +// ReaderHive +// +// Created by 澜声世纪 on 2025/12/13. +// + +import UIKit +import SnapKit +import YYCategories +internal import StoreKit + +class NRVipRetainItemView: UIView { + + var model: NRPayItem? { + didSet { + nameLabel.text = model?.getVipTitle() + + priceView.setNeedsUpdateConfiguration() + } + } + + private lazy var bgView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "bg_image_06")) + imageView.layer.cornerRadius = 12 + imageView.layer.masksToBounds = true + imageView.layer.borderWidth = 1 + imageView.layer.borderColor = UIColor.black.withAlphaComponent(0.05).cgColor + return imageView + }() + + private lazy var bgIconImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "coins_icon_03")) + return imageView + }() + + private lazy var vipIconImageView = UIImageView(image: UIImage(named: "vip_icon_year")) + + private lazy var nameLabel: UILabel = { + let label = NRLabel() + label.font = .font(ofSize: 16, weight: .semibold).withBoldItalic() + label.textColors = [UIColor.BA_8_A_2_A.cgColor, UIColor._8_B_5801.cgColor] + label.textStartPoint = .init(x: 0, y: 0.5) + label.textEndPoint = .init(x: 1, y: 0.5) + return label + }() + + private lazy var textLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .regular) + label.textColor = ._8_B_5801 + return label + }() + + private lazy var priceView: UIButton = { + var config = UIButton.Configuration.plain() + config.titleAlignment = .center + config.titlePadding = 0 + config.contentInsets = .init(top: 0, leading: 10, bottom: 0, trailing: 10) + + let button = NRGradientButton(configuration: config) + button.isUserInteractionEnabled = false + button.layer.masksToBounds = true + button.layer.cornerRadius = 24 + button.layer.borderWidth = 1 + button.layer.borderColor = UIColor.black.withAlphaComponent(0.15).cgColor + button.colors = [UIColor.white.cgColor, UIColor.FDE_9_CB.cgColor] + button.startPoint = .init(x: 0, y: 0.5) + button.endPoint = .init(x: 1, y: 0.5) + button.configurationUpdateHandler = { [weak self] button in + guard let self = self else { return } + + let currency = self.model?.currency ?? "" + let timeText = model?.getTimeString() ?? "" + let oldPrice = self.model?.price ?? "" + var discountPrice: String? = nil + + if self.model?.discount_type == 1, let introductoryPrice = self.model?.introductionaryOffer { + discountPrice = introductoryPrice.price.stringValue + } else if self.model?.discount_type == 2, let discount = self.model?.promotionalOffers?.first { + discountPrice = discount.price.stringValue + } + + if let discountPrice = discountPrice { + + let priceString = AttributedString("\(currency)\(discountPrice)", attributes: AttributeContainer([ + .font : UIFont.font(ofSize: 18, weight: .bold), + .foregroundColor : UIColor._946_A_37 + ])) + + + button.configuration?.attributedTitle = priceString + + var subtitle = AttributedString("\(currency)\(oldPrice)", attributes: AttributeContainer([ + .font : UIFont.font(ofSize: 12, weight: .regular), + .foregroundColor : UIColor.black.withAlphaComponent(0.05), + .strikethroughStyle: NSUnderlineStyle.double.rawValue, + .strikethroughColor: UIColor.black.withAlphaComponent(0.05) + ])) + + button.configuration?.attributedSubtitle = subtitle + + } else { + + button.configuration?.attributedTitle = AttributedString("\(currency)\(oldPrice)", attributes: AttributeContainer([ + .font : UIFont.font(ofSize: 18, weight: .bold), + .foregroundColor : UIColor._946_A_37 + ])) + + button.configuration?.attributedSubtitle = AttributedString("/\(timeText)", attributes: AttributeContainer([ + .font : UIFont.font(ofSize: 12, weight: .regular), + .foregroundColor : UIColor.black.withAlphaComponent(0.5) + ])) + } + + } + + return button + }() + + override init(frame: CGRect) { + super.init(frame: frame) + textLabel.text = "vip_tip_01".localized + nameLabel.text = "Weekly Refill" + + nr_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + + +extension NRVipRetainItemView { + + private func nr_setupUI() { + addSubview(bgView) + bgView.addSubview(bgIconImageView) + bgView.addSubview(vipIconImageView) + bgView.addSubview(nameLabel) + bgView.addSubview(textLabel) + bgView.addSubview(priceView) + + bgView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + + bgIconImageView.snp.makeConstraints { make in + make.top.bottom.left.equalToSuperview() + } + + vipIconImageView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(12) + make.top.equalToSuperview().offset(18) + } + + nameLabel.snp.makeConstraints { make in + make.centerY.equalTo(vipIconImageView) + make.left.equalTo(vipIconImageView.snp.right).offset(4) + } + + textLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(12) + make.bottom.equalToSuperview().offset(-20) + } + + priceView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalToSuperview().offset(-12) + make.height.equalTo(48) + make.width.greaterThanOrEqualTo(88) + } + } + +} diff --git a/ReaderHive/Class/Novel/VC/NRNovelDetailViewController.swift b/ReaderHive/Class/Novel/VC/NRNovelDetailViewController.swift index 27a3263..1f51e93 100644 --- a/ReaderHive/Class/Novel/VC/NRNovelDetailViewController.swift +++ b/ReaderHive/Class/Novel/VC/NRNovelDetailViewController.swift @@ -220,7 +220,8 @@ extension NRNovelDetailViewController { private func tableViewMaxContentOffsetY() -> CGFloat { let contentSizeH = tableView.contentSize.height let maxOffsetY = contentSizeH - tableView.bounds.height - return maxOffsetY +// return maxOffsetY + return floor(maxOffsetY) } func setMainTableViewToMaxContentOffsetY() { diff --git a/ReaderHive/Class/Novel/VC/Read/NRNovelReaderViewController.swift b/ReaderHive/Class/Novel/VC/Read/NRNovelReaderViewController.swift index 951d00e..3fe766b 100644 --- a/ReaderHive/Class/Novel/VC/Read/NRNovelReaderViewController.swift +++ b/ReaderHive/Class/Novel/VC/Read/NRNovelReaderViewController.swift @@ -184,15 +184,18 @@ extension NRNovelReaderViewController { extension NRNovelReaderViewController { ///装载数据 - private func loadData() { + func loadData() { Task { + NRHud.show() //获取小说数据 await self.viewModel.requestNovelDetail() //获取目录数据 await self.viewModel.requestChapterCatalogList() - guard !self.viewModel.chapterCatalogList.isEmpty else { return } - + guard !self.viewModel.chapterCatalogList.isEmpty else { + NRHud.dismiss() + return + } let indexPath = self.indexPathToReadRecord() let section = indexPath.section @@ -208,6 +211,7 @@ extension NRNovelReaderViewController { self.viewModel.checkCurrentIndexPath() self.reloadData() + NRHud.dismiss() } } diff --git a/ReaderHive/Class/Novel/VM/NRNovelReadViewModel.swift b/ReaderHive/Class/Novel/VM/NRNovelReadViewModel.swift index 81bca18..6bd7487 100644 --- a/ReaderHive/Class/Novel/VM/NRNovelReadViewModel.swift +++ b/ReaderHive/Class/Novel/VM/NRNovelReadViewModel.swift @@ -47,6 +47,9 @@ class NRNovelReadViewModel: NSObject { ///用来记录上报历史记录的时间 var uploadRecordDate: Date? + weak var popView: UIView? + + private var payDataRequest = NRPayDataRequest() } @@ -57,7 +60,7 @@ extension NRNovelReadViewModel { func backReadPage() { Task { guard let isShowModel = await NRNovelAPI.requestShowRecommendPop(id: self.novelId), isShowModel.is_pop_up == true else { - await _backReadPage() + _backReadPage() return } @@ -157,10 +160,74 @@ extension NRNovelReadViewModel { ///打开充值页面 func openRechargeView() { -// let view = NRDetailRechargeView() -// view.present(in: nil) + guard self.popView == nil else { return } + let (catalogModel, _) = self.getCurrentPageData() + guard let catalogModel = catalogModel else { return } + + if let model = NRIapManager.manager.payDateModel { + _openRechargeView(model, catalogModel) + + self.payDataRequest.requestProducts(isLoding: false) { [weak self] model in + guard let self = self else { return } + guard let model = model else { return } + if let view = self.popView as? NRDetailRechargeView { + view.payModel = model + } + } + } else { + self.payDataRequest.requestProducts(isLoding: true) { [weak self] model in + guard let self = self else { return } + guard let model = model else { return } + self._openRechargeView(model, catalogModel) + } + } } + private func _openRechargeView(_ payModel: NRPayDateModel, _ catalogModel: NRReadChapterCatalogModel) { + guard self.popView == nil else { return } + + + let view = NRDetailRechargeView() + view.payModel = payModel + view.worksId = self.novelId + view.chapterId = catalogModel.id + + view.buyFinishHandle = { [weak self] in + guard let self = self else { return } + self.targetCatalogModel = catalogModel + self.vc?.loadData() + } + + view.didDismissHandle = { [weak self] in + guard let self = self else { return } + self.showVipRetainAlert(catalogModel) + } + view.present(in: nil) + + self.popView = view + } + + ///展示挽留弹窗 + private func showVipRetainAlert(_ catalogModel: NRReadChapterCatalogModel) { + + payDataRequest.requestVipRetainPayInfo { [weak self] model in + guard let self = self else { return } + guard let model = model else { return } + + let view = NRVipRetainAlert() + view.model = model + view.worksId = self.novelId + view.chapterId = catalogModel.id + view.buyFinishHandle = { [weak self] in + guard let self = self else { return } + self.targetCatalogModel = catalogModel + self.vc?.loadData() + } + view.show(in: nil) + } + + + } } //MARK: UIGestureRecognizerDelegate diff --git a/ReaderHive/Class/Store/M/NRCoinsPackModel.swift b/ReaderHive/Class/Store/M/NRCoinsPackModel.swift new file mode 100644 index 0000000..737f148 --- /dev/null +++ b/ReaderHive/Class/Store/M/NRCoinsPackModel.swift @@ -0,0 +1,27 @@ +// +// NRCoinsPackModel.swift +// ReaderHive +// +// Created by 澜声世纪 on 2025/12/12. +// + +import UIKit +import SmartCodable + +class NRCoinsPackModel: NSObject, SmartCodable { + override required init() { } + + //当前可领取订阅数 + var receive_count: Int? + //已领取累计金币总数 + var week_total: Int? + //剩余可领取金币数 + var week_remaining_total: Int? + //订阅可领取累计金币总数 + var week_max_total: Int? + //当前可领取金币总数 + var receive_coins: Int? + + var receive_list: [NRCoinsPackReceiveModel]? + +} diff --git a/ReaderHive/Class/Store/M/NRCoinsPackReceiveModel.swift b/ReaderHive/Class/Store/M/NRCoinsPackReceiveModel.swift new file mode 100644 index 0000000..cbf875e --- /dev/null +++ b/ReaderHive/Class/Store/M/NRCoinsPackReceiveModel.swift @@ -0,0 +1,28 @@ +// +// NRCoinsPackReceiveModel.swift +// ReaderHive +// +// Created by 澜声世纪 on 2025/12/12. +// + +import UIKit +import SmartCodable + +class NRCoinsPackReceiveModel: NSObject, SmartCodable { + + override required init() { } + + var id: String? + var title: String? + //剩余可领取金币数 + var week_remaining_total: Int? + //已领取累计金币总数 + var week_total: Int? + //领取天数文本 + var day_text: String? + //当前可领取金币数 + var receive_coins: Int? + //可领取累计金币总数 + var week_max_total: Int? + +} diff --git a/ReaderHive/Class/Store/V/NRCoinsPackBuyView.swift b/ReaderHive/Class/Store/V/NRCoinsPackBuyView.swift new file mode 100644 index 0000000..fb71794 --- /dev/null +++ b/ReaderHive/Class/Store/V/NRCoinsPackBuyView.swift @@ -0,0 +1,116 @@ +// +// NRCoinsPackBuyView.swift +// ReaderHive +// +// Created by 澜声世纪 on 2025/12/12. +// + +import UIKit +import SnapKit +import HWPanModal + +class NRCoinsPackBuyView: UIView { + + var buyFinishHandle: (() -> Void)? + + var dataArr: [NRPayItem] = [] + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 16, weight: .semibold) + label.textColor = ._714_A_1_B + label.text = "Weekly Refills".localized + return label + }() + + private lazy var collectionViewLayout: UICollectionViewFlowLayout = { + let layout = UICollectionViewFlowLayout() + layout.itemSize = .init(width: UIScreen.width - 32, height: 84) + layout.minimumLineSpacing = 12 + return layout + }() + + private lazy var collectionView: NRCollectionView = { + let collectionView = NRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) + collectionView.delegate = self + collectionView.dataSource = self + collectionView.addObserver(self, forKeyPath: "contentSize", context: nil) + collectionView.register(NRStoreCoinsPackCell.self, forCellWithReuseIdentifier: "cell") + return collectionView + }() + + @MainActor deinit { + collectionView.removeObserver(self, forKeyPath: "contentSize") + } + + override init(frame: CGRect) { + super.init(frame: frame) + + nr_setupUI() + } + + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + if keyPath == "contentSize" { + let height = self.collectionView.contentSize.height + 1 + collectionView.snp.updateConstraints { make in + make.height.equalTo(height) + } + } + } +} + +extension NRCoinsPackBuyView { + + private func nr_setupUI() { + addSubview(titleLabel) + addSubview(collectionView) + + titleLabel.snp.makeConstraints { make in + make.top.equalToSuperview() + make.left.equalToSuperview().offset(16) + } + + collectionView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalToSuperview().offset(36) + make.height.equalTo(1) + make.bottom.equalToSuperview() + } + } + +} + +//MARK: UICollectionViewDelegate UICollectionViewDataSource +extension NRCoinsPackBuyView: UICollectionViewDelegate, UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! NRStoreCoinsPackCell + cell.model = self.dataArr[indexPath.row] + return cell + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return dataArr.count + } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let model = self.dataArr[indexPath.row] + + let view = NRCoinsPackConfirmView() + view.model = model + view.buyFinishHandle = { [weak self] in + guard let self = self else { return } + Task { + await NRLoginManager.manager.updateUserInfo() + } + self.buyFinishHandle?() + } + view.present(in: nil) + } + +} diff --git a/ReaderHive/Class/Store/V/NRCoinsPackClaimCell.swift b/ReaderHive/Class/Store/V/NRCoinsPackClaimCell.swift new file mode 100644 index 0000000..ae06041 --- /dev/null +++ b/ReaderHive/Class/Store/V/NRCoinsPackClaimCell.swift @@ -0,0 +1,261 @@ +// +// NRCoinsPackClaimCell.swift +// ReaderHive +// +// Created by 澜声世纪 on 2025/12/12. +// + +import UIKit +import SnapKit +import YYText + +class NRCoinsPackClaimCell: UICollectionViewCell { + + var clickClaimButton: ((_ id: String?) -> Void)? + + var model: NRCoinsPackReceiveModel? { + didSet { + coinsView1.coins = model?.week_max_total + coinsView2.coins = model?.week_remaining_total + + claimButton.isEnabled = (model?.receive_coins ?? 0) > 0 + claimButton.setNeedsUpdateConfiguration() + + let titleAtt = NSMutableAttributedString(string: "\(model?.title ?? "")") + titleAtt.yy_color = ._714_A_1_B + titleAtt.yy_font = .font(ofSize: 14, weight: .bold) + + let day = "Day".localized + let dayAtt = NSMutableAttributedString(string: " (\(day) \(model?.day_text ?? ""))") + dayAtt.yy_color = ._946_A_37 + dayAtt.yy_font = .font(ofSize: 14, weight: .regular) + titleAtt.append(dayAtt) + + titleLabel.attributedText = titleAtt + + } + } + + private lazy var bgView: UIView = { + let view = UIImageView(image: UIImage(named: "bg_image_07")) + view.layer.cornerRadius = 12 + view.layer.masksToBounds = true + view.layer.borderWidth = 1 + view.layer.borderColor = UIColor.black.withAlphaComponent(0.05).cgColor + return view + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.numberOfLines = 0 + return label + }() + + private lazy var lineView1: UIView = { + let view = NRDashedLineView() + view.direction = .horizontal + return view + }() + + private lazy var lineView2: UIView = { + let view = NRDashedLineView() + view.direction = .vertical + return view + }() + + private lazy var coinsView1: CoinsView = { + let view = CoinsView() + view.title = "Total Reward".localized + return view + }() + + private lazy var coinsView2: CoinsView = { + let view = CoinsView() + view.title = "Remaining".localized + return view + }() + + private lazy var claimButton: UIButton = { + var config = UIButton.Configuration.plain() + config.titleAlignment = .center + + let button = NRGradientButton(configuration: config, primaryAction: UIAction(handler: { [weak self] _ in + guard let self = self else { return } + self.clickClaimButton?(self.model?.id) + })) + button.layer.masksToBounds = true + button.layer.cornerRadius = 24 + button.layer.borderWidth = 1 + + button.startPoint = .init(x: 0, y: 0.5) + button.endPoint = .init(x: 1, y: 0.5) + button.configurationUpdateHandler = { [weak self] button in + guard let self = self else { return } + guard let button = button as? NRGradientButton else { return } + + if button.isEnabled { + button.colors = [UIColor.white.cgColor, UIColor.FDE_9_CB.cgColor] + button.layer.borderColor = UIColor.black.withAlphaComponent(0.15).cgColor + + let coinImage = UIImage(named: "coins_icon_01")! + let coinText = NSTextAttachment(image: coinImage) + coinText.bounds = .init(x: 0, y: -3, width: coinImage.size.width, height: coinImage.size.height) + let coinAtt = AttributedString(NSAttributedString(attachment: coinText)) + let countAtt = AttributedString(" \(self.model?.receive_coins ?? 0)", attributes: AttributeContainer([ + .font : UIFont.font(ofSize: 12, weight: .bold), + .foregroundColor : UIColor._946_A_37.withAlphaComponent(0.5) + ])) + + + button.configuration?.attributedTitle = AttributedString("Claim".localized, attributes: AttributeContainer([ + .font : UIFont.font(ofSize: 14, weight: .bold), + .foregroundColor : UIColor._714_A_1_B + ])) + + button.configuration?.attributedSubtitle = coinAtt + countAtt + + } else { + button.colors = [UIColor.E_0_E_0_E_0.cgColor, UIColor.E_0_E_0_E_0.cgColor] + button.layer.borderColor = UIColor.clear.cgColor + + button.configuration?.attributedTitle = AttributedString("Claimed".localized, attributes: AttributeContainer([ + .font : UIFont.font(ofSize: 14, weight: .bold), + .foregroundColor : UIColor.black.withAlphaComponent(0.25) + ])) + + button.configuration?.attributedSubtitle = nil + } + + + } + return button + }() + + override init(frame: CGRect) { + super.init(frame: frame) + nr_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +extension NRCoinsPackClaimCell { + + private func nr_setupUI() { + contentView.addSubview(bgView) + contentView.addSubview(titleLabel) + contentView.addSubview(lineView1) + contentView.addSubview(lineView2) + contentView.addSubview(coinsView1) + contentView.addSubview(coinsView2) + contentView.addSubview(claimButton) + + bgView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(12) + make.centerY.equalTo(self.bgView.snp.top).offset(25) + make.right.lessThanOrEqualToSuperview().offset(-12) + } + + lineView1.snp.makeConstraints { make in + make.left.equalToSuperview().offset(12) + make.centerX.equalToSuperview() + make.top.equalToSuperview().offset(48) + } + + coinsView1.snp.makeConstraints { make in + make.left.equalToSuperview().offset(12) + make.bottom.equalToSuperview().offset(-17) + } + + lineView2.snp.makeConstraints { make in + make.centerY.equalTo(coinsView1) + make.left.equalTo(coinsView1.snp.right).offset(12) + make.height.equalTo(32) + } + + coinsView2.snp.makeConstraints { make in + make.centerY.equalTo(coinsView1) + make.left.equalTo(lineView2.snp.right).offset(12) + } + + claimButton.snp.makeConstraints { make in + make.right.equalToSuperview().offset(-12) + make.centerY.equalTo(coinsView1) + make.height.equalTo(48) + make.width.equalTo(120) + } + } + +} + + +extension NRCoinsPackClaimCell { + + class CoinsView: UIView { + + var title: String? { + didSet { + titleLabel.text = title + } + } + + var coins: Int? { + didSet { + coinsLabel.text = "\(coins ?? 0)" + } + } + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .bold) + label.textColor = ._946_A_37 + return label + }() + + private lazy var iconImageView = UIImageView(image: UIImage(named: "coins_icon_01")) + + private lazy var coinsLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 14, weight: .bold) + label.textColor = ._946_A_37 + label.text = "0" + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + addSubview(titleLabel) + addSubview(iconImageView) + addSubview(coinsLabel) + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview() + make.top.equalToSuperview() + make.right.lessThanOrEqualToSuperview() + } + + iconImageView.snp.makeConstraints { make in + make.left.equalToSuperview() + make.bottom.equalToSuperview() + make.top.equalTo(titleLabel.snp.bottom).offset(8) + } + + coinsLabel.snp.makeConstraints { make in + make.centerY.equalTo(iconImageView) + make.left.equalTo(iconImageView.snp.right).offset(4) + make.right.lessThanOrEqualToSuperview() + } + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + } + +} diff --git a/ReaderHive/Class/Store/V/NRCoinsPackClaimView.swift b/ReaderHive/Class/Store/V/NRCoinsPackClaimView.swift new file mode 100644 index 0000000..e76e679 --- /dev/null +++ b/ReaderHive/Class/Store/V/NRCoinsPackClaimView.swift @@ -0,0 +1,107 @@ +// +// NRCoinsPackClaimView.swift +// ReaderHive +// +// Created by 澜声世纪 on 2025/12/12. +// + +import UIKit +import SnapKit + +class NRCoinsPackClaimView: UIView { + + var clickClaimButton: ((_ id: String?) -> Void)? + + var dataArr: [NRCoinsPackReceiveModel] = [] { + didSet { + collectionView.reloadData() + } + } + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 16, weight: .semibold) + label.textColor = ._714_A_1_B + label.text = "Active Refills".localized + return label + }() + + private lazy var collectionViewLayout: UICollectionViewFlowLayout = { + let layout = UICollectionViewFlowLayout() + layout.itemSize = .init(width: UIScreen.width - 32, height: 122) + layout.minimumLineSpacing = 12 + return layout + }() + + private lazy var collectionView: NRCollectionView = { + let collectionView = NRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) + collectionView.delegate = self + collectionView.dataSource = self + collectionView.addObserver(self, forKeyPath: "contentSize", context: nil) + collectionView.register(NRCoinsPackClaimCell.self, forCellWithReuseIdentifier: "cell") + return collectionView + }() + + deinit { + collectionView.removeObserver(self, forKeyPath: "contentSize") + } + + override init(frame: CGRect) { + super.init(frame: frame) + + nr_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + if keyPath == "contentSize" { + let height = self.collectionView.contentSize.height + 1 + collectionView.snp.updateConstraints { make in + make.height.equalTo(height) + } + } + } + +} + +extension NRCoinsPackClaimView { + + private func nr_setupUI() { + addSubview(titleLabel) + addSubview(collectionView) + + titleLabel.snp.makeConstraints { make in + make.top.equalToSuperview() + make.left.equalToSuperview().offset(16) + } + + collectionView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalToSuperview().offset(36) + make.height.equalTo(1) + make.bottom.equalToSuperview() + } + } +} + +//MARK: UICollectionViewDelegate UICollectionViewDataSource +extension NRCoinsPackClaimView: UICollectionViewDelegate, UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! NRCoinsPackClaimCell + cell.model = self.dataArr[indexPath.row] + cell.clickClaimButton = { [weak self] id in + self?.clickClaimButton?(id) + } + return cell + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return dataArr.count + } + + +} diff --git a/ReaderHive/Class/Store/V/NRCoinsPackHeaderView.swift b/ReaderHive/Class/Store/V/NRCoinsPackHeaderView.swift index 61ee264..6e4990f 100644 --- a/ReaderHive/Class/Store/V/NRCoinsPackHeaderView.swift +++ b/ReaderHive/Class/Store/V/NRCoinsPackHeaderView.swift @@ -7,11 +7,28 @@ import UIKit import SnapKit +import YYCategories + class NRCoinsPackHeaderView: UIView { var clickClaimButton: (() -> Void)? + var model: NRCoinsPackModel? { + didSet { + coinsView1.coins = model?.week_max_total + coinsView2.coins = model?.week_total + + countLabel.text = "\(model?.receive_count ?? 0)" + + if let coin = model?.receive_coins, coin > 0 { + claimButton.isEnabled = true + } else { + claimButton.isEnabled = false + } + claimButton.setNeedsUpdateConfiguration() + } + } private lazy var titleLabel: UILabel = { let label = UILabel() @@ -28,19 +45,85 @@ class NRCoinsPackHeaderView: UIView { return view }() - private lazy var lineView1: NRDashedLineView = { - let view = NRDashedLineView() - view.isHorizontal = false - return view - }() - private lazy var coinsView2: CoinsView = { let view = CoinsView() view.title = "Claimable Coins".localized view.coins = 0 return view }() + + private lazy var countTitleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .regular) + label.textColor = .black.withAlphaComponent(0.5) + label.text = "Active Refills".localized + ": " + return label + }() + + private lazy var countLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .regular) + label.textColor = .F_9710_D + label.text = "0" + return label + }() + + private lazy var claimButton: UIButton = { + var config = UIButton.Configuration.plain() + config.background.cornerRadius = 24 + let button = UIButton(configuration: config, primaryAction: UIAction(handler: { [weak self] _ in + self?.clickClaimButton?() + })) + button.isEnabled = false + + button.configurationUpdateHandler = { [weak self] button in + guard let self = self else { return } + if button.isEnabled { + button.configuration?.background.image = UIImage(named: "gradient_color_01") + + let coinImage = UIImage(named: "coins_icon_06")! + let coinText = NSTextAttachment(image: coinImage) + coinText.bounds = .init(x: 0, y: -3, width: coinImage.size.width, height: coinImage.size.height) + let coinAtt = AttributedString(NSAttributedString(attachment: coinText)) + + let textAtt = AttributedString("Claim All".localized + " ", attributes: AttributeContainer([ + .font : UIFont.font(ofSize: 14, weight: .semibold), + .foregroundColor : UIColor.white + ])) + + let countAtt = AttributedString(" \(self.model?.receive_coins ?? 0)".localized, attributes: AttributeContainer([ + .font : UIFont.font(ofSize: 14, weight: .semibold), + .foregroundColor : UIColor.white + ])) + + button.configuration?.attributedTitle = textAtt + coinAtt + countAtt + + + } else { + button.configuration?.background.image = UIImage(color: .E_0_E_0_E_0) + + button.configuration?.attributedTitle = AttributedString("Claimed".localized, attributes: AttributeContainer([ + .font : UIFont.font(ofSize: 14, weight: .semibold), + .foregroundColor : UIColor.black.withAlphaComponent(0.25) + ])) + } + + } + return button + }() + private lazy var lineView1: NRDashedLineView = { + let view = NRDashedLineView() + view.direction = .vertical + return view + }() + + private lazy var lineView2: NRDashedLineView = { + let view = NRDashedLineView() + view.direction = .horizontal + return view + }() + override init(frame: CGRect) { super.init(frame: frame) @@ -60,6 +143,10 @@ extension NRCoinsPackHeaderView { addSubview(coinsView1) addSubview(coinsView2) addSubview(lineView1) + addSubview(countTitleLabel) + addSubview(countLabel) + addSubview(claimButton) + addSubview(lineView2) titleLabel.snp.makeConstraints { make in make.left.equalToSuperview().offset(16) @@ -74,13 +161,35 @@ extension NRCoinsPackHeaderView { lineView1.snp.makeConstraints { make in make.centerY.equalTo(coinsView1) make.left.equalTo(coinsView1.snp.right).offset(18) - make.width.equalTo(1) make.height.equalTo(32) } coinsView2.snp.makeConstraints { make in make.centerY.equalTo(lineView1) make.left.equalTo(lineView1.snp.right).offset(18) + } + + countTitleLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(28) + make.top.equalTo(coinsView1.snp.bottom).offset(22) + } + + countLabel.snp.makeConstraints { make in + make.centerY.equalTo(countTitleLabel) + make.left.equalTo(countTitleLabel.snp.right) + } + + claimButton.snp.makeConstraints { make in + make.left.equalToSuperview().offset(28) + make.centerX.equalToSuperview() + make.top.equalTo(countTitleLabel.snp.bottom).offset(12) + make.height.equalTo(48) + make.bottom.equalToSuperview().offset(-16) + } + + lineView2.snp.makeConstraints { make in + make.left.equalToSuperview().offset(28) + make.centerX.equalToSuperview() make.bottom.equalToSuperview() } diff --git a/ReaderHive/Class/Store/V/NRStoreCoinsPackCell.swift b/ReaderHive/Class/Store/V/NRStoreCoinsPackCell.swift index f3f8713..ca32ad6 100644 --- a/ReaderHive/Class/Store/V/NRStoreCoinsPackCell.swift +++ b/ReaderHive/Class/Store/V/NRStoreCoinsPackCell.swift @@ -111,20 +111,20 @@ class NRStoreCoinsPackCell: NRStoreCoinsCell { let priceString = AttributedString("\(currency)\(discountPrice)", attributes: AttributeContainer([ .font : UIFont.font(ofSize: 18, weight: .bold), -// .foregroundColor : UIColor._114_CEE + .foregroundColor : UIColor._946_A_37 ])) -// button.configuration?.attributedTitle = priceString -// -// var subtitle = AttributedString("\(currency)\(oldPrice)", attributes: AttributeContainer([ -// .font : UIFont.font(ofSize: 12, weight: .regular), -// .foregroundColor : UIColor._636_F_7_B.withAlphaComponent(0.05), -// .strikethroughStyle: NSUnderlineStyle.double.rawValue, -// .strikethroughColor: UIColor._636_F_7_B.withAlphaComponent(0.05) -// ])) -// -// button.configuration?.attributedSubtitle = subtitle + button.configuration?.attributedTitle = priceString + + var subtitle = AttributedString("\(currency)\(oldPrice)", attributes: AttributeContainer([ + .font : UIFont.font(ofSize: 12, weight: .regular), + .foregroundColor : UIColor.black.withAlphaComponent(0.05), + .strikethroughStyle: NSUnderlineStyle.double.rawValue, + .strikethroughColor: UIColor.black.withAlphaComponent(0.05) + ])) + + button.configuration?.attributedSubtitle = subtitle } else { diff --git a/ReaderHive/Class/Store/VC/NRCoinsPackViewController.swift b/ReaderHive/Class/Store/VC/NRCoinsPackViewController.swift index d7bf576..98c58c3 100644 --- a/ReaderHive/Class/Store/VC/NRCoinsPackViewController.swift +++ b/ReaderHive/Class/Store/VC/NRCoinsPackViewController.swift @@ -7,9 +7,32 @@ import UIKit import SnapKit +import YYText class NRCoinsPackViewController: NRViewController { - + + + private var model: NRCoinsPackModel? { + didSet { + headerView.model = model + claimView.dataArr = model?.receive_list ?? [] + } + } + + private var payDataModel :NRPayDateModel? { + didSet { + var arr: [NRPayItem] = [] + + payDataModel?.list_coins?.forEach { + if $0.buy_type == .subCoins { + arr.append($0) + } + } + self.buyView.dataArr = arr + } + } + + private var payDataRequest: NRPayDataRequest = NRPayDataRequest() private lazy var bgIconImageView: UIImageView = { let imageView = UIImageView(image: UIImage(named: "calendar_icon_02")) @@ -23,15 +46,87 @@ class NRCoinsPackViewController: NRViewController { private lazy var headerView: NRCoinsPackHeaderView = { let view = NRCoinsPackHeaderView() + view.clickClaimButton = { [weak self] in + self?.requestReceiveCoins(nil) + } return view }() + private lazy var stackView: UIStackView = { + let stackView = UIStackView() + stackView.axis = .vertical + stackView.spacing = 12 + return stackView + }() + + private lazy var buyView: NRCoinsPackBuyView = { + let view = NRCoinsPackBuyView() + view.buyFinishHandle = { [weak self] in + self?.requestCoinsPackData() + } + return view + }() + + private lazy var claimView: NRCoinsPackClaimView = { + let view = NRCoinsPackClaimView() + view.clickClaimButton = { [weak self] id in + guard let self = self else { return } + self.requestReceiveCoins(id) + } + return view + }() + + private lazy var tipView: UIView = { + let view = UIView() + view.addSubview(tipTitleLabel) + view.addSubview(tipTextLabel) + + tipTitleLabel.snp.makeConstraints { make in + make.top.equalToSuperview() + make.left.equalToSuperview().offset(16) + } + + tipTextLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(16) + make.right.lessThanOrEqualToSuperview().offset(-16) + make.top.equalTo(tipTitleLabel.snp.bottom).offset(4) + make.bottom.equalToSuperview() + } + return view + }() + + private lazy var tipTitleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .medium) + label.textColor = .black.withAlphaComponent(0.25) + label.text = "coins_pack_tips_title".localized + return label + }() + + private lazy var tipTextLabel: UILabel = { + let att = NSMutableAttributedString(string: "coins_pack_tips".localized) + att.yy_lineSpacing = 3 + + let label = UILabel() + label.font = .font(ofSize: 10, weight: .regular) + label.textColor = .black.withAlphaComponent(0.25) + label.attributedText = att + label.numberOfLines = 0 + return label + }() + override func viewDidLoad() { super.viewDidLoad() self.title = "My Refills".localized configNavigationBack("arrow_left_icon_05") + payDataModel = NRIapManager.manager.payDateModel + nr_setupUI() + + updateLayout() + + requestCoinsPackData() } override func viewWillAppear(_ animated: Bool) { @@ -39,6 +134,18 @@ class NRCoinsPackViewController: NRViewController { self.navigationController?.setNavigationBarHidden(false, animated: true) self.nr_setNavigationStyle(titleColor: UINavigationBar.titleBlackColor) } + + private func updateLayout() { + stackView.nr_removeAllArrangedSubview() + + if self.claimView.dataArr.count > 0 { + stackView.addArrangedSubview(self.claimView) + } else if self.buyView.dataArr.count > 0 { + stackView.addArrangedSubview(self.buyView) + } + + stackView.addArrangedSubview(tipView) + } } @@ -48,6 +155,7 @@ extension NRCoinsPackViewController { view.addSubview(bgIconImageView) view.addSubview(scrollView) scrollView.addSubview(headerView) + scrollView.addSubview(stackView) bgIconImageView.snp.makeConstraints { make in @@ -65,6 +173,51 @@ extension NRCoinsPackViewController { make.centerX.equalToSuperview() make.top.equalToSuperview() } + + stackView.snp.makeConstraints { make in + make.left.centerX.equalToSuperview() + make.top.equalTo(headerView.snp.bottom).offset(16) + make.bottom.equalToSuperview().offset(-(UIScreen.safeBottom + 10)) + } } } + +extension NRCoinsPackViewController { + + private func requestCoinsPackData() { + NRStoreAPI.requestCoinsPackData { [weak self] model in + guard let self = self else { return } + guard let model = model else { return } + self.model = model + + if (model.receive_list?.count ?? 0) == 0 { + self.requestPayData() + } + + self.updateLayout() + } + + } + + private func requestPayData() { + + self.payDataRequest.requestProducts { [weak self] model in + guard let self = self else { return } + guard let model = model else { return } + self.payDataModel = model + + self.updateLayout() + } + } + + private func requestReceiveCoins(_ id: String?) { + NRStoreAPI.requestReceiveCoinsPackCoins(id: id) { [weak self] finish in + guard let self = self else { return } + self.requestCoinsPackData() + } + } + + + +} diff --git a/ReaderHive/Class/Store/VC/NRStoreViewController.swift b/ReaderHive/Class/Store/VC/NRStoreViewController.swift index 86d096b..283b9cf 100644 --- a/ReaderHive/Class/Store/VC/NRStoreViewController.swift +++ b/ReaderHive/Class/Store/VC/NRStoreViewController.swift @@ -44,12 +44,40 @@ class NRStoreViewController: NRViewController { } return view }() + + private lazy var tipView: UIView = { + let view = UIView() + + view.addSubview(tipTextLabel) + + + tipTextLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(16) + make.right.lessThanOrEqualToSuperview().offset(-16) + make.top.equalToSuperview() + make.bottom.equalToSuperview() + } + return view + }() + + private lazy var tipTextLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 10, weight: .regular) + label.textColor = .black.withAlphaComponent(0.5) + label.text = "store_tips".localized + label.numberOfLines = 0 + return label + }() override func viewDidLoad() { super.viewDidLoad() self.title = "Store".localized configNavigationBack("arrow_left_icon_05") + let barButtonItem = UIBarButtonItem(title: "Restore".localized, style: .plain, target: self, action: #selector(handleRestore)) + barButtonItem.tintColor = .black.withAlphaComponent(0.5) + self.navigationItem.rightBarButtonItem = barButtonItem + nr_setupUI() restore(isLoding: false) @@ -96,7 +124,7 @@ class NRStoreViewController: NRViewController { self.addVipView() self.addCoinsView() } -// self.stackView.addArrangedSubview(tipView) + self.stackView.addArrangedSubview(tipView) } private func addCoinsView() { diff --git a/ReaderHive/Libs/IAP/NRPayDataRequest.swift b/ReaderHive/Libs/IAP/NRPayDataRequest.swift index 84ee8f6..a802305 100644 --- a/ReaderHive/Libs/IAP/NRPayDataRequest.swift +++ b/ReaderHive/Libs/IAP/NRPayDataRequest.swift @@ -11,12 +11,11 @@ internal import StoreKit class NRPayDataRequest: NSObject { private var oldTemplateModel: NRPayDateModel? - private(set) var newTemplateModel: NRPayDateModel? -// private var payAlertModel: NRPayAlertModel? + private var payAlertModel: NRPayAlertModel? private var completerBlock: ((_ model: NRPayDateModel?) -> Void)? -// private var payAlertBlock: ((_ model: FAPayAlertModel?) -> Void)? + private var payAlertBlock: ((_ model: NRPayAlertModel?) -> Void)? private var isLoding = false private var isToast = false @@ -80,26 +79,26 @@ class NRPayDataRequest: NSObject { // } ///挽留信息 -// func requestVipRetainPayInfo(completer: ((_ model: FAPayAlertModel?) -> Void)?) { -// self.completerBlock = nil -// self.payAlertBlock = completer -// -// FAStoreAPI.requestVipRetainPayInfo { [weak self] model in -// guard let self = self else { return } -// guard let model = model else { -// self.payAlertBlock?(nil) -// return -// } -// self.payAlertModel = model -// -// let productId = FAIapManager.manager.getProductId(templateId: model.info?.ios_template_id) ?? "" -// -// let set = Set([productId]) -// let productsRequest = SKProductsRequest(productIdentifiers: set) -// productsRequest.delegate = self -// productsRequest.start() -// } -// } + func requestVipRetainPayInfo(completer: ((_ model: NRPayAlertModel?) -> Void)?) { + self.completerBlock = nil + self.payAlertBlock = completer + + NRStoreAPI.requestVipRetainPayInfo { [weak self] model in + guard let self = self else { return } + guard let model = model else { + self.payAlertBlock?(nil) + return + } + self.payAlertModel = model + + let productId = NRIapManager.manager.getProductId(templateId: model.info?.ios_template_id) ?? "" + + let set = Set([productId]) + let productsRequest = SKProductsRequest(productIdentifiers: set) + productsRequest.delegate = self + productsRequest.start() + } + } } //MARK: SKProductsRequestDelegate @@ -148,31 +147,29 @@ extension NRPayDataRequest: SKProductsRequestDelegate { templateModel.list_coins = newCoinList templateModel.list_sub_vip = newVipList - self.newTemplateModel = templateModel NRIapManager.manager.payDateModel = templateModel DispatchQueue.main.async { block(templateModel) } + } else if let block = self.payAlertBlock { + guard let coinalertModel = self.payAlertModel else { return } + let productId = NRIapManager.manager.getProductId(templateId: coinalertModel.info?.ios_template_id) ?? "" + + for product in products { + if productId == product.productIdentifier { + coinalertModel.info?.price = product.price.stringValue + coinalertModel.info?.currency = product.priceLocale.currencySymbol + coinalertModel.info?.product = product + break + } + } + + DispatchQueue.main.async { + block(coinalertModel) + } } -// else if let block = self.payAlertBlock { -// guard let coinalertModel = self.payAlertModel else { return } -// let productId = FAIapManager.manager.getProductId(templateId: coinalertModel.info?.ios_template_id) ?? "" -// -// for product in products { -// if productId == product.productIdentifier { -// coinalertModel.info?.price = product.price.stringValue -// coinalertModel.info?.currency = product.priceLocale.currencySymbol -// coinalertModel.info?.product = product -// break -// } -// } -// -// DispatchQueue.main.async { -// block(coinalertModel) -// } -// } } } diff --git a/ReaderHive/Source/Assets.xcassets/Color/#FECE62.colorset/Contents.json b/ReaderHive/Source/Assets.xcassets/Color/#FECE62.colorset/Contents.json new file mode 100644 index 0000000..f7c32bf --- /dev/null +++ b/ReaderHive/Source/Assets.xcassets/Color/#FECE62.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x62", + "green" : "0xCE", + "red" : "0xFE" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ReaderHive/Source/Assets.xcassets/Color/#FFEECA.colorset/Contents.json b/ReaderHive/Source/Assets.xcassets/Color/#FFEECA.colorset/Contents.json new file mode 100644 index 0000000..cd5632b --- /dev/null +++ b/ReaderHive/Source/Assets.xcassets/Color/#FFEECA.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xCA", + "green" : "0xEE", + "red" : "0xFF" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ReaderHive/Source/Assets.xcassets/Image/bg_image_07.imageset/Contents.json b/ReaderHive/Source/Assets.xcassets/Image/bg_image_07.imageset/Contents.json new file mode 100644 index 0000000..1cb41c1 --- /dev/null +++ b/ReaderHive/Source/Assets.xcassets/Image/bg_image_07.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "背景部分@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "背景部分@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ReaderHive/Source/Assets.xcassets/Image/bg_image_07.imageset/背景部分@2x.png b/ReaderHive/Source/Assets.xcassets/Image/bg_image_07.imageset/背景部分@2x.png new file mode 100644 index 0000000..289a133 Binary files /dev/null and b/ReaderHive/Source/Assets.xcassets/Image/bg_image_07.imageset/背景部分@2x.png differ diff --git a/ReaderHive/Source/Assets.xcassets/Image/bg_image_07.imageset/背景部分@3x.png b/ReaderHive/Source/Assets.xcassets/Image/bg_image_07.imageset/背景部分@3x.png new file mode 100644 index 0000000..e6491c5 Binary files /dev/null and b/ReaderHive/Source/Assets.xcassets/Image/bg_image_07.imageset/背景部分@3x.png differ diff --git a/ReaderHive/Source/Assets.xcassets/Image/calendar_icon_03.imageset/Contents.json b/ReaderHive/Source/Assets.xcassets/Image/calendar_icon_03.imageset/Contents.json new file mode 100644 index 0000000..f714916 --- /dev/null +++ b/ReaderHive/Source/Assets.xcassets/Image/calendar_icon_03.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "插画@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "插画@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ReaderHive/Source/Assets.xcassets/Image/calendar_icon_03.imageset/插画@2x.png b/ReaderHive/Source/Assets.xcassets/Image/calendar_icon_03.imageset/插画@2x.png new file mode 100644 index 0000000..fc4a439 Binary files /dev/null and b/ReaderHive/Source/Assets.xcassets/Image/calendar_icon_03.imageset/插画@2x.png differ diff --git a/ReaderHive/Source/Assets.xcassets/Image/calendar_icon_03.imageset/插画@3x.png b/ReaderHive/Source/Assets.xcassets/Image/calendar_icon_03.imageset/插画@3x.png new file mode 100644 index 0000000..16d6702 Binary files /dev/null and b/ReaderHive/Source/Assets.xcassets/Image/calendar_icon_03.imageset/插画@3x.png differ diff --git a/ReaderHive/Source/Assets.xcassets/Image/close_icon_03.imageset/Contents.json b/ReaderHive/Source/Assets.xcassets/Image/close_icon_03.imageset/Contents.json new file mode 100644 index 0000000..b58aab8 --- /dev/null +++ b/ReaderHive/Source/Assets.xcassets/Image/close_icon_03.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "关闭按钮@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "关闭按钮@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ReaderHive/Source/Assets.xcassets/Image/close_icon_03.imageset/关闭按钮@2x.png b/ReaderHive/Source/Assets.xcassets/Image/close_icon_03.imageset/关闭按钮@2x.png new file mode 100644 index 0000000..1d5dcfd Binary files /dev/null and b/ReaderHive/Source/Assets.xcassets/Image/close_icon_03.imageset/关闭按钮@2x.png differ diff --git a/ReaderHive/Source/Assets.xcassets/Image/close_icon_03.imageset/关闭按钮@3x.png b/ReaderHive/Source/Assets.xcassets/Image/close_icon_03.imageset/关闭按钮@3x.png new file mode 100644 index 0000000..cc2ca7e Binary files /dev/null and b/ReaderHive/Source/Assets.xcassets/Image/close_icon_03.imageset/关闭按钮@3x.png differ diff --git a/ReaderHive/Source/Assets.xcassets/Image/coins_icon_06.imageset/Coins@2x.png b/ReaderHive/Source/Assets.xcassets/Image/coins_icon_06.imageset/Coins@2x.png new file mode 100644 index 0000000..77c0083 Binary files /dev/null and b/ReaderHive/Source/Assets.xcassets/Image/coins_icon_06.imageset/Coins@2x.png differ diff --git a/ReaderHive/Source/Assets.xcassets/Image/coins_icon_06.imageset/Coins@3x.png b/ReaderHive/Source/Assets.xcassets/Image/coins_icon_06.imageset/Coins@3x.png new file mode 100644 index 0000000..2913937 Binary files /dev/null and b/ReaderHive/Source/Assets.xcassets/Image/coins_icon_06.imageset/Coins@3x.png differ diff --git a/ReaderHive/Source/Assets.xcassets/Image/coins_icon_06.imageset/Contents.json b/ReaderHive/Source/Assets.xcassets/Image/coins_icon_06.imageset/Contents.json new file mode 100644 index 0000000..21da17f --- /dev/null +++ b/ReaderHive/Source/Assets.xcassets/Image/coins_icon_06.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Coins@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Coins@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ReaderHive/Source/en.lproj/Localizable.strings b/ReaderHive/Source/en.lproj/Localizable.strings index cdcfd1b..4a0dc6c 100644 --- a/ReaderHive/Source/en.lproj/Localizable.strings +++ b/ReaderHive/Source/en.lproj/Localizable.strings @@ -117,7 +117,27 @@ "Rewards Overview" = "Rewards Overview"; "Weekly Total" = "Weekly Total"; "Claimable Coins" = "Claimable Coins"; +"Active Refills" = "Active Refills"; +"Claim All" = "Claim All"; +"Claimed" = "Claimed"; +"Weekly Refills" = "Weekly Refills"; +"Total Reward" = "Total Reward"; +"Remaining" = "Remaining"; +"Claim" = "Claim"; +"Day" = "Day"; +"Restore" = "Restore"; +"Balance" = "Balance"; +"Daily Coins" = "Daily Coins"; +"Buy Now" = "Buy Now"; +"retain_alert_text" = "Unlock every show you love!"; + +"vip_tip_01" = "Unlimited access to all series"; + +"coins_pack_tips_title" = "Subscription Rules"; +"coins_pack_tips" = "1.Coins are delivered instantly upon purchase.
2.Daily bonus coins available from the next day.
3.All coins will be revoked when the subscription expires, including both initial and daily coins."; + +"store_tips" = "1. Coins are virtual items and cannot be refunded. Use it for this product.
2. Both the coins and the reward coins will never expire.
3. Coins will be used first when unlocking episodes. If the amount is insufficient, reward coins will automatically be used.
4. The purchase has not been credited, click torefresh.
5. For other questions, contact us via Profile > Feedback."; "pay_error_1" = "You are already a member!"; "pay_error_2" = "You have unfinished in-app purchases, please restore them first.";