This commit is contained in:
zeng 2025-12-09 13:31:07 +08:00
parent 5b2b59ba12
commit 5ee82e3ab0
19 changed files with 166 additions and 80 deletions

View File

@ -88,14 +88,17 @@ struct NRNovelAPI {
} }
/// 10011 10005 /// 10011 10005
static func requestChapterData(novelId: String, chapterId: String) async -> (NRReadChapterModel?, Int?) { static func requestChapterData(novelId: String, chapterId: String, isToast: Bool = false, isLoding: Bool = false) async -> (NRReadChapterModel?, Int?) {
await withCheckedContinuation { continuation in await withCheckedContinuation { continuation in
var param = NRNetwork.Parameters(path: "/novel/getChapterInfo") var param = NRNetwork.Parameters(path: "/novel/getChapterInfo")
param.method = .get param.method = .get
param.isToast = isToast
param.isLoding = isLoding
param.parameters = [ param.parameters = [
"short_play_id" : novelId, "short_play_id" : novelId,
"short_play_video_id" : chapterId, "short_play_video_id" : chapterId,
] ]
NRNetwork.request(parameters: param) { (response: NRNetwork.Response<NRReadChapterModel>) in NRNetwork.request(parameters: param) { (response: NRNetwork.Response<NRReadChapterModel>) in
if response.isSuccess { if response.isSuccess {
continuation.resume(returning: (response.data, response.code)) continuation.resume(returning: (response.data, response.code))

View File

@ -76,7 +76,7 @@ class NRImageView: UIImageView {
override func layoutSubviews() { override func layoutSubviews() {
super.layoutSubviews() super.layoutSubviews()
placeholderImageView.frame = .init(x: 0, y: 0, width: self.bounds.width * (2 / 3), height: self.bounds.height * (2 / 3)) placeholderImageView.frame = .init(x: 0, y: 0, width: self.bounds.width * (1 / 3), height: self.bounds.height * (1 / 3))
placeholderImageView.center = .init(x: self.bounds.width / 2, y: self.bounds.height / 2) placeholderImageView.center = .init(x: self.bounds.width / 2, y: self.bounds.height / 2)
} }

View File

@ -90,7 +90,7 @@ extension NRExploreNovelContentViewController {
private func nr_setupUI() { private func nr_setupUI() {
view.addSubview(titleLabel) view.addSubview(titleLabel)
view.addSubview(moreButton) // view.addSubview(moreButton)
view.addSubview(pageMenuView) view.addSubview(pageMenuView)
view.addSubview(pageView) view.addSubview(pageView)
view.addSubview(lineView) view.addSubview(lineView)
@ -98,17 +98,18 @@ extension NRExploreNovelContentViewController {
titleLabel.snp.makeConstraints { make in titleLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(12) make.left.equalToSuperview().offset(12)
make.centerY.equalTo(moreButton) make.top.equalToSuperview().offset(16)
make.height.equalTo(24)
} }
moreButton.snp.makeConstraints { make in // moreButton.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-12) // make.right.equalToSuperview().offset(-12)
make.top.equalToSuperview().offset(16) // make.top.equalToSuperview().offset(16)
} // }
pageMenuView.snp.makeConstraints { make in pageMenuView.snp.makeConstraints { make in
make.left.right.equalToSuperview() make.left.right.equalToSuperview()
make.top.equalTo(moreButton.snp.bottom).offset(12) make.top.equalTo(titleLabel.snp.bottom).offset(12)
make.height.equalTo(24) make.height.equalTo(24)
} }

View File

@ -7,7 +7,6 @@
import UIKit import UIKit
import SnapKit import SnapKit
//import JXPagingView
import JXSegmentedView import JXSegmentedView
class NRExploreNovelViewController: NRViewController { class NRExploreNovelViewController: NRViewController {

View File

@ -29,8 +29,25 @@ class NRReadChapterCatalogModel: NSObject, SmartCodable {
@IgnoredKey @IgnoredKey
var chapterModel: NRReadChapterModel? var chapterModel: NRReadChapterModel?
///
func parserEmpty() {
let chapterModel = NRReadChapterModel()
if self.is_lock == true {
chapterModel.parserEmpty("Not unlocked yet".localized)
} else {
chapterModel.parserEmpty("Loading".localized)
}
self.chapterModel = chapterModel
}
///
func parserAllData() {
if self.chapterModel?.novel_txt == nil {
parserEmpty()
} else {
self.chapterModel?.parser()
}
}
} }

View File

@ -52,11 +52,6 @@ class NRReadChapterModel: NSObject, SmartCodable {
/// ///
func parser() { func parser() {
guard novel_txt != nil else {
self.parserEmpty()
return
}
let tempAttributes = NRNovelReadSetManager.manager.attributes(isTitle: false, isPageing: true) let tempAttributes = NRNovelReadSetManager.manager.attributes(isTitle: false, isPageing: true)
guard !NSDictionary(dictionary: attributes).isEqual(to: tempAttributes) else { return } guard !NSDictionary(dictionary: attributes).isEqual(to: tempAttributes) else { return }
@ -77,8 +72,8 @@ class NRReadChapterModel: NSObject, SmartCodable {
} }
/// ///
func parserEmpty() { func parserEmpty(_ text: String) {
fullContent = NSMutableAttributedString(string: "\n\n\n\n\n" + "Not unlocked yet".localized, attributes: NRNovelReadSetManager.manager.attributes(isTitle: true)) fullContent = NSMutableAttributedString(string: "\n\n\n\n\n" + text, attributes: NRNovelReadSetManager.manager.attributes(isTitle: true))
let pageModel = NRReadPageModel() let pageModel = NRReadPageModel()
pageModel.content = fullContent pageModel.content = fullContent

View File

@ -33,6 +33,8 @@ class NRNovelReadContentTopView: UIView {
var configuration = UIButton.Configuration.plain() var configuration = UIButton.Configuration.plain()
configuration.contentInsets = .zero configuration.contentInsets = .zero
configuration.imagePadding = 12 configuration.imagePadding = 12
configuration.titleAlignment = .center
configuration.titleLineBreakMode = .byTruncatingTail
let button = UIButton(configuration: configuration, primaryAction: UIAction(handler: { [weak self] _ in let button = UIButton(configuration: configuration, primaryAction: UIAction(handler: { [weak self] _ in
guard let self = self else { return } guard let self = self else { return }
@ -99,6 +101,7 @@ extension NRNovelReadContentTopView {
backButton.snp.makeConstraints { make in backButton.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16) make.left.equalToSuperview().offset(16)
make.top.bottom.equalToSuperview() make.top.bottom.equalToSuperview()
make.right.lessThanOrEqualToSuperview().offset(-16)
} }
} }

View File

@ -145,6 +145,7 @@ extension NRNovelReadGradeView {
nameLabel.snp.makeConstraints { make in nameLabel.snp.makeConstraints { make in
make.top.equalTo(coverImageView).offset(8) make.top.equalTo(coverImageView).offset(8)
make.left.equalTo(coverImageView.snp.right).offset(12) make.left.equalTo(coverImageView.snp.right).offset(12)
make.right.lessThanOrEqualToSuperview().offset(-16)
} }
starView.snp.makeConstraints { make in starView.snp.makeConstraints { make in

View File

@ -32,6 +32,7 @@ class NRNovelReadTopView: UIView {
configuration.contentInsets = .zero configuration.contentInsets = .zero
configuration.image = UIImage(named: "arrow_left_icon_03") configuration.image = UIImage(named: "arrow_left_icon_03")
configuration.imagePadding = 12 configuration.imagePadding = 12
configuration.titleLineBreakMode = .byTruncatingTail
let button = UIButton(configuration: configuration, primaryAction: UIAction(handler: { [weak self] _ in let button = UIButton(configuration: configuration, primaryAction: UIAction(handler: { [weak self] _ in
guard let self = self else { return } guard let self = self else { return }
@ -62,6 +63,8 @@ class NRNovelReadTopView: UIView {
self.viewModel?.showMoreView() self.viewModel?.showMoreView()
})) }))
button.setImage(UIImage(named: "more_icon_01"), for: .normal) button.setImage(UIImage(named: "more_icon_01"), for: .normal)
button.setContentHuggingPriority(.required, for: .horizontal)
button.setContentCompressionResistancePriority(.required, for: .horizontal)
return button return button
}() }()
@ -148,6 +151,7 @@ extension NRNovelReadTopView {
backButton.snp.makeConstraints { make in backButton.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16) make.left.equalToSuperview().offset(16)
make.top.bottom.equalToSuperview() make.top.bottom.equalToSuperview()
make.right.lessThanOrEqualTo(moreButton.snp.left).offset(-10)
} }
moreButton.snp.makeConstraints { make in moreButton.snp.makeConstraints { make in

View File

@ -257,16 +257,28 @@ extension NRNovelReaderCatalogView: UITableViewDelegate, UITableViewDataSource {
} }
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let novelId = self.novelModel?.id else { return }
let currentModel = self.catalogDataArr[indexPath.row]
let lastIndex = indexPath.row - 1 let lastIndex = indexPath.row - 1
var lastModel: NRReadChapterCatalogModel? var lastModel: NRReadChapterCatalogModel?
if lastIndex >= 0 { if lastIndex >= 0 {
lastModel = self.catalogDataArr[lastIndex] lastModel = self.catalogDataArr[lastIndex]
} }
/// ///
if lastModel?.is_lock == true { return } if lastModel?.is_lock == true {
Task {
let (_, code) = await NRNovelAPI.requestChapterData(novelId: novelId, chapterId: currentModel.id ?? "", isToast: true, isLoding: true)
if code == 200 {
self.didSelected?(indexPath.row)
self.dismiss()
}
}
return
}
self.didSelected?(indexPath.row) self.didSelected?(indexPath.row)
self.dismiss() self.dismiss()
} }
} }

View File

@ -142,8 +142,8 @@ extension NRNovelDetailCatalogViewController: UITableViewDelegate, UITableViewDa
} }
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let id = self.novelModel?.id else { return } guard let novelId = self.novelModel?.id else { return }
let model = dataArr[indexPath.row] let currentModel = dataArr[indexPath.row]
let lastIndex = indexPath.row - 1 let lastIndex = indexPath.row - 1
var lastModel: NRReadChapterCatalogModel? var lastModel: NRReadChapterCatalogModel?
@ -152,17 +152,27 @@ extension NRNovelDetailCatalogViewController: UITableViewDelegate, UITableViewDa
} }
if lastModel?.is_lock == true { if lastModel?.is_lock == true {
Task {
let (_, code) = await NRNovelAPI.requestChapterData(novelId: novelId, chapterId: currentModel.id ?? "", isToast: true, isLoding: true)
if code == 200 {
self.pushReadVC(currentModel)
}
}
} else { } else {
let vc = NRNovelReaderViewController() self.pushReadVC(currentModel)
vc.novelId = id
vc.targetCatalogModel = model
self.navigationController?.pushViewController(vc, animated: true)
} }
} }
private func pushReadVC(_ model: NRReadChapterCatalogModel) {
guard let novelId = self.novelModel?.id else { return }
let vc = NRNovelReaderViewController()
vc.novelId = novelId
vc.targetCatalogModel = model
self.navigationController?.pushViewController(vc, animated: true)
}
} }
extension NRNovelDetailCatalogViewController { extension NRNovelDetailCatalogViewController {

View File

@ -156,8 +156,14 @@ extension NRNovelReaderViewController {
/// ///
func getBelowReadController() -> UIViewController? { func getBelowReadController() -> UIViewController? {
let (catalogModel, pageModel) = self.viewModel.getBelowPageData() let (currentCatalogModel, _) = self.viewModel.getCurrentPageData()
return getReadController(catalogModel: catalogModel, pageModel: pageModel)
if currentCatalogModel?.is_lock == true {
return nil
} else {
let (catalogModel, pageModel) = self.viewModel.getBelowPageData()
return getReadController(catalogModel: catalogModel, pageModel: pageModel)
}
} }
/// ///

View File

@ -141,6 +141,11 @@ class NRNovelReaderViewController: NRViewController {
self.viewModel.parserAllDataAndReloadPage(dismissMenu: false) self.viewModel.parserAllDataAndReloadPage(dismissMenu: false)
} }
func reloadData() {
if let vc = self.getCurrentReadController() {
setViewController(displayController: vc, isAbove: false, animated: false)
}
}
} }
extension NRNovelReaderViewController { extension NRNovelReaderViewController {
@ -227,9 +232,7 @@ extension NRNovelReaderViewController {
// //
self.viewModel.checkCurrentIndexPath() self.viewModel.checkCurrentIndexPath()
if let vc = self.getCurrentReadController() { self.reloadData()
setViewController(displayController: vc, isAbove: false, animated: false)
}
} }
} }

View File

@ -78,7 +78,7 @@ extension NRNovelReadViewModel {
func parserAllDataAndReloadPage(dismissMenu: Bool = true) { func parserAllDataAndReloadPage(dismissMenu: Bool = true) {
/// ///
self.chapterCatalogList.forEach { self.chapterCatalogList.forEach {
$0.chapterModel?.parser() $0.parserAllData()
} }
self.checkCurrentIndexPath() self.checkCurrentIndexPath()
@ -101,7 +101,7 @@ extension NRNovelReadViewModel {
let catalogModel = chapterCatalogList[currentSection] let catalogModel = chapterCatalogList[currentSection]
let listCount = catalogModel.chapterModel?.pageList?.count ?? 0 let listCount = catalogModel.chapterModel?.pageList?.count ?? 0
if listCount <= currentRow { if listCount <= currentRow, listCount > 0 {
currentRow = listCount - 1 currentRow = listCount - 1
} }
self.currentPageIndexPath = IndexPath(row: currentRow, section: currentSection) self.currentPageIndexPath = IndexPath(row: currentRow, section: currentSection)
@ -118,7 +118,15 @@ extension NRNovelReadViewModel {
guard let displayController = vc?.getReadController(catalogModel: catalogModel, pageModel: chapterModel.pageList?.first) else { return } guard let displayController = vc?.getReadController(catalogModel: catalogModel, pageModel: chapterModel.pageList?.first) else { return }
self.vc?.setViewController(displayController: displayController, isAbove: false, animated: false, dismissMenu: dismissMenu) self.vc?.setViewController(displayController: displayController, isAbove: false, animated: false, dismissMenu: dismissMenu)
//
if chapterModel.novel_txt == nil {
Task {
guard let _ = await self.requestChapterData(catalogModel) else { return }
await MainActor.run {
self.vc?.reloadData()
}
}
}
} else { // } else { //
Task { Task {
guard let _ = await self.requestChapterData(catalogModel) else { return } guard let _ = await self.requestChapterData(catalogModel) else { return }
@ -155,19 +163,35 @@ extension NRNovelReadViewModel {
model.episode = catalogModel.episode model.episode = catalogModel.episode
model.page = pageModel.page?.intValue model.page = pageModel.page?.intValue
NRKeyedArchiver.archiver(folderName: kNRReadRecordFolderName, fileName: novelId, object: model) NRKeyedArchiver.archiver(folderName: kNRReadRecordFolderName, fileName: novelId, object: model)
NRNovelAPI.requestUploadRecord(novelId, chapterId: catalogModel.id ?? "")
let lastTime = uploadRecordDate?.timeIntervalSince1970 ?? 0
let nowTime = Date().timeIntervalSince1970
if lastTime == 0 || nowTime - lastTime > 5 {//5
uploadRecordDate = Date()
NRNovelAPI.requestUploadRecord(novelId, chapterId: catalogModel.id ?? "")
}
} }
/// ///
func getReadRecord() -> NRNovelReadRecordModel? { func getReadRecord() -> NRNovelReadRecordModel? {
guard novelId.count > 0 else { return nil } guard novelId.count > 0 else { return nil }
var recordModel = NRKeyedArchiver.unarchiver(folderName: kNRReadRecordFolderName, fileName: novelId) as? NRNovelReadRecordModel var recordModel = NRKeyedArchiver.unarchiver(folderName: kNRReadRecordFolderName, fileName: novelId, as: NRNovelReadRecordModel.self)
// //
if self.novelModel?.progress?.short_play_video_id != "0", self.novelModel?.progress?.short_play_video_id != recordModel?.short_play_video_id { if self.novelModel?.progress?.short_play_video_id != "0", self.novelModel?.progress?.short_play_video_id != recordModel?.short_play_video_id {
recordModel = self.novelModel?.progress recordModel = self.novelModel?.progress
} }
return recordModel return recordModel
} }
///
func setEmptyData() {
for model in self.chapterCatalogList {
if model.chapterModel == nil {
model.parserEmpty()
}
}
}
} }
//MARK: //MARK:
@ -186,7 +210,7 @@ extension NRNovelReadViewModel {
guard let list = await NRNovelAPI.requestChapterCatalogList(id: self.novelId) else { return } guard let list = await NRNovelAPI.requestChapterCatalogList(id: self.novelId) else { return }
await MainActor.run { await MainActor.run {
self.chapterCatalogList = list self.chapterCatalogList = list
self.addUnlockPageData() self.setEmptyData()
} }
} }
/// ///
@ -194,30 +218,29 @@ extension NRNovelReadViewModel {
func requestChapterData(_ catalogModel: NRReadChapterCatalogModel) async -> NRReadChapterModel? { func requestChapterData(_ catalogModel: NRReadChapterCatalogModel) async -> NRReadChapterModel? {
guard let chapterId = catalogModel.id else { return nil } guard let chapterId = catalogModel.id else { return nil }
let myCoins = NRLoginManager.manager.userInfo?.totalCoins ?? 0
let lockCoins = catalogModel.coins ?? 0
//
if catalogModel.is_lock == true, myCoins < lockCoins {
return nil
}
let (model, code) = await NRNovelAPI.requestChapterData(novelId: self.novelId, chapterId: chapterId) let (model, code) = await NRNovelAPI.requestChapterData(novelId: self.novelId, chapterId: chapterId)
guard let model = model else { return nil } Task {//线
await NRLoginManager.manager.updateUserInfo()
model.parser()
///
if chapterId == self.chapterCatalogList.last?.id {
model.pageList?.append(NRReadPageModel.createReadFinishModel())
} }
catalogModel.chapterModel = model if code == 200 {
guard let model = model else { return nil }
if catalogModel.is_lock == true {
catalogModel.is_lock = false model.parser()
self.addUnlockPageData() ///
if chapterId == self.chapterCatalogList.last?.id {
model.pageList?.append(NRReadPageModel.createReadFinishModel())
}
catalogModel.chapterModel = model
if catalogModel.is_lock == true {
catalogModel.is_lock = false
}
return model
} }
return model
return nil
} }
/// ///
@ -249,16 +272,6 @@ extension NRNovelReadViewModel {
} }
} }
///
func addUnlockPageData() {
for model in self.chapterCatalogList {
if model.is_lock == true {
let chapterModel = NRReadChapterModel()
chapterModel.parserEmpty()
model.chapterModel = chapterModel
break
}
}
}
} }

View File

@ -44,6 +44,9 @@ class NRNovelReadViewModel: NSObject {
return tap return tap
}() }()
///
var uploadRecordDate: Date?
} }

View File

@ -7,6 +7,7 @@
import UIKit import UIKit
import IQKeyboardManagerSwift import IQKeyboardManagerSwift
import IQKeyboardToolbarManager import IQKeyboardToolbarManager
import MJRefresh
extension AppDelegate { extension AppDelegate {
@ -19,6 +20,8 @@ extension AppDelegate {
IQKeyboardManager.shared.resignOnTouchOutside = true IQKeyboardManager.shared.resignOnTouchOutside = true
IQKeyboardToolbarManager.shared.isEnabled = false IQKeyboardToolbarManager.shared.isEnabled = false
MJRefreshConfig.default.languageCode = NRLocalizedManager.shared.mjLocalizedKey
let appearance = UINavigationBar.defaultAppearance() let appearance = UINavigationBar.defaultAppearance()
UINavigationBar.appearance().scrollEdgeAppearance = appearance UINavigationBar.appearance().scrollEdgeAppearance = appearance
UINavigationBar.appearance().standardAppearance = appearance UINavigationBar.appearance().standardAppearance = appearance

View File

@ -17,21 +17,34 @@ class NRKeyedArchiver: NSObject {
class func archiver(folderName:String, fileName:String, object:AnyObject) { class func archiver(folderName:String, fileName:String, object:AnyObject) {
var path = documentDirectoryPath + "/\(mineFolderName)/\(folderName)" var path = documentDirectoryPath + "/\(mineFolderName)/\(folderName)"
guard creat_file(path: path) else { return } //
if creat_file(path: path) { // path += "/\(fileName)"
let url = URL(fileURLWithPath: path)
path += "/\(fileName)" do {
let data = try NSKeyedArchiver.archivedData(withRootObject: object, requiringSecureCoding: true)
NSKeyedArchiver.archiveRootObject(object, toFile: path) try data.write(to: url, options: .atomic)
} catch let error {
nrPrint(message: error)
} }
} }
/// ///
class func unarchiver(folderName:String, fileName:String) ->AnyObject? { class func unarchiver<T: NSObject & NSSecureCoding>(folderName:String, fileName:String, as type: T.Type) -> T? {
let path = documentDirectoryPath + "/\(mineFolderName)/\(folderName)/\(fileName)" let path = documentDirectoryPath + "/\(mineFolderName)/\(folderName)/\(fileName)"
return NSKeyedUnarchiver.unarchiveObject(withFile: path) as AnyObject? let url = URL(fileURLWithPath: path)
do {
let data = try Data(contentsOf: url)
let result = try NSKeyedUnarchiver.unarchivedObject(ofClass: type, from: data)
return result
} catch let error {
nrPrint(message: error)
}
return nil
} }
/// ///

View File

@ -23,7 +23,7 @@ class NRLoginManager: NSObject {
func updateUserInfo() async { func updateUserInfo() async {
guard let userInfo = await NRUserAPI.requestUserInfo() else { return } guard let userInfo = await NRUserAPI.requestUserInfo() else { return }
self.userInfo = userInfo self.userInfo = userInfo
UserDefaults.nr_setObject(token, forKey: kNRUserInfoDefaultsKey) UserDefaults.nr_setObject(userInfo, forKey: kNRUserInfoDefaultsKey)
await MainActor.run { await MainActor.run {
NotificationCenter.default.post(name: NRLoginManager.userInfoUpdateNotification, object: nil) NotificationCenter.default.post(name: NRLoginManager.userInfoUpdateNotification, object: nil)

View File

@ -82,7 +82,7 @@
"Bonus" = "Bonus"; "Bonus" = "Bonus";
"Top Up" = "Top Up"; "Top Up" = "Top Up";
"Language" = "Language"; "Language" = "Language";
"Loading" = "Loading";