新增挽留弹窗
This commit is contained in:
parent
b3e52fac66
commit
5d55036f74
@ -91,6 +91,8 @@
|
|||||||
85859E662EF3FC6E0020D282 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85859E632EF3FC6E0020D282 /* NotificationService.swift */; };
|
85859E662EF3FC6E0020D282 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85859E632EF3FC6E0020D282 /* NotificationService.swift */; };
|
||||||
85859E682EFCD1D80020D282 /* NRHomeMustReadTodayTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85859E672EFCD1D80020D282 /* NRHomeMustReadTodayTransformer.swift */; };
|
85859E682EFCD1D80020D282 /* NRHomeMustReadTodayTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85859E672EFCD1D80020D282 /* NRHomeMustReadTodayTransformer.swift */; };
|
||||||
85ACA3F92F28A7CD009D52B0 /* NRNovelReadViewModel+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85ACA3F82F28A7C8009D52B0 /* NRNovelReadViewModel+View.swift */; };
|
85ACA3F92F28A7CD009D52B0 /* NRNovelReadViewModel+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85ACA3F82F28A7C8009D52B0 /* NRNovelReadViewModel+View.swift */; };
|
||||||
|
85B5B3982F30732800700E83 /* NRPayRetainAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85B5B3972F30732800700E83 /* NRPayRetainAlert.swift */; };
|
||||||
|
85B5B39A2F30790600700E83 /* NRBorderLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85B5B3992F30790600700E83 /* NRBorderLabel.swift */; };
|
||||||
85C1786B2F050AA400A8A76E /* Poppins-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 85C178682F050AA400A8A76E /* Poppins-Medium.ttf */; };
|
85C1786B2F050AA400A8A76E /* Poppins-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 85C178682F050AA400A8A76E /* Poppins-Medium.ttf */; };
|
||||||
85C1786C2F050AA400A8A76E /* Poppins-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 85C1786A2F050AA400A8A76E /* Poppins-SemiBold.ttf */; };
|
85C1786C2F050AA400A8A76E /* Poppins-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 85C1786A2F050AA400A8A76E /* Poppins-SemiBold.ttf */; };
|
||||||
85C1786D2F050AA400A8A76E /* Poppins-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 85C178672F050AA400A8A76E /* Poppins-Bold.ttf */; };
|
85C1786D2F050AA400A8A76E /* Poppins-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 85C178672F050AA400A8A76E /* Poppins-Bold.ttf */; };
|
||||||
@ -602,6 +604,8 @@
|
|||||||
85859E632EF3FC6E0020D282 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
|
85859E632EF3FC6E0020D282 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
|
||||||
85859E672EFCD1D80020D282 /* NRHomeMustReadTodayTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRHomeMustReadTodayTransformer.swift; sourceTree = "<group>"; };
|
85859E672EFCD1D80020D282 /* NRHomeMustReadTodayTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRHomeMustReadTodayTransformer.swift; sourceTree = "<group>"; };
|
||||||
85ACA3F82F28A7C8009D52B0 /* NRNovelReadViewModel+View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NRNovelReadViewModel+View.swift"; sourceTree = "<group>"; };
|
85ACA3F82F28A7C8009D52B0 /* NRNovelReadViewModel+View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NRNovelReadViewModel+View.swift"; sourceTree = "<group>"; };
|
||||||
|
85B5B3972F30732800700E83 /* NRPayRetainAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRPayRetainAlert.swift; sourceTree = "<group>"; };
|
||||||
|
85B5B3992F30790600700E83 /* NRBorderLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRBorderLabel.swift; sourceTree = "<group>"; };
|
||||||
85C178672F050AA400A8A76E /* Poppins-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Poppins-Bold.ttf"; sourceTree = "<group>"; };
|
85C178672F050AA400A8A76E /* Poppins-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Poppins-Bold.ttf"; sourceTree = "<group>"; };
|
||||||
85C178682F050AA400A8A76E /* Poppins-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Poppins-Medium.ttf"; sourceTree = "<group>"; };
|
85C178682F050AA400A8A76E /* Poppins-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Poppins-Medium.ttf"; sourceTree = "<group>"; };
|
||||||
85C178692F050AA400A8A76E /* Poppins-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Poppins-Regular.ttf"; sourceTree = "<group>"; };
|
85C178692F050AA400A8A76E /* Poppins-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Poppins-Regular.ttf"; sourceTree = "<group>"; };
|
||||||
@ -1080,6 +1084,7 @@
|
|||||||
F3B859302EE66B950095A9CC /* NRDetailRechargeView.swift */,
|
F3B859302EE66B950095A9CC /* NRDetailRechargeView.swift */,
|
||||||
85CF94282EED4664006467E3 /* NRVipRetainAlert.swift */,
|
85CF94282EED4664006467E3 /* NRVipRetainAlert.swift */,
|
||||||
85CF942A2EED47F2006467E3 /* NRVipRetainItemView.swift */,
|
85CF942A2EED47F2006467E3 /* NRVipRetainItemView.swift */,
|
||||||
|
85B5B3972F30732800700E83 /* NRPayRetainAlert.swift */,
|
||||||
);
|
);
|
||||||
path = V;
|
path = V;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -1378,6 +1383,7 @@
|
|||||||
0373D95F2ED59DA10017DCC7 /* NRSearchResultCell.swift */,
|
0373D95F2ED59DA10017DCC7 /* NRSearchResultCell.swift */,
|
||||||
F34990BC2EDEC24E0039E939 /* NRStarGradeView.swift */,
|
F34990BC2EDEC24E0039E939 /* NRStarGradeView.swift */,
|
||||||
85CF94262EED1734006467E3 /* NRHomeCoinsPackButton.swift */,
|
85CF94262EED1734006467E3 /* NRHomeCoinsPackButton.swift */,
|
||||||
|
85B5B3992F30790600700E83 /* NRBorderLabel.swift */,
|
||||||
);
|
);
|
||||||
path = V;
|
path = V;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -2804,6 +2810,7 @@
|
|||||||
85CF95E02EF3CC93006467E3 /* QRLikeDataView.swift in Sources */,
|
85CF95E02EF3CC93006467E3 /* QRLikeDataView.swift in Sources */,
|
||||||
85CF95E12EF3CC93006467E3 /* YLOBaitingView.swift in Sources */,
|
85CF95E12EF3CC93006467E3 /* YLOBaitingView.swift in Sources */,
|
||||||
85CF95E22EF3CC93006467E3 /* UPPackCell.swift in Sources */,
|
85CF95E22EF3CC93006467E3 /* UPPackCell.swift in Sources */,
|
||||||
|
85B5B39A2F30790600700E83 /* NRBorderLabel.swift in Sources */,
|
||||||
85CF95E32EF3CC93006467E3 /* NVDModalTextButton.swift in Sources */,
|
85CF95E32EF3CC93006467E3 /* NVDModalTextButton.swift in Sources */,
|
||||||
85CF95E42EF3CC93006467E3 /* NAStream.swift in Sources */,
|
85CF95E42EF3CC93006467E3 /* NAStream.swift in Sources */,
|
||||||
85CF95E52EF3CC93006467E3 /* UWQCheckController.swift in Sources */,
|
85CF95E52EF3CC93006467E3 /* UWQCheckController.swift in Sources */,
|
||||||
@ -2991,6 +2998,7 @@
|
|||||||
F3B8597D2EE9627B0095A9CC /* NRWalletHeaderView.swift in Sources */,
|
F3B8597D2EE9627B0095A9CC /* NRWalletHeaderView.swift in Sources */,
|
||||||
039810872ED057260006E317 /* NRUserDefaultsKey.swift in Sources */,
|
039810872ED057260006E317 /* NRUserDefaultsKey.swift in Sources */,
|
||||||
F34349222EDD227A00AA7E70 /* NRReadSettingSpacingView.swift in Sources */,
|
F34349222EDD227A00AA7E70 /* NRReadSettingSpacingView.swift in Sources */,
|
||||||
|
85B5B3982F30732800700E83 /* NRPayRetainAlert.swift in Sources */,
|
||||||
85CF94522EF14FBD006467E3 /* NROpenAppManager.swift in Sources */,
|
85CF94522EF14FBD006467E3 /* NROpenAppManager.swift in Sources */,
|
||||||
85606A902EEBA8F3005D989D /* NRCoinsPackConfirmView.swift in Sources */,
|
85606A902EEBA8F3005D989D /* NRCoinsPackConfirmView.swift in Sources */,
|
||||||
F34990F32EE02FD60039E939 /* NRNovelReadFinishViewController.swift in Sources */,
|
F34990F32EE02FD60039E939 /* NRNovelReadFinishViewController.swift in Sources */,
|
||||||
@ -3093,7 +3101,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.9;
|
MARKETING_VERSION = 1.1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lssj.ReaderHive;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lssj.ReaderHive;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@ -3138,7 +3146,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.9;
|
MARKETING_VERSION = 1.1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lssj.ReaderHive;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lssj.ReaderHive;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
|||||||
@ -180,6 +180,26 @@ struct NRStoreAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///挽留支付项
|
||||||
|
static func requestPayRetainInfo(completer: ((_ model: NRPayDateModel?) -> Void)?) {
|
||||||
|
var param = NRNetwork.Parameters(path: "/payRetrieveSettings")
|
||||||
|
param.method = .post
|
||||||
|
param.isLoding = false
|
||||||
|
param.isToast = false
|
||||||
|
param.parameters = [
|
||||||
|
"discount" : "1",
|
||||||
|
"purchases_token" : JXIAPManager.manager.getAppStoreReceipt() ?? "",
|
||||||
|
]
|
||||||
|
|
||||||
|
NRNetwork.request(parameters: param) { (response: NRNetwork.Response<NRPayDateModel>) in
|
||||||
|
if (response.data?.list_coins?.count ?? 0) > 0 {
|
||||||
|
completer?(response.data)
|
||||||
|
} else {
|
||||||
|
completer?(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///获取金币包可领取信息
|
///获取金币包可领取信息
|
||||||
static func requestCoinBagCanReceiveInfo() async -> NRCoinPackCanReceiveModel? {
|
static func requestCoinBagCanReceiveInfo() async -> NRCoinPackCanReceiveModel? {
|
||||||
await withCheckedContinuation { continuation in
|
await withCheckedContinuation { continuation in
|
||||||
|
|||||||
74
ReaderHive/Class/Home/V/NRBorderLabel.swift
Normal file
74
ReaderHive/Class/Home/V/NRBorderLabel.swift
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
//
|
||||||
|
// NRBorderLabel.swift
|
||||||
|
// ReaderHive
|
||||||
|
//
|
||||||
|
// Created by 澜声世纪 on 2026/2/2.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class NRBorderLabel: UILabel {
|
||||||
|
|
||||||
|
var borderLineWidth: CGFloat = 0
|
||||||
|
var borderImage: UIImage?
|
||||||
|
var borderColor: UIColor?
|
||||||
|
var borderColors: [CGColor]?
|
||||||
|
var borderStartPoint: CGPoint?
|
||||||
|
var borderEndPoint: CGPoint?
|
||||||
|
|
||||||
|
|
||||||
|
var textColorImage: UIImage?
|
||||||
|
var vp_textColor: UIColor?
|
||||||
|
var textColors: [CGColor]?
|
||||||
|
var textStartPoint: CGPoint?
|
||||||
|
var textEndPoint: CGPoint?
|
||||||
|
|
||||||
|
override var intrinsicContentSize: CGSize {
|
||||||
|
let size = super.intrinsicContentSize
|
||||||
|
return .init(width: size.width + borderLineWidth, height: size.height + borderLineWidth)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func drawText(in rect: CGRect) {
|
||||||
|
guard let context = UIGraphicsGetCurrentContext(),
|
||||||
|
let text = self.text,
|
||||||
|
let font = self.font else {
|
||||||
|
super.drawText(in: rect)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let attributes: [NSAttributedString.Key: Any] = [
|
||||||
|
.font: font
|
||||||
|
]
|
||||||
|
|
||||||
|
let textSize = text.size(withAttributes: attributes)
|
||||||
|
let x = borderLineWidth / 2
|
||||||
|
let y = borderLineWidth / 2
|
||||||
|
let textRect = CGRect(x: x, y: y, width: textSize.width, height: textSize.height)
|
||||||
|
let size = CGSizeMake(textSize.width + borderLineWidth / 2, textSize.height + borderLineWidth / 2)
|
||||||
|
|
||||||
|
context.setLineWidth(borderLineWidth)
|
||||||
|
context.setLineJoin(.round)
|
||||||
|
// 先描边
|
||||||
|
context.setTextDrawingMode(.stroke)
|
||||||
|
if let image = self.borderImage {
|
||||||
|
self.textColor = UIColor(patternImage: image.nr_resized(to: rect.size))
|
||||||
|
} else if let colors = self.borderColors, let startPoint = self.borderStartPoint, let endPoine = self.borderEndPoint {
|
||||||
|
self.textColor = UIColor(patternImage: UIImage.nr_getGradientImage(size: size, colors: colors, startPoint: startPoint, endPoint: endPoine))
|
||||||
|
} else {
|
||||||
|
self.textColor = self.borderColor ?? .clear
|
||||||
|
}
|
||||||
|
super.drawText(in: textRect)
|
||||||
|
|
||||||
|
// 再填充
|
||||||
|
context.setTextDrawingMode(.fill)
|
||||||
|
if let image = self.textColorImage {
|
||||||
|
self.textColor = UIColor(patternImage: image.nr_resized(to: rect.size))
|
||||||
|
} else if let colors = self.textColors, let startPoint = self.textStartPoint, let endPoine = self.textEndPoint {
|
||||||
|
self.textColor = UIColor(patternImage: UIImage.nr_getGradientImage(size: size, colors: colors, startPoint: startPoint, endPoint: endPoine))
|
||||||
|
} else {
|
||||||
|
self.textColor = vp_textColor // 填充色
|
||||||
|
}
|
||||||
|
super.drawText(in: textRect)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
541
ReaderHive/Class/Novel/V/NRPayRetainAlert.swift
Normal file
541
ReaderHive/Class/Novel/V/NRPayRetainAlert.swift
Normal file
@ -0,0 +1,541 @@
|
|||||||
|
//
|
||||||
|
// NRPayRetainAlert.swift
|
||||||
|
// ReaderHive
|
||||||
|
//
|
||||||
|
// Created by 澜声世纪 on 2026/2/2.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import SnapKit
|
||||||
|
import YYCategories
|
||||||
|
import YYText
|
||||||
|
|
||||||
|
class NRPayRetainAlert: NRBaseAlert {
|
||||||
|
|
||||||
|
var buyFinishHandle: (() -> Void)?
|
||||||
|
|
||||||
|
var model: NRPayDateModel? {
|
||||||
|
didSet {
|
||||||
|
self.collectionView.reloadData()
|
||||||
|
self.titleLabel.text = model?.retrieve_lang?.title
|
||||||
|
self.countdownView.model = model
|
||||||
|
|
||||||
|
bonusTagView.update(text: model?.retrieve_lang?.subtitle ?? "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var novelId: String?
|
||||||
|
|
||||||
|
var videoInfo: NRReadChapterCatalogModel?
|
||||||
|
|
||||||
|
private var selectedIndex = -1
|
||||||
|
|
||||||
|
private lazy var bgView: UIImageView = {
|
||||||
|
let imageView = UIImageView(image: UIImage(named: "pay_retain_bg_image"))
|
||||||
|
imageView.setContentHuggingPriority(.required, for: .horizontal)
|
||||||
|
imageView.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||||
|
imageView.isUserInteractionEnabled = true
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
|
|
||||||
|
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_01"), for: .normal)
|
||||||
|
return button
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var titleLabel: NRBorderLabel = {
|
||||||
|
let label = NRBorderLabel()
|
||||||
|
label.vp_textColor = .FFEFD_4
|
||||||
|
label.borderImage = UIImage(named: "gradient_color_01")
|
||||||
|
label.borderLineWidth = 12
|
||||||
|
label.font = .font(ofSize: 42, weight: .init(900)).withBoldItalic()
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var countdownView: PayRetainCountdownView = {
|
||||||
|
let view = PayRetainCountdownView()
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var bonusTagView: PayRetainBonusTagView = {
|
||||||
|
let view = PayRetainBonusTagView()
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var collectionView: UICollectionView = {
|
||||||
|
let layout = UICollectionViewFlowLayout()
|
||||||
|
layout.scrollDirection = .vertical
|
||||||
|
layout.minimumLineSpacing = 7
|
||||||
|
layout.minimumInteritemSpacing = 0
|
||||||
|
layout.itemSize = .init(width: 300, height: 68)
|
||||||
|
|
||||||
|
let view = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
||||||
|
view.backgroundColor = .clear
|
||||||
|
view.isScrollEnabled = true
|
||||||
|
view.showsVerticalScrollIndicator = false
|
||||||
|
view.dataSource = self
|
||||||
|
view.delegate = self
|
||||||
|
view.register(NRPayRetainPackCell.self, forCellWithReuseIdentifier: NRPayRetainPackCell.reuseIdentifier)
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
nr_setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension NRPayRetainAlert {
|
||||||
|
|
||||||
|
private func nr_setupUI() {
|
||||||
|
contentView.addSubview(bgView)
|
||||||
|
contentView.addSubview(closeButton)
|
||||||
|
bgView.addSubview(titleLabel)
|
||||||
|
bgView.addSubview(countdownView)
|
||||||
|
bgView.addSubview(bonusTagView)
|
||||||
|
bgView.addSubview(collectionView)
|
||||||
|
|
||||||
|
bgView.snp.makeConstraints { make in
|
||||||
|
make.left.right.top.equalToSuperview()
|
||||||
|
make.bottom.equalToSuperview().offset(-70)
|
||||||
|
}
|
||||||
|
|
||||||
|
closeButton.snp.makeConstraints { make in
|
||||||
|
make.centerX.equalToSuperview()
|
||||||
|
// make.top.equalTo(bgView.snp.bottom).offset(10)
|
||||||
|
make.bottom.equalToSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
titleLabel.snp.makeConstraints { make in
|
||||||
|
make.centerX.equalToSuperview()
|
||||||
|
make.right.lessThanOrEqualToSuperview()
|
||||||
|
make.top.equalToSuperview().offset(67)
|
||||||
|
}
|
||||||
|
|
||||||
|
countdownView.snp.makeConstraints { make in
|
||||||
|
make.centerX.equalToSuperview()
|
||||||
|
make.top.equalTo(titleLabel.snp.bottom).offset(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
bonusTagView.snp.makeConstraints { make in
|
||||||
|
make.top.equalTo(titleLabel.snp.bottom).offset(38)
|
||||||
|
make.centerX.equalToSuperview()
|
||||||
|
make.right.lessThanOrEqualToSuperview().offset(-10)
|
||||||
|
}
|
||||||
|
|
||||||
|
collectionView.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview().offset(10)
|
||||||
|
make.right.equalToSuperview().offset(-10)
|
||||||
|
make.top.equalTo(bonusTagView.snp.bottom).offset(10)
|
||||||
|
make.bottom.equalToSuperview().offset(-10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - UICollectionViewDataSource & Delegate
|
||||||
|
extension NRPayRetainAlert: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
|
||||||
|
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
|
return self.model?.list_coins?.count ?? 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||||
|
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: NRPayRetainPackCell.reuseIdentifier, for: indexPath) as! NRPayRetainPackCell
|
||||||
|
let item = self.model?.list_coins?[indexPath.row]
|
||||||
|
cell.configure(with: item, isSelected: indexPath.item == selectedIndex)
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||||
|
selectedIndex = indexPath.item
|
||||||
|
collectionView.reloadData()
|
||||||
|
|
||||||
|
guard let item = self.model?.list_coins?[indexPath.row] else { return }
|
||||||
|
|
||||||
|
NRIapManager.manager.start(model: item, shortPlayId: self.novelId, videoId: self.videoInfo?.id) { [weak self] finish in
|
||||||
|
guard let self = self else { return }
|
||||||
|
if finish {
|
||||||
|
Task {
|
||||||
|
await NRLoginManager.manager.updateUserInfo()
|
||||||
|
}
|
||||||
|
self.dismiss()
|
||||||
|
self.buyFinishHandle?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension NRPayRetainAlert {
|
||||||
|
final class PayRetainCountdownView: UIView {
|
||||||
|
var model: NRPayDateModel? {
|
||||||
|
didSet {
|
||||||
|
prefixLabel.text = model?.retrieve_lang?.remaining_time
|
||||||
|
suffixLabel.text = model?.retrieve_lang?.miss_out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if DEBUG
|
||||||
|
private static let defaultCountdownSeconds = 10
|
||||||
|
#else
|
||||||
|
private static let defaultCountdownSeconds = 4 * 60 * 60
|
||||||
|
#endif
|
||||||
|
private static let countdownEndTimeKey = "NRPayRetainCountdownEndTime"
|
||||||
|
private var countdownTimer: Timer?
|
||||||
|
private var remainingSeconds: Int = 0
|
||||||
|
|
||||||
|
private lazy var resolveEndTime: TimeInterval = {
|
||||||
|
let defaults = UserDefaults.standard
|
||||||
|
let now = Date().timeIntervalSince1970
|
||||||
|
let storedEndTime = defaults.double(forKey: Self.countdownEndTimeKey)
|
||||||
|
if storedEndTime > now {
|
||||||
|
return storedEndTime
|
||||||
|
}
|
||||||
|
let newEndTime = now + TimeInterval(Self.defaultCountdownSeconds)
|
||||||
|
defaults.set(newEndTime, forKey: Self.countdownEndTimeKey)
|
||||||
|
return newEndTime
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let prefixLabel = UILabel()
|
||||||
|
private let suffixLabel = UILabel()
|
||||||
|
private let timeStackView = UIStackView()
|
||||||
|
private let hourView = TimeBoxView()
|
||||||
|
private let minuteView = TimeBoxView()
|
||||||
|
private let secondView = TimeBoxView()
|
||||||
|
private let colonLabel1 = UILabel()
|
||||||
|
private let colonLabel2 = UILabel()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
setupView()
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
stopCountdown()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func didMoveToWindow() {
|
||||||
|
super.didMoveToWindow()
|
||||||
|
if window != nil {
|
||||||
|
startCountdown()
|
||||||
|
} else {
|
||||||
|
stopCountdown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupView() {
|
||||||
|
let mainStack = UIStackView()
|
||||||
|
mainStack.axis = .horizontal
|
||||||
|
mainStack.alignment = .center
|
||||||
|
mainStack.spacing = 5
|
||||||
|
|
||||||
|
prefixLabel.font = .font(ofSize: 12, weight: .medium)
|
||||||
|
prefixLabel.textColor = UIColor._0_F_0_F_0_F
|
||||||
|
|
||||||
|
suffixLabel.font = .font(ofSize: 12, weight: .medium)
|
||||||
|
suffixLabel.textColor = UIColor._0_F_0_F_0_F
|
||||||
|
|
||||||
|
timeStackView.axis = .horizontal
|
||||||
|
timeStackView.alignment = .center
|
||||||
|
timeStackView.spacing = 3
|
||||||
|
|
||||||
|
colonLabel1.font = .font(ofSize: 12, weight: .medium)
|
||||||
|
colonLabel1.textColor = UIColor._0_F_0_F_0_F
|
||||||
|
colonLabel1.text = ":"
|
||||||
|
|
||||||
|
colonLabel2.font = .font(ofSize: 12, weight: .medium)
|
||||||
|
colonLabel2.textColor = UIColor._0_F_0_F_0_F
|
||||||
|
colonLabel2.text = ":"
|
||||||
|
|
||||||
|
timeStackView.addArrangedSubview(hourView)
|
||||||
|
timeStackView.addArrangedSubview(colonLabel1)
|
||||||
|
timeStackView.addArrangedSubview(minuteView)
|
||||||
|
timeStackView.addArrangedSubview(colonLabel2)
|
||||||
|
timeStackView.addArrangedSubview(secondView)
|
||||||
|
|
||||||
|
addSubview(mainStack)
|
||||||
|
mainStack.addArrangedSubview(prefixLabel)
|
||||||
|
mainStack.addArrangedSubview(timeStackView)
|
||||||
|
mainStack.addArrangedSubview(suffixLabel)
|
||||||
|
|
||||||
|
mainStack.snp.makeConstraints { make in
|
||||||
|
make.edges.equalToSuperview()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 每次展示时从 4 小时开始倒计时
|
||||||
|
private func startCountdown() {
|
||||||
|
stopCountdown()
|
||||||
|
let endTime = self.resolveEndTime
|
||||||
|
remainingSeconds = max(0, Int(endTime - Date().timeIntervalSince1970))
|
||||||
|
updateTimeLabels()
|
||||||
|
guard remainingSeconds > 0 else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
countdownTimer = Timer.scheduledTimer(timeInterval: 1,
|
||||||
|
target: YYTextWeakProxy(target: self),
|
||||||
|
selector: #selector(handleCountdownTimer),
|
||||||
|
userInfo: nil,
|
||||||
|
repeats: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func stopCountdown() {
|
||||||
|
countdownTimer?.invalidate()
|
||||||
|
countdownTimer = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func handleCountdownTimer() {
|
||||||
|
guard remainingSeconds > 0 else {
|
||||||
|
updateTimeLabels()
|
||||||
|
stopCountdown()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
remainingSeconds -= 1
|
||||||
|
updateTimeLabels()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private func updateTimeLabels() {
|
||||||
|
if remainingSeconds <= 0 {
|
||||||
|
self.isHidden = true
|
||||||
|
self.stopCountdown()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let hours = remainingSeconds / 3600
|
||||||
|
let minutes = (remainingSeconds % 3600) / 60
|
||||||
|
let seconds = remainingSeconds % 60
|
||||||
|
hourView.setText(String(format: "%02d", hours))
|
||||||
|
minuteView.setText(String(format: "%02d", minutes))
|
||||||
|
secondView.setText(String(format: "%02d", seconds))
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class TimeBoxView: UIView {
|
||||||
|
private let label = UILabel()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
setupView()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func setText(_ text: String) {
|
||||||
|
label.text = text
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupView() {
|
||||||
|
backgroundColor = UIColor._0_F_0_F_0_F
|
||||||
|
layer.cornerRadius = 4
|
||||||
|
layer.masksToBounds = true
|
||||||
|
|
||||||
|
label.font = .font(ofSize: 12, weight: .medium)
|
||||||
|
label.textColor = .white
|
||||||
|
label.textAlignment = .center
|
||||||
|
|
||||||
|
addSubview(label)
|
||||||
|
label.snp.makeConstraints { make in
|
||||||
|
make.center.equalToSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
snp.makeConstraints { make in
|
||||||
|
make.width.height.equalTo(18)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class PayRetainBonusTagView: UIView {
|
||||||
|
private let leftStar = UIImageView(image: UIImage(named: "star_icon_06"))
|
||||||
|
private let rightStar = UIImageView(image: UIImage(named: "star_icon_06"))
|
||||||
|
private let titleLabel: NRLabel = {
|
||||||
|
let label = NRLabel()
|
||||||
|
label.font = .font(ofSize: 20, weight: .bold).withBoldItalic()
|
||||||
|
label.textColorImage = UIImage(named: "gradient_color_01")
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
setupView()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新标签文案
|
||||||
|
func update(text: String) {
|
||||||
|
titleLabel.text = text
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupView() {
|
||||||
|
addSubview(leftStar)
|
||||||
|
addSubview(rightStar)
|
||||||
|
addSubview(titleLabel)
|
||||||
|
|
||||||
|
leftStar.snp.makeConstraints { make in
|
||||||
|
make.centerY.equalToSuperview()
|
||||||
|
make.left.equalToSuperview().offset(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
titleLabel.snp.makeConstraints { make in
|
||||||
|
make.top.equalToSuperview()
|
||||||
|
make.centerY.equalToSuperview()
|
||||||
|
make.left.equalTo(leftStar.snp.right).offset(5)
|
||||||
|
}
|
||||||
|
|
||||||
|
rightStar.snp.makeConstraints { make in
|
||||||
|
make.centerY.equalToSuperview()
|
||||||
|
make.left.equalTo(titleLabel.snp.right).offset(5)
|
||||||
|
make.right.equalToSuperview().offset(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Pack Cell
|
||||||
|
final class NRPayRetainPackCell: UICollectionViewCell {
|
||||||
|
static let reuseIdentifier = "cell"
|
||||||
|
|
||||||
|
private lazy var bgView: UIImageView = {
|
||||||
|
let imageView = UIImageView(image: UIImage(named: "pay_retain_cell_bg_image"))
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
|
private let selectedBorderView = UIView()
|
||||||
|
private let coinIconView = UIImageView(image: UIImage(named: "coins_icon_05"))
|
||||||
|
private let coinLabel = UILabel()
|
||||||
|
private let bonusLabel = UILabel()
|
||||||
|
|
||||||
|
|
||||||
|
private lazy var priceView: UIView = {
|
||||||
|
let view = NRGradientView()
|
||||||
|
view.colors = [UIColor.white.cgColor, UIColor.FDE_9_CB.cgColor]
|
||||||
|
view.startPoint = .init(x: 0, y: 0.5)
|
||||||
|
view.endPoint = .init(x: 1, y: 0.5)
|
||||||
|
|
||||||
|
view.layer.cornerRadius = 20
|
||||||
|
view.layer.masksToBounds = true
|
||||||
|
view.layer.borderWidth = 1
|
||||||
|
view.layer.borderColor = UIColor.black.withAlphaComponent(0.15).cgColor
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
private let priceLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.textColor = ._946_A_37
|
||||||
|
label.font = .font(ofSize: 22, weight: .bold)
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
setupView()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绑定套餐数据和选中状态
|
||||||
|
func configure(with item: NRPayItem?, isSelected: Bool) {
|
||||||
|
selectedBorderView.isHidden = !isSelected
|
||||||
|
guard let item = item else { return }
|
||||||
|
|
||||||
|
coinLabel.text = "\(item.coins ?? 0)"
|
||||||
|
bonusLabel.text = "+\((item.ext_info?.max_total_coins_pop ?? 0) - (item.ext_info?.max_total_coins ?? 0)) " + "free_coins".localized
|
||||||
|
|
||||||
|
|
||||||
|
var price = item.price
|
||||||
|
if let discountPrice = item.discount_price {
|
||||||
|
price = discountPrice
|
||||||
|
}
|
||||||
|
priceLabel.text = "\(item.currency ?? "")\(price ?? "")"
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupView() {
|
||||||
|
contentView.backgroundColor = .clear
|
||||||
|
|
||||||
|
|
||||||
|
// bgView.layer.cornerRadius = 15
|
||||||
|
// bgView.layer.masksToBounds = true
|
||||||
|
|
||||||
|
selectedBorderView.layer.cornerRadius = 12
|
||||||
|
selectedBorderView.layer.borderWidth = 2
|
||||||
|
selectedBorderView.layer.borderColor = UIColor.F_9710_D.cgColor
|
||||||
|
selectedBorderView.isHidden = true
|
||||||
|
|
||||||
|
|
||||||
|
coinIconView.contentMode = .scaleAspectFit
|
||||||
|
|
||||||
|
coinLabel.font = .font(ofSize: 22, weight: .bold)
|
||||||
|
coinLabel.textColor = ._946_A_37
|
||||||
|
|
||||||
|
bonusLabel.font = .font(ofSize: 12, weight: .medium)
|
||||||
|
bonusLabel.textColor = ._946_A_37
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let coinStack = UIStackView(arrangedSubviews: [coinIconView, coinLabel])
|
||||||
|
coinStack.axis = .horizontal
|
||||||
|
coinStack.alignment = .center
|
||||||
|
coinStack.spacing = 2
|
||||||
|
|
||||||
|
contentView.addSubview(bgView)
|
||||||
|
contentView.addSubview(selectedBorderView)
|
||||||
|
|
||||||
|
bgView.addSubview(coinStack)
|
||||||
|
bgView.addSubview(bonusLabel)
|
||||||
|
bgView.addSubview(priceView)
|
||||||
|
priceView.addSubview(priceLabel)
|
||||||
|
|
||||||
|
bgView.snp.makeConstraints { make in
|
||||||
|
make.edges.equalToSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedBorderView.snp.makeConstraints { make in
|
||||||
|
make.edges.equalToSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
coinStack.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview().offset(60)
|
||||||
|
make.top.equalToSuperview().offset(12)
|
||||||
|
}
|
||||||
|
|
||||||
|
coinIconView.snp.makeConstraints { make in
|
||||||
|
make.width.height.equalTo(22)
|
||||||
|
}
|
||||||
|
|
||||||
|
bonusLabel.snp.makeConstraints { make in
|
||||||
|
make.left.equalTo(coinStack)
|
||||||
|
make.top.equalTo(coinStack.snp.bottom).offset(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
priceView.snp.makeConstraints { make in
|
||||||
|
make.right.equalToSuperview().offset(-12)
|
||||||
|
make.centerY.equalToSuperview()
|
||||||
|
make.height.equalTo(40)
|
||||||
|
make.width.greaterThanOrEqualTo(90)
|
||||||
|
}
|
||||||
|
|
||||||
|
priceLabel.snp.makeConstraints { make in
|
||||||
|
make.center.equalToSuperview()
|
||||||
|
make.right.lessThanOrEqualToSuperview().offset(-10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -158,10 +158,10 @@ extension NRNovelReadViewModel {
|
|||||||
self.vc?.loadData()
|
self.vc?.loadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
// view.didDismissHandle = { [weak self] in
|
view.didDismissHandle = { [weak self] in
|
||||||
// guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
// self.showVipRetainAlert(catalogModel)
|
self.showPayRetainAlert(catalogModel)
|
||||||
// }
|
}
|
||||||
view.present(in: nil)
|
view.present(in: nil)
|
||||||
|
|
||||||
self.popView = view
|
self.popView = view
|
||||||
@ -190,4 +190,25 @@ extension NRNovelReadViewModel {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func showPayRetainAlert(_ catalogModel: NRReadChapterCatalogModel) {
|
||||||
|
|
||||||
|
payDataRequest = NRPayDataRequest()
|
||||||
|
|
||||||
|
payDataRequest?.requestPayRetainInfo { [weak self] model in
|
||||||
|
guard let self = self else { return }
|
||||||
|
guard let model = model else { return }
|
||||||
|
|
||||||
|
let view = NRPayRetainAlert()
|
||||||
|
view.novelId = self.novelId
|
||||||
|
view.model = model
|
||||||
|
view.videoInfo = catalogModel
|
||||||
|
view.buyFinishHandle = { [weak self] in
|
||||||
|
guard let self = self else { return }
|
||||||
|
self.targetCatalogModel = catalogModel
|
||||||
|
self.vc?.loadData()
|
||||||
|
}
|
||||||
|
view.show(in: NRTool.keyWindow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,6 +28,18 @@ class NRPayDateModel: NSObject, SmartCodable {
|
|||||||
///0: 普通金币 1:金币包模式
|
///0: 普通金币 1:金币包模式
|
||||||
var show_type: Int?
|
var show_type: Int?
|
||||||
|
|
||||||
|
var retrieve_lang: NRPayRetrieveLang?
|
||||||
|
}
|
||||||
|
|
||||||
|
class NRPayRetrieveLang: NSObject, SmartCodable {
|
||||||
|
|
||||||
|
required override init() { }
|
||||||
|
|
||||||
|
var title: String?
|
||||||
|
var remaining_time: String?
|
||||||
|
var miss_out: String?
|
||||||
|
var subtitle: String?
|
||||||
|
var claim_reward: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
class NRPayItem: NSObject, SmartCodable {
|
class NRPayItem: NSObject, SmartCodable {
|
||||||
@ -95,6 +107,16 @@ class NRPayItem: NSObject, SmartCodable {
|
|||||||
return product?.discounts
|
return product?.discounts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var discount_price: String? {
|
||||||
|
var price: String? = nil
|
||||||
|
if self.discount_type == 1, let introductoryPrice = self.introductionaryOffer {
|
||||||
|
price = introductoryPrice.price.stringValue
|
||||||
|
} else if self.discount_type == 2, let discount = self.promotionalOffers?.first {
|
||||||
|
price = discount.price.stringValue
|
||||||
|
}
|
||||||
|
return price
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static func mappingForKey() -> [SmartKeyTransformer]? {
|
static func mappingForKey() -> [SmartKeyTransformer]? {
|
||||||
return [
|
return [
|
||||||
@ -152,5 +174,6 @@ class NRPayExtInfo: NSObject, SmartCodable {
|
|||||||
var max_total_coins: Int?
|
var max_total_coins: Int?
|
||||||
var extra_day_coins: Int?
|
var extra_day_coins: Int?
|
||||||
var sub_coins_txt_list: [String]?
|
var sub_coins_txt_list: [String]?
|
||||||
|
var max_total_coins_pop: Int?
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,7 @@ class NRPayDataRequest: NSObject {
|
|||||||
private var payAlertModel: NRPayAlertModel?
|
private var payAlertModel: NRPayAlertModel?
|
||||||
|
|
||||||
private var completerBlock: ((_ model: NRPayDateModel?) -> Void)?
|
private var completerBlock: ((_ model: NRPayDateModel?) -> Void)?
|
||||||
|
private var payRetainBlock: ((_ model: NRPayDateModel?) -> Void)?
|
||||||
private var payAlertBlock: ((_ model: NRPayAlertModel?) -> Void)?
|
private var payAlertBlock: ((_ model: NRPayAlertModel?) -> Void)?
|
||||||
|
|
||||||
private var isLoding = false
|
private var isLoding = false
|
||||||
@ -99,6 +100,33 @@ class NRPayDataRequest: NSObject {
|
|||||||
productsRequest.start()
|
productsRequest.start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func requestPayRetainInfo(completer: ((_ model: NRPayDateModel?) -> Void)?) {
|
||||||
|
self.payRetainBlock = completer
|
||||||
|
self.isLoding = true
|
||||||
|
self.isToast = true
|
||||||
|
NRHud.show()
|
||||||
|
|
||||||
|
NRStoreAPI.requestPayRetainInfo { [weak self] model in
|
||||||
|
guard let self = self else { return }
|
||||||
|
guard let model = model else {
|
||||||
|
NRHud.dismiss()
|
||||||
|
self.payRetainBlock?(nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.oldTemplateModel = model
|
||||||
|
|
||||||
|
var productIdArr: [String] = []
|
||||||
|
model.list_coins?.forEach { item in
|
||||||
|
productIdArr.append(NRIapManager.manager.getProductId(templateId: item.ios_template_id) ?? "")
|
||||||
|
}
|
||||||
|
|
||||||
|
let set = Set(productIdArr)
|
||||||
|
let productsRequest = SKProductsRequest(productIdentifiers: set)
|
||||||
|
productsRequest.delegate = self
|
||||||
|
productsRequest.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: SKProductsRequestDelegate
|
//MARK: SKProductsRequestDelegate
|
||||||
@ -110,7 +138,7 @@ extension NRPayDataRequest: SKProductsRequestDelegate {
|
|||||||
}
|
}
|
||||||
let products = response.products
|
let products = response.products
|
||||||
|
|
||||||
if let block = self.completerBlock {
|
if self.completerBlock != nil || self.payRetainBlock != nil {
|
||||||
guard let templateModel = self.oldTemplateModel else { return }
|
guard let templateModel = self.oldTemplateModel else { return }
|
||||||
|
|
||||||
var newCoinList: [NRPayItem] = []
|
var newCoinList: [NRPayItem] = []
|
||||||
@ -147,11 +175,16 @@ extension NRPayDataRequest: SKProductsRequestDelegate {
|
|||||||
templateModel.list_coins = newCoinList
|
templateModel.list_coins = newCoinList
|
||||||
templateModel.list_sub_vip = newVipList
|
templateModel.list_sub_vip = newVipList
|
||||||
|
|
||||||
|
if let block = self.completerBlock {
|
||||||
NRIapManager.manager.payDateModel = templateModel
|
NRIapManager.manager.payDateModel = templateModel
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
block(templateModel)
|
block(templateModel)
|
||||||
}
|
}
|
||||||
|
} else if let block = self.payRetainBlock {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
block(templateModel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else if let block = self.payAlertBlock {
|
} else if let block = self.payAlertBlock {
|
||||||
guard let coinalertModel = self.payAlertModel else { return }
|
guard let coinalertModel = self.payAlertModel else { return }
|
||||||
|
|||||||
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0x0F",
|
||||||
|
"green" : "0x0F",
|
||||||
|
"red" : "0x0F"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
22
ReaderHive/Source/Assets.xcassets/Image/pay_retain_bg_image.imageset/Contents.json
vendored
Normal file
22
ReaderHive/Source/Assets.xcassets/Image/pay_retain_bg_image.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "bg@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "bg@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
ReaderHive/Source/Assets.xcassets/Image/pay_retain_bg_image.imageset/bg@2x.png
vendored
Normal file
BIN
ReaderHive/Source/Assets.xcassets/Image/pay_retain_bg_image.imageset/bg@2x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 251 KiB |
BIN
ReaderHive/Source/Assets.xcassets/Image/pay_retain_bg_image.imageset/bg@3x.png
vendored
Normal file
BIN
ReaderHive/Source/Assets.xcassets/Image/pay_retain_bg_image.imageset/bg@3x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 495 KiB |
22
ReaderHive/Source/Assets.xcassets/Image/pay_retain_cell_bg_image.imageset/Contents.json
vendored
Normal file
22
ReaderHive/Source/Assets.xcassets/Image/pay_retain_cell_bg_image.imageset/Contents.json
vendored
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
ReaderHive/Source/Assets.xcassets/Image/pay_retain_cell_bg_image.imageset/金币包背景@2x.png
vendored
Normal file
BIN
ReaderHive/Source/Assets.xcassets/Image/pay_retain_cell_bg_image.imageset/金币包背景@2x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 92 KiB |
BIN
ReaderHive/Source/Assets.xcassets/Image/pay_retain_cell_bg_image.imageset/金币包背景@3x.png
vendored
Normal file
BIN
ReaderHive/Source/Assets.xcassets/Image/pay_retain_cell_bg_image.imageset/金币包背景@3x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 197 KiB |
22
ReaderHive/Source/Assets.xcassets/Image/star_icon_06.imageset/Contents.json
vendored
Normal file
22
ReaderHive/Source/Assets.xcassets/Image/star_icon_06.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "star@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "star@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
ReaderHive/Source/Assets.xcassets/Image/star_icon_06.imageset/star@2x.png
vendored
Normal file
BIN
ReaderHive/Source/Assets.xcassets/Image/star_icon_06.imageset/star@2x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 421 B |
BIN
ReaderHive/Source/Assets.xcassets/Image/star_icon_06.imageset/star@3x.png
vendored
Normal file
BIN
ReaderHive/Source/Assets.xcassets/Image/star_icon_06.imageset/star@3x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 549 B |
@ -152,6 +152,7 @@
|
|||||||
"reader_chapter_price" = "Chapter Price";
|
"reader_chapter_price" = "Chapter Price";
|
||||||
"reader_my_vip_tips" = "Unrestricted access to all series!";
|
"reader_my_vip_tips" = "Unrestricted access to all series!";
|
||||||
"reader_visitor" = "Visitor";
|
"reader_visitor" = "Visitor";
|
||||||
|
"free_coins" = "free coins";
|
||||||
|
|
||||||
"vip_retain_alert_text" = "Unlock every show you love!";
|
"vip_retain_alert_text" = "Unlock every show you love!";
|
||||||
"reader_log_out_content" = "Are you sure you want to log out?";
|
"reader_log_out_content" = "Are you sure you want to log out?";
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user