增加上报阅读时长,修复BUG
This commit is contained in:
parent
1b625064e6
commit
82ec2a370f
@ -90,6 +90,7 @@
|
||||
85859E5C2EF3FC5F0020D282 /* NotificationService.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 85859E552EF3FC5F0020D282 /* NotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
85859E662EF3FC6E0020D282 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85859E632EF3FC6E0020D282 /* NotificationService.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 */; };
|
||||
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 */; };
|
||||
85C1786D2F050AA400A8A76E /* Poppins-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 85C178672F050AA400A8A76E /* Poppins-Bold.ttf */; };
|
||||
@ -600,6 +601,7 @@
|
||||
85859E622EF3FC6E0020D282 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; 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>"; };
|
||||
85ACA3F82F28A7C8009D52B0 /* NRNovelReadViewModel+View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NRNovelReadViewModel+View.swift"; 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>"; };
|
||||
85C178692F050AA400A8A76E /* Poppins-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Poppins-Regular.ttf"; sourceTree = "<group>"; };
|
||||
@ -1088,6 +1090,7 @@
|
||||
0373D9442ED57B7B0017DCC7 /* NRNovelDetailViewModel.swift */,
|
||||
F34348EF2ED8381E00AA7E70 /* NRNovelReadViewModel.swift */,
|
||||
F34991262EE282660039E939 /* NRNovelReadViewModel+Data.swift */,
|
||||
85ACA3F82F28A7C8009D52B0 /* NRNovelReadViewModel+View.swift */,
|
||||
);
|
||||
path = VM;
|
||||
sourceTree = "<group>";
|
||||
@ -2615,6 +2618,7 @@
|
||||
039810B62ED42D840006E317 /* NRHomeNovelNewArrivalsCell.swift in Sources */,
|
||||
0373D95A2ED593D50017DCC7 /* NRSearchRecordCell.swift in Sources */,
|
||||
0373D9582ED5935D0017DCC7 /* NRSearchRecordView.swift in Sources */,
|
||||
85ACA3F92F28A7CD009D52B0 /* NRNovelReadViewModel+View.swift in Sources */,
|
||||
F343490C2ED9751800AA7E70 /* NRReadChapterCatalogModel.swift in Sources */,
|
||||
F3B859862EE972F70095A9CC /* NRConsumptionRecordsCell.swift in Sources */,
|
||||
F34990C72EDFCE500039E939 /* NRNovelReadGradeView.swift in Sources */,
|
||||
|
||||
@ -162,6 +162,25 @@ struct NRStatAPI {
|
||||
|
||||
NRNetwork.request(parameters: param) { (response: NRNetwork.Response<String>) in }
|
||||
}
|
||||
|
||||
///统计用户观看时长
|
||||
static func nr_requestWatchNovelDuration(novelId: String, duration: Int, percent: Int) {
|
||||
|
||||
let parameters: [String : Any] = [
|
||||
"request_id": "\(Int(Date().timeIntervalSince1970))",
|
||||
"short_play_id" : novelId,
|
||||
"duration" : duration,
|
||||
"percent" : percent //观看百分比
|
||||
]
|
||||
|
||||
var param = NRNetwork.Parameters(path: "/watchShortDuration")
|
||||
param.method = .post
|
||||
param.isLoding = false
|
||||
param.isToast = false
|
||||
param.parameters = parameters
|
||||
|
||||
NRNetwork.request(parameters: param) { (response: NRNetwork.Response<String>) in }
|
||||
}
|
||||
}
|
||||
|
||||
extension NRStatAPI {
|
||||
|
||||
@ -24,7 +24,7 @@ extension NRTargetType: TargetType {
|
||||
var path: String {
|
||||
switch self {
|
||||
case .request(let param):
|
||||
return "/readerhive" + param.path
|
||||
return NRBaseURLPrefix + param.path
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,17 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
#if DEBUG
|
||||
let NRBaseURL = "https://api-novel-test.guyantv.com"
|
||||
let NRBaseURLPrefix = ""
|
||||
//let NRBaseURL = "https://api-readerhive.readerhive.net"
|
||||
//let NRBaseURLPrefix = "/readerhive"
|
||||
#else
|
||||
let NRBaseURL = "https://api-readerhive.readerhive.net"
|
||||
let NRBaseURLPrefix = "/readerhive"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
let NRWebBaseURL = "https://www.readerhive.net"
|
||||
|
||||
|
||||
@ -153,9 +153,9 @@ extension UIViewController {
|
||||
titleColor: UIColor = UINavigationBar.titleWhiteColor,
|
||||
isTranslucent: Bool = true
|
||||
) {
|
||||
self.navigationController?.navigationBar.nr_setTranslucent(isTranslucent: isTranslucent)
|
||||
self.navigationController?.navigationBar.nr_setBackgroundColor(backgroundColor: backgroundColor)
|
||||
self.navigationController?.navigationBar.nr_setTitleTextAttributes(titleTextAttributes: [
|
||||
self.navigationController?.navigationBar.nr_setTranslucent(isTranslucent)
|
||||
self.navigationController?.navigationBar.nr_setBackgroundColor(backgroundColor)
|
||||
self.navigationController?.navigationBar.nr_setTitleTextAttributes([
|
||||
NSAttributedString.Key.font : titleFont,
|
||||
NSAttributedString.Key.foregroundColor : titleColor
|
||||
])
|
||||
|
||||
@ -22,10 +22,9 @@ class NRHomeNovelNextView: NRHomeNovelHeaderContentView {
|
||||
let itemHeight = 150 / 100 * itemWidth + 68
|
||||
|
||||
let layout = UICollectionViewFlowLayout()
|
||||
layout.scrollDirection = .horizontal
|
||||
layout.itemSize = .init(width: itemWidth, height: itemHeight)
|
||||
layout.minimumLineSpacing = 15
|
||||
layout.minimumInteritemSpacing = 18
|
||||
layout.itemSize = .init(width: floor(itemWidth), height: itemHeight)
|
||||
layout.minimumLineSpacing = 18
|
||||
layout.minimumInteritemSpacing = 15
|
||||
return layout
|
||||
}()
|
||||
|
||||
@ -36,10 +35,15 @@ class NRHomeNovelNextView: NRHomeNovelHeaderContentView {
|
||||
collectionView.dataSource = self
|
||||
collectionView.showsHorizontalScrollIndicator = false
|
||||
collectionView.contentInset = .init(top: 0, left: 16, bottom: 0, right: 16)
|
||||
collectionView.addObserver(self, forKeyPath: "contentSize", context: nil)
|
||||
collectionView.register(NRHomeNovelNextViewCell.self, forCellWithReuseIdentifier: "cell")
|
||||
return collectionView
|
||||
}()
|
||||
|
||||
@MainActor deinit {
|
||||
self.collectionView.removeObserver(self, forKeyPath: "contentSize")
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
self.titleLabel.text = "reader_home_title3".localized
|
||||
@ -51,6 +55,16 @@ class NRHomeNovelNextView: NRHomeNovelHeaderContentView {
|
||||
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 NRHomeNovelNextView {
|
||||
@ -60,7 +74,7 @@ extension NRHomeNovelNextView {
|
||||
|
||||
collectionView.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview()
|
||||
make.height.equalTo(collectionViewLayout.itemSize.height * 2 + collectionViewLayout.minimumInteritemSpacing)
|
||||
make.height.equalTo(1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -82,6 +82,8 @@ class NRStarGradeView: UIView {
|
||||
|
||||
private lazy var cosmosView: CosmosView = {
|
||||
let view = CosmosView(settings: settings)
|
||||
view.setContentHuggingPriority(.required, for: .horizontal)
|
||||
view.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||
view.didTouchCosmos = { [weak self] rating in
|
||||
guard let self = self else { return }
|
||||
self.didTouch?(rating)
|
||||
|
||||
@ -21,7 +21,12 @@ class NRMeCoinsPackView: UIView {
|
||||
return view
|
||||
}()
|
||||
|
||||
private lazy var iconImageView = UIImageView(image: UIImage(named: "gift_icon_01"))
|
||||
private lazy var iconImageView: UIImageView = {
|
||||
let imageView = UIImageView(image: UIImage(named: "gift_icon_01"))
|
||||
imageView.setContentHuggingPriority(.required, for: .horizontal)
|
||||
imageView.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||
return imageView
|
||||
}()
|
||||
|
||||
private lazy var titleLabel: UILabel = {
|
||||
let label = NRLabel()
|
||||
@ -41,7 +46,12 @@ class NRMeCoinsPackView: UIView {
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var indicatorImageView = UIImageView(image: UIImage(named: "arrow_right_icon_07"))
|
||||
private lazy var indicatorImageView: UIImageView = {
|
||||
let imageView = UIImageView(image: UIImage(named: "arrow_right_icon_07"))
|
||||
imageView.setContentHuggingPriority(.required, for: .horizontal)
|
||||
imageView.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||
return imageView
|
||||
}()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
@ -109,7 +109,7 @@ class NRDetailRechargeView: NRPanModalContentView {
|
||||
let label = UILabel()
|
||||
label.font = .font(ofSize: 12, weight: .regular)
|
||||
label.textColor = .black
|
||||
label.text = "Chapter Price".localized + ":"
|
||||
label.text = "reader_chapter_price".localized + ":"
|
||||
return label
|
||||
}()
|
||||
|
||||
|
||||
@ -115,7 +115,11 @@ extension NRNovelDetailHeaderView {
|
||||
}
|
||||
|
||||
private func updateNumLabel() {
|
||||
numLabel.text = NSNumber(value: num).toString(minimumFractionDigits: minimumFractionDigits)
|
||||
if num < 1000 {
|
||||
numLabel.text = NSNumber(value: num).toString(minimumFractionDigits: minimumFractionDigits)
|
||||
} else {
|
||||
numLabel.text = NSNumber(value: num).formattedNumber()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -32,7 +32,7 @@ class NRNovelReadBottomView: UIView {
|
||||
}()
|
||||
|
||||
lazy var catalogButton: UIButton = {
|
||||
let button = self.createButton(title: "Catalog".localized, icon: UIImage(named: "catalog_icon_01"), nightIcon: UIImage(named: "catalog_icon_02"))
|
||||
let button = self.createButton(title: "reader_book_catalog".localized, icon: UIImage(named: "catalog_icon_01"), nightIcon: UIImage(named: "catalog_icon_02"))
|
||||
button.addAction(UIAction(handler: { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
self.viewModel?.showCatalogView()
|
||||
|
||||
@ -18,6 +18,13 @@ class NRNovelReadFinishHeaderView: UICollectionReusableView {
|
||||
}
|
||||
}
|
||||
|
||||
var didChangeContentHeight: ((_ height: CGFloat) -> Void)?
|
||||
|
||||
private lazy var contentView: NRScrollView = {
|
||||
let contentView = NRScrollView()
|
||||
contentView.addObserver(self, forKeyPath: "contentSize", context: nil)
|
||||
return contentView
|
||||
}()
|
||||
|
||||
private lazy var coverBgView: UIView = {
|
||||
let view = NRGradientView()
|
||||
@ -55,6 +62,8 @@ class NRNovelReadFinishHeaderView: UICollectionReusableView {
|
||||
label.textColors = [UIColor.F_3912_F.cgColor, UIColor.FF_4_A_4_A.cgColor, UIColor.FA_9_B_1_F.cgColor]
|
||||
label.textStartPoint = .init(x: 0, y: 0.5)
|
||||
label.textEndPoint = .init(x: 1, y: 0.5)
|
||||
label.numberOfLines = 0
|
||||
label.textAlignment = .center
|
||||
label.text = "reader_book_finished".localized
|
||||
return label
|
||||
}()
|
||||
@ -84,10 +93,14 @@ class NRNovelReadFinishHeaderView: UICollectionReusableView {
|
||||
let label = UILabel()
|
||||
label.font = .font(ofSize: 16, weight: .semibold)
|
||||
label.textColor = .black
|
||||
label.text = "read_finish_list_title".localized
|
||||
label.text = "reader_book_finished_list_title".localized
|
||||
return label
|
||||
}()
|
||||
|
||||
deinit {
|
||||
self.contentView.removeObserver(self, forKeyPath: "contentSize")
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
nr_setupUI()
|
||||
@ -103,19 +116,31 @@ class NRNovelReadFinishHeaderView: UICollectionReusableView {
|
||||
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.contentView.contentSize.height + 1
|
||||
self.didChangeContentHeight?(height)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension NRNovelReadFinishHeaderView {
|
||||
|
||||
private func nr_setupUI() {
|
||||
addSubview(coverBgView)
|
||||
addSubview(contentView)
|
||||
contentView.addSubview(coverBgView)
|
||||
coverBgView.addSubview(coverImageView)
|
||||
addSubview(coverDecorateView)
|
||||
addSubview(titleLabel)
|
||||
addSubview(textLabel)
|
||||
addSubview(gradeView)
|
||||
addSubview(lineView)
|
||||
addSubview(listTitleLabel)
|
||||
contentView.addSubview(coverDecorateView)
|
||||
contentView.addSubview(titleLabel)
|
||||
contentView.addSubview(textLabel)
|
||||
contentView.addSubview(gradeView)
|
||||
contentView.addSubview(lineView)
|
||||
contentView.addSubview(listTitleLabel)
|
||||
|
||||
contentView.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview()
|
||||
}
|
||||
|
||||
coverBgView.snp.makeConstraints { make in
|
||||
make.centerX.equalToSuperview()
|
||||
@ -138,12 +163,14 @@ extension NRNovelReadFinishHeaderView {
|
||||
titleLabel.snp.makeConstraints { make in
|
||||
make.centerX.equalToSuperview()
|
||||
make.top.equalTo(coverBgView.snp.bottom).offset(20)
|
||||
// make.right.lessThanOrEqualToSuperview().offset(-16)
|
||||
make.right.lessThanOrEqualTo(gradeView)
|
||||
}
|
||||
|
||||
textLabel.snp.makeConstraints { make in
|
||||
make.centerX.equalToSuperview()
|
||||
make.right.lessThanOrEqualToSuperview().offset(16)
|
||||
make.top.equalTo(titleLabel.snp.bottom).offset(6)
|
||||
make.right.lessThanOrEqualTo(gradeView)
|
||||
}
|
||||
|
||||
gradeView.snp.makeConstraints { make in
|
||||
@ -162,6 +189,7 @@ extension NRNovelReadFinishHeaderView {
|
||||
listTitleLabel.snp.makeConstraints { make in
|
||||
make.left.equalToSuperview().offset(16)
|
||||
make.top.equalTo(lineView.snp.bottom).offset(16)
|
||||
make.bottom.equalToSuperview().offset(-16)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -38,6 +38,7 @@ class NRNovelReadStarGradeView: UIView {
|
||||
let label = UILabel()
|
||||
label.font = .font(ofSize: 12, weight: .medium)
|
||||
label.textColor = .black
|
||||
label.numberOfLines = 0
|
||||
return label
|
||||
}()
|
||||
|
||||
@ -46,8 +47,11 @@ class NRNovelReadStarGradeView: UIView {
|
||||
view.filledImage = UIImage(named: "star_icon_04")
|
||||
view.emptyImage = UIImage(named: "star_icon_05")
|
||||
view.updateOnTouch = true
|
||||
view.fillMode = .full
|
||||
// view.fillMode = .full
|
||||
view.fillMode = .precise
|
||||
view.didFinishTouching = self.didFinishTouchingGrade
|
||||
view.setContentHuggingPriority(.required, for: .horizontal)
|
||||
view.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||
return view
|
||||
}()
|
||||
|
||||
@ -102,6 +106,7 @@ extension NRNovelReadStarGradeView {
|
||||
|
||||
label.snp.makeConstraints { make in
|
||||
make.centerY.equalToSuperview()
|
||||
make.right.lessThanOrEqualTo(gradeView.snp.left).offset(-10)
|
||||
make.left.equalToSuperview().offset(12)
|
||||
}
|
||||
|
||||
|
||||
@ -119,6 +119,10 @@ extension NRNovelReadFinishViewController: UICollectionViewDelegate, UICollectio
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
|
||||
let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "header", for: indexPath) as! NRNovelReadFinishHeaderView
|
||||
view.didChangeContentHeight = { [weak self] height in
|
||||
guard let self = self else { return }
|
||||
self.collectionViewLayout.headerReferenceSize = .init(width: UIScreen.width, height: height)
|
||||
}
|
||||
view.novelModel = self.viewModel?.novelModel
|
||||
return view
|
||||
}
|
||||
|
||||
@ -50,6 +50,7 @@ extension NRNovelReaderViewController {
|
||||
|
||||
if let vc = viewController as? NRNovelReadBaseViewController {
|
||||
let model = vc.catalogModel
|
||||
let pageModel = vc.pageModel
|
||||
let lockCoins = model?.coins ?? 0
|
||||
let myCoins = NRLoginManager.manager.userInfo?.totalCoins ?? 0
|
||||
if model?.is_lock == true {
|
||||
@ -59,6 +60,13 @@ extension NRNovelReaderViewController {
|
||||
self.viewModel.openRechargeView()
|
||||
}
|
||||
}
|
||||
|
||||
if pageModel?.pageType != .textPart {
|
||||
self.viewModel.statWatchNovelDuration()
|
||||
} else {
|
||||
self.viewModel.startWatchStartDate()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -114,6 +114,8 @@ class NRNovelReaderViewController: NRViewController {
|
||||
UIApplication.shared.isIdleTimerDisabled = true
|
||||
|
||||
UIScreen.main.brightness = NRNovelReadSetManager.manager.brightness
|
||||
|
||||
self.viewModel.startWatchStartDate()
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
@ -124,6 +126,7 @@ class NRNovelReaderViewController: NRViewController {
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
UIScreen.main.brightness = oldBrightness
|
||||
self.viewModel.statWatchNovelDuration()
|
||||
}
|
||||
|
||||
override var prefersStatusBarHidden: Bool {
|
||||
@ -217,6 +220,8 @@ extension NRNovelReaderViewController {
|
||||
///装载数据
|
||||
func loadData() {
|
||||
Task {
|
||||
self.viewModel.statWatchNovelDuration()
|
||||
|
||||
NRHud.show()
|
||||
//获取小说数据
|
||||
await self.viewModel.requestNovelDetail()
|
||||
@ -324,11 +329,13 @@ extension NRNovelReaderViewController {
|
||||
@objc private func willResignActiveNotification() {
|
||||
guard self.isViewDidAppear else { return }
|
||||
UIScreen.main.brightness = oldBrightness
|
||||
self.viewModel.statWatchNovelDuration()
|
||||
}
|
||||
|
||||
@objc private func didBecomeActiveNotification() {
|
||||
guard self.isViewDidAppear else { return }
|
||||
UIScreen.main.brightness = NRNovelReadSetManager.manager.brightness
|
||||
self.viewModel.startWatchStartDate()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ class NRNovelDetailViewModel: NSObject {
|
||||
var recommandDataArr: [NRNovelModel]?
|
||||
|
||||
func requestDetailData() async {
|
||||
let (model, id, msg) = await NRNovelAPI.requestDetail(novelId)
|
||||
let (model, _, _) = await NRNovelAPI.requestDetail(novelId)
|
||||
|
||||
await MainActor.run {
|
||||
if let model = model {
|
||||
|
||||
@ -286,6 +286,33 @@ extension NRNovelReadViewModel {
|
||||
}
|
||||
}
|
||||
|
||||
func startWatchStartDate() {
|
||||
let (model, pageModel) = self.getCurrentPageData()
|
||||
guard let model = model else { return }
|
||||
guard let pageModel = pageModel else { return }
|
||||
if model.is_lock == false, self.watchStartDate == nil, pageModel.pageType == .textPart {
|
||||
if self.vc?.isViewDidAppear == true {
|
||||
self.watchStartDate = Date()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///统计观看时长
|
||||
func statWatchNovelDuration() {
|
||||
guard let watchStartDate = self.watchStartDate else { return }
|
||||
self.watchStartDate = nil
|
||||
let (catalogModel, _) = getCurrentPageData()
|
||||
|
||||
// guard let chapterModel = catalogModel?.chapterModel else { return }
|
||||
let totalEp = CGFloat(self.novelModel?.episode_total ?? 1)
|
||||
// let currentEp = CGFloat(NSNumber(pointer: chapterModel.episode ?? "0").floatValue)
|
||||
let currentEp = CGFloat(self.currentPageIndexPath.section)
|
||||
|
||||
let nowDate = Date()
|
||||
let duration = Int(nowDate.timeIntervalSince1970 - watchStartDate.timeIntervalSince1970)
|
||||
let percent = currentEp / totalEp * 100
|
||||
|
||||
NRStatAPI.nr_requestWatchNovelDuration(novelId: self.novelId, duration: duration, percent: Int(percent))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
193
ReaderHive/Class/Novel/VM/NRNovelReadViewModel+View.swift
Normal file
193
ReaderHive/Class/Novel/VM/NRNovelReadViewModel+View.swift
Normal file
@ -0,0 +1,193 @@
|
||||
//
|
||||
// NRNovelReadViewModel+View.swift
|
||||
// ReaderHive
|
||||
//
|
||||
// Created by 澜声世纪 on 2026/1/27.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import HWPanModal
|
||||
import YYCategories
|
||||
|
||||
extension NRNovelReadViewModel {
|
||||
|
||||
///退出阅读页面
|
||||
func backReadPage() {
|
||||
Task {
|
||||
guard let isShowModel = await NRNovelAPI.requestShowRecommendPop(id: self.novelId), isShowModel.is_pop_up == true else {
|
||||
_backReadPage()
|
||||
return
|
||||
}
|
||||
|
||||
await MainActor.run {
|
||||
let alert = NRAlert(title: "reader_recommend_book".localized, detail: "reader_recommend_book_tip".localized, topIconImage: UIImage(named: "alert_top_icon_01"), highlightButtonText: "reader_recommend_book_ok".localized)
|
||||
alert.closeHandle = { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self._backReadPage()
|
||||
}
|
||||
alert.highlightHandle = { [weak self] in
|
||||
guard let self = self else { return }
|
||||
NRNovelAPI.requestConfirmRecommend(self.novelId)
|
||||
self._backReadPage()
|
||||
}
|
||||
alert.show()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private func _backReadPage() {
|
||||
self.vc?.navigationController?.popViewController(animated: true)
|
||||
}
|
||||
|
||||
func showAllMenuView(isShow: Bool) {
|
||||
self.showTopView(isShow: isShow)
|
||||
self.showBottomView(isShow: isShow)
|
||||
}
|
||||
|
||||
///显示顶部视图
|
||||
func showTopView(isShow: Bool) {
|
||||
guard showTop != isShow else { return }
|
||||
self.showTop = isShow
|
||||
self.vc?.setNeedsStatusBarAppearanceUpdate()
|
||||
|
||||
UIView.animate(withDuration: NRNovelReadSetManager.manager.animateDuration) { [weak self] in
|
||||
guard let self = self else { return }
|
||||
if isShow {
|
||||
self.topView?.transform = CGAffineTransform(translationX: 0, y: NRNovelReadSetManager.manager.topViewHeight)
|
||||
} else {
|
||||
self.topView?.transform = CGAffineTransform.identity
|
||||
}
|
||||
} completion: { _ in
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
///显示底部视图
|
||||
func showBottomView(isShow: Bool) {
|
||||
|
||||
UIView.animate(withDuration: NRNovelReadSetManager.manager.animateDuration) { [weak self] in
|
||||
guard let self = self else { return }
|
||||
if isShow {
|
||||
self.bottomView?.transform = CGAffineTransform(translationX: 0, y: -NRNovelReadSetManager.manager.bottomViewHeight)
|
||||
} else {
|
||||
self.bottomView?.transform = CGAffineTransform.identity
|
||||
}
|
||||
} completion: { _ in
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
///显示更多工具栏
|
||||
func showMoreView() {
|
||||
guard let model = self.novelModel else { return }
|
||||
|
||||
let view = NRNovelReadGradeView()
|
||||
view.model = model
|
||||
view.present(in: nil)
|
||||
}
|
||||
|
||||
///展示设置页面
|
||||
func showSettingView() {
|
||||
|
||||
let view = NRNovelReadSettingView()
|
||||
view.present(in: nil)
|
||||
}
|
||||
|
||||
///展示目录
|
||||
func showCatalogView() {
|
||||
let (catalogModel, _) = self.getCurrentPageData()
|
||||
|
||||
let view = NRNovelReaderCatalogView()
|
||||
view.novelModel = self.novelModel
|
||||
view.currentCatalogModel = catalogModel
|
||||
view.catalogDataArr = self.chapterCatalogList
|
||||
view.didSelected = { [weak self] index in
|
||||
guard let self = self else { return }
|
||||
if index != self.currentPageIndexPath.section {
|
||||
self.skip(chapterIndex: index)
|
||||
}
|
||||
}
|
||||
view.show()
|
||||
}
|
||||
|
||||
///打开充值页面
|
||||
func openRechargeView() {
|
||||
guard self.popView == nil else { return }
|
||||
let (catalogModel, _) = self.getCurrentPageData()
|
||||
guard let catalogModel = catalogModel else { return }
|
||||
self.statWatchNovelDuration()
|
||||
|
||||
self.payDataRequest = NRPayDataRequest()
|
||||
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 }
|
||||
|
||||
NRStatAPI.nr_requestEventStat(orderCode: nil, shortPlayId: self.novelId, videoId: catalogModel.id, eventKey: .payTemplateDialog, errorMsg: nil, otherParamenters: [
|
||||
"event_name" : "pay open"
|
||||
])
|
||||
|
||||
let view = NRDetailRechargeView()
|
||||
view.price = catalogModel.coins
|
||||
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 = NRPayDataRequest()
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -27,7 +27,7 @@ class NRNovelReadViewModel: NSObject {
|
||||
}
|
||||
}
|
||||
|
||||
private(set) var showTop = false
|
||||
var showTop = false
|
||||
///章节目录列表
|
||||
lazy var chapterCatalogList: [NRReadChapterCatalogModel] = []
|
||||
|
||||
@ -49,190 +49,13 @@ class NRNovelReadViewModel: NSObject {
|
||||
|
||||
weak var popView: UIView?
|
||||
|
||||
private var payDataRequest = NRPayDataRequest()
|
||||
var payDataRequest: NRPayDataRequest?
|
||||
///用来统计观看时长
|
||||
var watchStartDate: Date?
|
||||
}
|
||||
|
||||
|
||||
|
||||
extension NRNovelReadViewModel {
|
||||
|
||||
///退出阅读页面
|
||||
func backReadPage() {
|
||||
Task {
|
||||
guard let isShowModel = await NRNovelAPI.requestShowRecommendPop(id: self.novelId), isShowModel.is_pop_up == true else {
|
||||
_backReadPage()
|
||||
return
|
||||
}
|
||||
|
||||
await MainActor.run {
|
||||
let alert = NRAlert(title: "reader_recommend_book".localized, detail: "reader_recommend_book_tip".localized, topIconImage: UIImage(named: "alert_top_icon_01"), highlightButtonText: "reader_recommend_book_ok".localized)
|
||||
alert.closeHandle = { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self._backReadPage()
|
||||
}
|
||||
alert.highlightHandle = { [weak self] in
|
||||
guard let self = self else { return }
|
||||
NRNovelAPI.requestConfirmRecommend(self.novelId)
|
||||
self._backReadPage()
|
||||
}
|
||||
alert.show()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private func _backReadPage() {
|
||||
self.vc?.navigationController?.popViewController(animated: true)
|
||||
}
|
||||
|
||||
func showAllMenuView(isShow: Bool) {
|
||||
self.showTopView(isShow: isShow)
|
||||
self.showBottomView(isShow: isShow)
|
||||
}
|
||||
|
||||
///显示顶部视图
|
||||
func showTopView(isShow: Bool) {
|
||||
guard showTop != isShow else { return }
|
||||
self.showTop = isShow
|
||||
self.vc?.setNeedsStatusBarAppearanceUpdate()
|
||||
|
||||
UIView.animate(withDuration: NRNovelReadSetManager.manager.animateDuration) { [weak self] in
|
||||
guard let self = self else { return }
|
||||
if isShow {
|
||||
self.topView?.transform = CGAffineTransform(translationX: 0, y: NRNovelReadSetManager.manager.topViewHeight)
|
||||
} else {
|
||||
self.topView?.transform = CGAffineTransform.identity
|
||||
}
|
||||
} completion: { _ in
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
///显示底部视图
|
||||
func showBottomView(isShow: Bool) {
|
||||
|
||||
UIView.animate(withDuration: NRNovelReadSetManager.manager.animateDuration) { [weak self] in
|
||||
guard let self = self else { return }
|
||||
if isShow {
|
||||
self.bottomView?.transform = CGAffineTransform(translationX: 0, y: -NRNovelReadSetManager.manager.bottomViewHeight)
|
||||
} else {
|
||||
self.bottomView?.transform = CGAffineTransform.identity
|
||||
}
|
||||
} completion: { _ in
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
///显示更多工具栏
|
||||
func showMoreView() {
|
||||
guard let model = self.novelModel else { return }
|
||||
|
||||
let view = NRNovelReadGradeView()
|
||||
view.model = model
|
||||
view.present(in: nil)
|
||||
}
|
||||
|
||||
///展示设置页面
|
||||
func showSettingView() {
|
||||
|
||||
let view = NRNovelReadSettingView()
|
||||
view.present(in: nil)
|
||||
}
|
||||
|
||||
///展示目录
|
||||
func showCatalogView() {
|
||||
let (catalogModel, _) = self.getCurrentPageData()
|
||||
|
||||
let view = NRNovelReaderCatalogView()
|
||||
view.novelModel = self.novelModel
|
||||
view.currentCatalogModel = catalogModel
|
||||
view.catalogDataArr = self.chapterCatalogList
|
||||
view.didSelected = { [weak self] index in
|
||||
guard let self = self else { return }
|
||||
if index != self.currentPageIndexPath.section {
|
||||
self.skip(chapterIndex: index)
|
||||
}
|
||||
}
|
||||
view.show()
|
||||
}
|
||||
|
||||
///打开充值页面
|
||||
func openRechargeView() {
|
||||
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 }
|
||||
|
||||
NRStatAPI.nr_requestEventStat(orderCode: nil, shortPlayId: self.novelId, videoId: catalogModel.id, eventKey: .payTemplateDialog, errorMsg: nil, otherParamenters: [
|
||||
"event_name" : "pay open"
|
||||
])
|
||||
|
||||
let view = NRDetailRechargeView()
|
||||
view.price = catalogModel.coins
|
||||
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
|
||||
extension NRNovelReadViewModel: UIGestureRecognizerDelegate {
|
||||
|
||||
@ -85,7 +85,7 @@ class NRCoinsPackConfirmView: NRPanModalContentView {
|
||||
var configuration = UIButton.Configuration.plain()
|
||||
configuration.background.image = UIImage(named: "gradient_color_01")
|
||||
configuration.background.cornerRadius = 24
|
||||
configuration.attributedTitle = AttributedString("Continue".localized, attributes: AttributeContainer([
|
||||
configuration.attributedTitle = AttributedString("reader_continue".localized, attributes: AttributeContainer([
|
||||
.font : UIFont.font(ofSize: 14, weight: .bold),
|
||||
.foregroundColor : UIColor.white
|
||||
]))
|
||||
|
||||
@ -15,7 +15,8 @@ class NRStoreVipCell: UICollectionViewCell {
|
||||
var model: NRPayItem? {
|
||||
didSet {
|
||||
titleLabel.text = model?.brief
|
||||
desLabel.text = model?.nr_description
|
||||
// desLabel.text = model?.nr_description
|
||||
desLabel.text = "reader_my_vip_tips".localized
|
||||
|
||||
if let coins = model?.send_coins, coins > 0 {
|
||||
extraLabel.text = "+\(coins) \("reader_extra".localized)"
|
||||
@ -35,10 +36,6 @@ class NRStoreVipCell: UICollectionViewCell {
|
||||
}
|
||||
|
||||
if let price = offerPrice {
|
||||
// let priceString = NSMutableAttributedString(string: currency + price)
|
||||
// priceString.yy_font = .font(ofSize: 28, weight: .init(800))
|
||||
// priceLabel.attributedText = priceString
|
||||
//
|
||||
let oldPriceStr = NSMutableAttributedString(string: oldPrice)
|
||||
oldPriceStr.yy_strikethroughColor = oldPriceLabel.textColor
|
||||
oldPriceStr.yy_strikethroughStyle = .double
|
||||
|
||||
@ -18,6 +18,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
nr_registThirdparty(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(networkStatusDidChangeNotification), name: NRNetworkReachableManager.networkStatusDidChangeNotification, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(localizedDidChangeNotification), name: NRLocalizedManager.localizedDidChangeNotification, object: nil)
|
||||
|
||||
setConfig()
|
||||
|
||||
@ -51,6 +52,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
self.refreshAppData()
|
||||
}
|
||||
|
||||
@objc private func localizedDidChangeNotification() {
|
||||
self.refreshAppData()
|
||||
}
|
||||
}
|
||||
|
||||
private extension AppDelegate {
|
||||
@ -63,5 +67,7 @@ private extension AppDelegate {
|
||||
NRIapManager.manager.preloadingProducts()
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@ class NRCoinsPackAlert: NRAlert {
|
||||
titleLabel.numberOfLines = 0
|
||||
titleLabel.font = .font(ofSize: 20, weight: .medium)
|
||||
titleLabel.textColor = .F_9710_D
|
||||
titleLabel.text = "coins_pack_alert_title".localized
|
||||
titleLabel.text = "reader_my_daily_reward".localized
|
||||
|
||||
let coinsView = UIView()
|
||||
|
||||
|
||||
@ -33,6 +33,7 @@ struct NREmpty {
|
||||
view?.actionBtnBackGroundColor = .clear
|
||||
view?.actionBtnMargin = 16
|
||||
view?.actionBtnHorizontalMargin = 25
|
||||
view?.actionBtnWidth = 0
|
||||
|
||||
return view!
|
||||
|
||||
|
||||
@ -26,7 +26,7 @@ class NRUserInfo: NSObject, SmartCodable, NSSecureCoding {
|
||||
if let name = family_name, !name.isEmpty {
|
||||
return name
|
||||
} else {
|
||||
return "Visitor"
|
||||
return "reader_visitor".localized
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -62,11 +62,11 @@ class NRNovelReadSetManager: NSObject {
|
||||
var paragraphSpacing: CGFloat {
|
||||
switch self.readSet.paragraphSpacingType {
|
||||
case .small:
|
||||
return 15
|
||||
return 6
|
||||
case .standard:
|
||||
return 17
|
||||
return 8
|
||||
case .large:
|
||||
return 19
|
||||
return 10
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -149,13 +149,15 @@
|
||||
"reader_open_notification_later" = "Later";
|
||||
"reader_default" = "Default";
|
||||
"reader_login" = "Log in";
|
||||
"Chapter Price" = "Chapter Price";
|
||||
"reader_chapter_price" = "Chapter Price";
|
||||
"reader_my_vip_tips" = "Unrestricted access to all series!";
|
||||
"reader_visitor" = "Visitor";
|
||||
|
||||
"vip_retain_alert_text" = "Unlock every show you love!";
|
||||
"reader_log_out_content" = "Are you sure you want to log out?";
|
||||
"detail_collect_alert_title" = "Delete This Book?";
|
||||
"detail_collect_alert_text" = "Confirm to delete this book from the list";
|
||||
"coins_pack_alert_title" = "Daily Reward is waiting!";
|
||||
"reader_my_daily_reward" = "Daily Reward is waiting!";
|
||||
"reader_open_notification" = "Enable Notifications";
|
||||
"reader_open_notification_info" = "Stay informed with popular recommendations and latest updates!";
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user