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
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
var param = NRNetwork.Parameters(path: "/novel/getChapterInfo")
param.method = .get
param.isToast = isToast
param.isLoding = isLoding
param.parameters = [
"short_play_id" : novelId,
"short_play_video_id" : chapterId,
]
NRNetwork.request(parameters: param) { (response: NRNetwork.Response<NRReadChapterModel>) in
if response.isSuccess {
continuation.resume(returning: (response.data, response.code))

View File

@ -76,7 +76,7 @@ class NRImageView: UIImageView {
override func 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)
}

View File

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

View File

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

View File

@ -29,8 +29,25 @@ class NRReadChapterCatalogModel: NSObject, SmartCodable {
@IgnoredKey
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() {
guard novel_txt != nil else {
self.parserEmpty()
return
}
let tempAttributes = NRNovelReadSetManager.manager.attributes(isTitle: false, isPageing: true)
guard !NSDictionary(dictionary: attributes).isEqual(to: tempAttributes) else { return }
@ -77,8 +72,8 @@ class NRReadChapterModel: NSObject, SmartCodable {
}
///
func parserEmpty() {
fullContent = NSMutableAttributedString(string: "\n\n\n\n\n" + "Not unlocked yet".localized, attributes: NRNovelReadSetManager.manager.attributes(isTitle: true))
func parserEmpty(_ text: String) {
fullContent = NSMutableAttributedString(string: "\n\n\n\n\n" + text, attributes: NRNovelReadSetManager.manager.attributes(isTitle: true))
let pageModel = NRReadPageModel()
pageModel.content = fullContent

View File

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

View File

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

View File

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

View File

@ -257,16 +257,28 @@ extension NRNovelReaderCatalogView: UITableViewDelegate, UITableViewDataSource {
}
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
var lastModel: NRReadChapterCatalogModel?
if lastIndex >= 0 {
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.dismiss()
self.dismiss()
}
}

View File

@ -142,8 +142,8 @@ extension NRNovelDetailCatalogViewController: UITableViewDelegate, UITableViewDa
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let id = self.novelModel?.id else { return }
let model = dataArr[indexPath.row]
guard let novelId = self.novelModel?.id else { return }
let currentModel = dataArr[indexPath.row]
let lastIndex = indexPath.row - 1
var lastModel: NRReadChapterCatalogModel?
@ -152,17 +152,27 @@ extension NRNovelDetailCatalogViewController: UITableViewDelegate, UITableViewDa
}
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 {
let vc = NRNovelReaderViewController()
vc.novelId = id
vc.targetCatalogModel = model
self.navigationController?.pushViewController(vc, animated: true)
self.pushReadVC(currentModel)
}
}
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 {

View File

@ -156,8 +156,14 @@ extension NRNovelReaderViewController {
///
func getBelowReadController() -> UIViewController? {
let (catalogModel, pageModel) = self.viewModel.getBelowPageData()
return getReadController(catalogModel: catalogModel, pageModel: pageModel)
let (currentCatalogModel, _) = self.viewModel.getCurrentPageData()
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)
}
func reloadData() {
if let vc = self.getCurrentReadController() {
setViewController(displayController: vc, isAbove: false, animated: false)
}
}
}
extension NRNovelReaderViewController {
@ -227,9 +232,7 @@ extension NRNovelReaderViewController {
//
self.viewModel.checkCurrentIndexPath()
if let vc = self.getCurrentReadController() {
setViewController(displayController: vc, isAbove: false, animated: false)
}
self.reloadData()
}
}

View File

@ -78,7 +78,7 @@ extension NRNovelReadViewModel {
func parserAllDataAndReloadPage(dismissMenu: Bool = true) {
///
self.chapterCatalogList.forEach {
$0.chapterModel?.parser()
$0.parserAllData()
}
self.checkCurrentIndexPath()
@ -101,7 +101,7 @@ extension NRNovelReadViewModel {
let catalogModel = chapterCatalogList[currentSection]
let listCount = catalogModel.chapterModel?.pageList?.count ?? 0
if listCount <= currentRow {
if listCount <= currentRow, listCount > 0 {
currentRow = listCount - 1
}
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 }
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 { //
Task {
guard let _ = await self.requestChapterData(catalogModel) else { return }
@ -155,19 +163,35 @@ extension NRNovelReadViewModel {
model.episode = catalogModel.episode
model.page = pageModel.page?.intValue
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? {
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 {
recordModel = self.novelModel?.progress
}
return recordModel
}
///
func setEmptyData() {
for model in self.chapterCatalogList {
if model.chapterModel == nil {
model.parserEmpty()
}
}
}
}
//MARK:
@ -186,7 +210,7 @@ extension NRNovelReadViewModel {
guard let list = await NRNovelAPI.requestChapterCatalogList(id: self.novelId) else { return }
await MainActor.run {
self.chapterCatalogList = list
self.addUnlockPageData()
self.setEmptyData()
}
}
///
@ -194,30 +218,29 @@ extension NRNovelReadViewModel {
func requestChapterData(_ catalogModel: NRReadChapterCatalogModel) async -> NRReadChapterModel? {
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)
guard let model = model else { return nil }
model.parser()
///
if chapterId == self.chapterCatalogList.last?.id {
model.pageList?.append(NRReadPageModel.createReadFinishModel())
Task {//线
await NRLoginManager.manager.updateUserInfo()
}
catalogModel.chapterModel = model
if catalogModel.is_lock == true {
catalogModel.is_lock = false
self.addUnlockPageData()
if code == 200 {
guard let model = model else { return nil }
model.parser()
///
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
}()
///
var uploadRecordDate: Date?
}

View File

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

View File

@ -17,21 +17,34 @@ class NRKeyedArchiver: NSObject {
class func archiver(folderName:String, fileName:String, object:AnyObject) {
var path = documentDirectoryPath + "/\(mineFolderName)/\(folderName)"
guard creat_file(path: path) else { return } //
if creat_file(path: path) { //
path += "/\(fileName)"
NSKeyedArchiver.archiveRootObject(object, toFile: path)
path += "/\(fileName)"
let url = URL(fileURLWithPath: path)
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: object, requiringSecureCoding: true)
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)"
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 {
guard let userInfo = await NRUserAPI.requestUserInfo() else { return }
self.userInfo = userInfo
UserDefaults.nr_setObject(token, forKey: kNRUserInfoDefaultsKey)
UserDefaults.nr_setObject(userInfo, forKey: kNRUserInfoDefaultsKey)
await MainActor.run {
NotificationCenter.default.post(name: NRLoginManager.userInfoUpdateNotification, object: nil)

View File

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