This commit is contained in:
澜声世纪 2025-12-13 16:05:04 +08:00
parent 211bf5f3df
commit c943a249e5
41 changed files with 1948 additions and 118 deletions

View File

@ -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 = "<group>"; };
85606A9B2EEBE243005D989D /* NRCoinsPackHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRCoinsPackHeaderView.swift; sourceTree = "<group>"; };
85606A9E2EEBE95A005D989D /* NRDashedLineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRDashedLineView.swift; sourceTree = "<group>"; };
85CF941C2EEBFEA6006467E3 /* NRCoinsPackModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRCoinsPackModel.swift; sourceTree = "<group>"; };
85CF941E2EEBFECF006467E3 /* NRCoinsPackReceiveModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRCoinsPackReceiveModel.swift; sourceTree = "<group>"; };
85CF94202EEC050D006467E3 /* NRCoinsPackBuyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRCoinsPackBuyView.swift; sourceTree = "<group>"; };
85CF94222EEC08F4006467E3 /* NRCoinsPackClaimView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRCoinsPackClaimView.swift; sourceTree = "<group>"; };
85CF94242EEC09AE006467E3 /* NRCoinsPackClaimCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRCoinsPackClaimCell.swift; sourceTree = "<group>"; };
85CF94262EED1734006467E3 /* NRHomeCoinsPackButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRHomeCoinsPackButton.swift; sourceTree = "<group>"; };
85CF94282EED4664006467E3 /* NRVipRetainAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRVipRetainAlert.swift; sourceTree = "<group>"; };
85CF942A2EED47F2006467E3 /* NRVipRetainItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRVipRetainItemView.swift; sourceTree = "<group>"; };
85CF942C2EED5204006467E3 /* NRPayAlertModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRPayAlertModel.swift; sourceTree = "<group>"; };
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 = "<group>"; };
F34348AE2ED5B85300AA7E70 /* NRExploreViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRExploreViewController.swift; sourceTree = "<group>"; };
F34348B02ED5B9A400AA7E70 /* NRExploreNovelViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRExploreNovelViewController.swift; sourceTree = "<group>"; };
@ -501,6 +519,7 @@
F34349042ED9442300AA7E70 /* NRReadPageModel.swift */,
F34990BE2EDEDDCF0039E939 /* NRCategoryModel.swift */,
F34991282EE285660039E939 /* NRShowRecommendPop.swift */,
85CF942C2EED5204006467E3 /* NRPayAlertModel.swift */,
);
path = M;
sourceTree = "<group>";
@ -515,6 +534,8 @@
F34348E02ED70A2200AA7E70 /* NRNovelDetailHeaderView+NovelCoverInfo.swift */,
F34348E22ED70D2300AA7E70 /* NRNovelDetailHeaderView+Data.swift */,
F3B859302EE66B950095A9CC /* NRDetailRechargeView.swift */,
85CF94282EED4664006467E3 /* NRVipRetainAlert.swift */,
85CF942A2EED47F2006467E3 /* NRVipRetainItemView.swift */,
);
path = V;
sourceTree = "<group>";
@ -797,6 +818,7 @@
0373D95D2ED59C430017DCC7 /* NRSearchResultView.swift */,
0373D95F2ED59DA10017DCC7 /* NRSearchResultCell.swift */,
F34990BC2EDEC24E0039E939 /* NRStarGradeView.swift */,
85CF94262EED1734006467E3 /* NRHomeCoinsPackButton.swift */,
);
path = V;
sourceTree = "<group>";
@ -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 = "<group>";
@ -1102,6 +1127,8 @@
F3B859802EE9716E0095A9CC /* NRBuyRecordsModel.swift */,
F3B8598A2EEA51540095A9CC /* NRRewardCoinsModel.swift */,
F3B859922EEA63CD0095A9CC /* NROrderRecordsModel.swift */,
85CF941C2EEBFEA6006467E3 /* NRCoinsPackModel.swift */,
85CF941E2EEBFECF006467E3 /* NRCoinsPackReceiveModel.swift */,
);
path = M;
sourceTree = "<group>";
@ -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 */,

View File

@ -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<NRCoinsPackModel>) 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<String>) 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<NRPayAlertModel>) in
if let _ = response.data?.info {
completer?(response.data)
} else {
completer?(nil)
}
}
}
}

View File

@ -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"

View File

@ -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))
}
//

View File

@ -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()
}
}

View File

@ -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)
}
}
}

View File

@ -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

View File

@ -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)
}
}
}

View File

@ -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()
}

View File

@ -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?
}

View File

@ -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))
}
}
}

View File

@ -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()
}
}
}

View File

@ -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)
}
}
}

View File

@ -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() {

View File

@ -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()
}
}

View File

@ -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

View File

@ -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]?
}

View File

@ -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?
}

View File

@ -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)
}
}

View File

@ -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")
}
}
}

View File

@ -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
}
}

View File

@ -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()
}

View File

@ -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 {

View File

@ -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()
}
}
}

View File

@ -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() {

View File

@ -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)
// }
// }
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 KiB

View File

@ -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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

View File

@ -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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -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
}
}

View File

@ -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.<br>2.Daily bonus coins available from the next day.<br>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.<br>2. Both the coins and the reward coins will never expire.<br>3. Coins will be used first when unlocking episodes. If the amount is insufficient, reward coins will automatically be used.<br>4. The purchase has not been credited, click <Restore> torefresh.<br>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.";