版本更新

This commit is contained in:
澜声世纪 2025-12-16 10:36:24 +08:00
parent 5667b11a55
commit 5c9c5d64b6
39 changed files with 1036 additions and 34 deletions

View File

@ -102,6 +102,15 @@
85CF94372EEFE27E006467E3 /* FacebookLogin in Frameworks */ = {isa = PBXBuildFile; productRef = 85CF94362EEFE27E006467E3 /* FacebookLogin */; }; 85CF94372EEFE27E006467E3 /* FacebookLogin in Frameworks */ = {isa = PBXBuildFile; productRef = 85CF94362EEFE27E006467E3 /* FacebookLogin */; };
85CF94392EEFE35A006467E3 /* NRLoginManager+Facebook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF94382EEFE34F006467E3 /* NRLoginManager+Facebook.swift */; }; 85CF94392EEFE35A006467E3 /* NRLoginManager+Facebook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF94382EEFE34F006467E3 /* NRLoginManager+Facebook.swift */; };
85CF943B2EEFE94C006467E3 /* AppDelegate+Open.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF943A2EEFE947006467E3 /* AppDelegate+Open.swift */; }; 85CF943B2EEFE94C006467E3 /* AppDelegate+Open.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF943A2EEFE947006467E3 /* AppDelegate+Open.swift */; };
85CF943D2EEFEC14006467E3 /* NRSettingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF943C2EEFEC14006467E3 /* NRSettingViewController.swift */; };
85CF943F2EEFF1B2006467E3 /* NRSettingCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF943E2EEFF1B2006467E3 /* NRSettingCell.swift */; };
85CF94412EEFF62E006467E3 /* NRSettingFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF94402EEFF62E006467E3 /* NRSettingFooterView.swift */; };
85CF94462EF001AD006467E3 /* NRMyUnlocksViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 85CF94452EF001AD006467E3 /* NRMyUnlocksViewController.xib */; };
85CF94472EF001AD006467E3 /* NRMyUnlocksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF94442EF001AD006467E3 /* NRMyUnlocksViewController.swift */; };
85CF944A2EF004D3006467E3 /* NRMyUnlocksCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 85CF94492EF004D3006467E3 /* NRMyUnlocksCell.xib */; };
85CF944B2EF004D3006467E3 /* NRMyUnlocksCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF94482EF004D3006467E3 /* NRMyUnlocksCell.swift */; };
85CF944D2EF0EC00006467E3 /* NRVersionUpdateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF944C2EF0EC00006467E3 /* NRVersionUpdateModel.swift */; };
85CF944F2EF0F04D006467E3 /* NRUpdatesAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF944E2EF0F04D006467E3 /* NRUpdatesAlert.swift */; };
F34348AF2ED5B85300AA7E70 /* NRExploreViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34348AE2ED5B85300AA7E70 /* NRExploreViewController.swift */; }; F34348AF2ED5B85300AA7E70 /* NRExploreViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34348AE2ED5B85300AA7E70 /* NRExploreViewController.swift */; };
F34348B12ED5B9A400AA7E70 /* NRExploreNovelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34348B02ED5B9A400AA7E70 /* NRExploreNovelViewController.swift */; }; F34348B12ED5B9A400AA7E70 /* NRExploreNovelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34348B02ED5B9A400AA7E70 /* NRExploreNovelViewController.swift */; };
F34348B32ED5BB6100AA7E70 /* NRExploreNovelMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34348B22ED5BB6100AA7E70 /* NRExploreNovelMenuView.swift */; }; F34348B32ED5BB6100AA7E70 /* NRExploreNovelMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34348B22ED5BB6100AA7E70 /* NRExploreNovelMenuView.swift */; };
@ -339,6 +348,15 @@
85CF94342EEFDFAC006467E3 /* ReaderHive.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ReaderHive.entitlements; sourceTree = "<group>"; }; 85CF94342EEFDFAC006467E3 /* ReaderHive.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ReaderHive.entitlements; sourceTree = "<group>"; };
85CF94382EEFE34F006467E3 /* NRLoginManager+Facebook.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NRLoginManager+Facebook.swift"; sourceTree = "<group>"; }; 85CF94382EEFE34F006467E3 /* NRLoginManager+Facebook.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NRLoginManager+Facebook.swift"; sourceTree = "<group>"; };
85CF943A2EEFE947006467E3 /* AppDelegate+Open.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Open.swift"; sourceTree = "<group>"; }; 85CF943A2EEFE947006467E3 /* AppDelegate+Open.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Open.swift"; sourceTree = "<group>"; };
85CF943C2EEFEC14006467E3 /* NRSettingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRSettingViewController.swift; sourceTree = "<group>"; };
85CF943E2EEFF1B2006467E3 /* NRSettingCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRSettingCell.swift; sourceTree = "<group>"; };
85CF94402EEFF62E006467E3 /* NRSettingFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRSettingFooterView.swift; sourceTree = "<group>"; };
85CF94442EF001AD006467E3 /* NRMyUnlocksViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRMyUnlocksViewController.swift; sourceTree = "<group>"; };
85CF94452EF001AD006467E3 /* NRMyUnlocksViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NRMyUnlocksViewController.xib; sourceTree = "<group>"; };
85CF94482EF004D3006467E3 /* NRMyUnlocksCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRMyUnlocksCell.swift; sourceTree = "<group>"; };
85CF94492EF004D3006467E3 /* NRMyUnlocksCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NRMyUnlocksCell.xib; sourceTree = "<group>"; };
85CF944C2EF0EC00006467E3 /* NRVersionUpdateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRVersionUpdateModel.swift; sourceTree = "<group>"; };
85CF944E2EF0F04D006467E3 /* NRUpdatesAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRUpdatesAlert.swift; sourceTree = "<group>"; };
C3BEE224CB3F55939653D26D /* Pods-NovelReader.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NovelReader.debug.xcconfig"; path = "Target Support Files/Pods-NovelReader/Pods-NovelReader.debug.xcconfig"; sourceTree = "<group>"; }; C3BEE224CB3F55939653D26D /* Pods-NovelReader.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NovelReader.debug.xcconfig"; path = "Target Support Files/Pods-NovelReader/Pods-NovelReader.debug.xcconfig"; sourceTree = "<group>"; };
F34348AE2ED5B85300AA7E70 /* NRExploreViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRExploreViewController.swift; sourceTree = "<group>"; }; F34348AE2ED5B85300AA7E70 /* NRExploreViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRExploreViewController.swift; sourceTree = "<group>"; };
F34348B02ED5B9A400AA7E70 /* NRExploreNovelViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRExploreNovelViewController.swift; sourceTree = "<group>"; }; F34348B02ED5B9A400AA7E70 /* NRExploreNovelViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRExploreNovelViewController.swift; sourceTree = "<group>"; };
@ -1028,6 +1046,7 @@
children = ( children = (
F34991042EE165EA0039E939 /* NRMeItem.swift */, F34991042EE165EA0039E939 /* NRMeItem.swift */,
F3B8593B2EE677170095A9CC /* NRLanguageModel.swift */, F3B8593B2EE677170095A9CC /* NRLanguageModel.swift */,
85CF944C2EF0EC00006467E3 /* NRVersionUpdateModel.swift */,
); );
path = M; path = M;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1046,6 +1065,8 @@
F34991172EE1780A0039E939 /* NRNovelHistoryCell.swift */, F34991172EE1780A0039E939 /* NRNovelHistoryCell.swift */,
F3B859382EE676610095A9CC /* NRLanguageCell.swift */, F3B859382EE676610095A9CC /* NRLanguageCell.swift */,
85CF942E2EEFB2CF006467E3 /* NRLoginView.swift */, 85CF942E2EEFB2CF006467E3 /* NRLoginView.swift */,
85CF943E2EEFF1B2006467E3 /* NRSettingCell.swift */,
85CF94402EEFF62E006467E3 /* NRSettingFooterView.swift */,
); );
path = V; path = V;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1059,6 +1080,11 @@
F34991152EE176640039E939 /* NRNovelHistoryViewController.swift */, F34991152EE176640039E939 /* NRNovelHistoryViewController.swift */,
F3B859362EE6750B0095A9CC /* NRLanguageViewController.swift */, F3B859362EE6750B0095A9CC /* NRLanguageViewController.swift */,
F3B859702EE94A1B0095A9CC /* NRFeedbackViewController.swift */, F3B859702EE94A1B0095A9CC /* NRFeedbackViewController.swift */,
85CF943C2EEFEC14006467E3 /* NRSettingViewController.swift */,
85CF94442EF001AD006467E3 /* NRMyUnlocksViewController.swift */,
85CF94452EF001AD006467E3 /* NRMyUnlocksViewController.xib */,
85CF94482EF004D3006467E3 /* NRMyUnlocksCell.swift */,
85CF94492EF004D3006467E3 /* NRMyUnlocksCell.xib */,
); );
path = VC; path = VC;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1086,6 +1112,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
F34991222EE26EAC0039E939 /* NRAlert.swift */, F34991222EE26EAC0039E939 /* NRAlert.swift */,
85CF944E2EF0F04D006467E3 /* NRUpdatesAlert.swift */,
F349911E2EE26C350039E939 /* NRAlertWindowManager.swift */, F349911E2EE26C350039E939 /* NRAlertWindowManager.swift */,
F34991202EE26C660039E939 /* NRBaseAlert.swift */, F34991202EE26C660039E939 /* NRBaseAlert.swift */,
); );
@ -1259,10 +1286,12 @@
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
85CF94462EF001AD006467E3 /* NRMyUnlocksViewController.xib in Resources */,
03980F8A2ED009EB0006E317 /* Assets.xcassets in Resources */, 03980F8A2ED009EB0006E317 /* Assets.xcassets in Resources */,
039810732ED053BE0006E317 /* Localizable.strings in Resources */, 039810732ED053BE0006E317 /* Localizable.strings in Resources */,
F3B859872EE972F70095A9CC /* NRConsumptionRecordsCell.xib in Resources */, F3B859872EE972F70095A9CC /* NRConsumptionRecordsCell.xib in Resources */,
03980F8C2ED009EB0006E317 /* LaunchScreen.storyboard in Resources */, 03980F8C2ED009EB0006E317 /* LaunchScreen.storyboard in Resources */,
85CF944A2EF004D3006467E3 /* NRMyUnlocksCell.xib in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -1331,6 +1360,7 @@
F34348FF2ED85BF200AA7E70 /* NRReadBatteryView.swift in Sources */, F34348FF2ED85BF200AA7E70 /* NRReadBatteryView.swift in Sources */,
F3B8593C2EE677170095A9CC /* NRLanguageModel.swift in Sources */, F3B8593C2EE677170095A9CC /* NRLanguageModel.swift in Sources */,
039810BA2ED4377E0006E317 /* NRHomeNovelReadWhatCell.swift in Sources */, 039810BA2ED4377E0006E317 /* NRHomeNovelReadWhatCell.swift in Sources */,
85CF94472EF001AD006467E3 /* NRMyUnlocksViewController.swift in Sources */,
F3B8596B2EE91C9F0095A9CC /* NRStoreCoinsCell.swift in Sources */, F3B8596B2EE91C9F0095A9CC /* NRStoreCoinsCell.swift in Sources */,
F3B859712EE94A1B0095A9CC /* NRFeedbackViewController.swift in Sources */, F3B859712EE94A1B0095A9CC /* NRFeedbackViewController.swift in Sources */,
85CF94392EEFE35A006467E3 /* NRLoginManager+Facebook.swift in Sources */, 85CF94392EEFE35A006467E3 /* NRLoginManager+Facebook.swift in Sources */,
@ -1351,6 +1381,7 @@
85CF942B2EED47F2006467E3 /* NRVipRetainItemView.swift in Sources */, 85CF942B2EED47F2006467E3 /* NRVipRetainItemView.swift in Sources */,
0398107C2ED055260006E317 /* AppDelegate+Config.swift in Sources */, 0398107C2ED055260006E317 /* AppDelegate+Config.swift in Sources */,
F34348B52ED5C6F800AA7E70 /* NRTableView.swift in Sources */, F34348B52ED5C6F800AA7E70 /* NRTableView.swift in Sources */,
85CF944B2EF004D3006467E3 /* NRMyUnlocksCell.swift in Sources */,
F34348DB2ED6F80A00AA7E70 /* NRNovelReaderViewController.swift in Sources */, F34348DB2ED6F80A00AA7E70 /* NRNovelReaderViewController.swift in Sources */,
F34990B92EDEB1620039E939 /* NRHomeNovelModuleItem.swift in Sources */, F34990B92EDEB1620039E939 /* NRHomeNovelModuleItem.swift in Sources */,
F34349032ED943C300AA7E70 /* NRReadParser.swift in Sources */, F34349032ED943C300AA7E70 /* NRReadParser.swift in Sources */,
@ -1408,6 +1439,7 @@
85CF94252EEC09AE006467E3 /* NRCoinsPackClaimCell.swift in Sources */, 85CF94252EEC09AE006467E3 /* NRCoinsPackClaimCell.swift in Sources */,
0373D94D2ED583A80017DCC7 /* NRNovelModel.swift in Sources */, 0373D94D2ED583A80017DCC7 /* NRNovelModel.swift in Sources */,
F34348AF2ED5B85300AA7E70 /* NRExploreViewController.swift in Sources */, F34348AF2ED5B85300AA7E70 /* NRExploreViewController.swift in Sources */,
85CF944D2EF0EC00006467E3 /* NRVersionUpdateModel.swift in Sources */,
F3B859732EE94A760095A9CC /* NRAppWebViewController.swift in Sources */, F3B859732EE94A760095A9CC /* NRAppWebViewController.swift in Sources */,
0373D9602ED59DA10017DCC7 /* NRSearchResultCell.swift in Sources */, 0373D9602ED59DA10017DCC7 /* NRSearchResultCell.swift in Sources */,
039810952ED066710006E317 /* UIScreen+NRAdd.swift in Sources */, 039810952ED066710006E317 /* UIScreen+NRAdd.swift in Sources */,
@ -1436,6 +1468,7 @@
0398106D2ED053000006E317 /* NRDefine.swift in Sources */, 0398106D2ED053000006E317 /* NRDefine.swift in Sources */,
F34349282EDD815D00AA7E70 /* NRNovelCatalogCell.swift in Sources */, F34349282EDD815D00AA7E70 /* NRNovelCatalogCell.swift in Sources */,
039810D22ED54F190006E317 /* NRHomeNovelListTextCell.swift in Sources */, 039810D22ED54F190006E317 /* NRHomeNovelListTextCell.swift in Sources */,
85CF943D2EEFEC14006467E3 /* NRSettingViewController.swift in Sources */,
F343492C2EDE72F300AA7E70 /* NRHomeAPI.swift in Sources */, F343492C2EDE72F300AA7E70 /* NRHomeAPI.swift in Sources */,
F3B859372EE6750B0095A9CC /* NRLanguageViewController.swift in Sources */, F3B859372EE6750B0095A9CC /* NRLanguageViewController.swift in Sources */,
F34348E12ED70A2700AA7E70 /* NRNovelDetailHeaderView+NovelCoverInfo.swift in Sources */, F34348E12ED70A2700AA7E70 /* NRNovelDetailHeaderView+NovelCoverInfo.swift in Sources */,
@ -1496,6 +1529,7 @@
F34991092EE169C60039E939 /* NRAboutHeaderView.swift in Sources */, F34991092EE169C60039E939 /* NRAboutHeaderView.swift in Sources */,
039810AE2ED3F3400006E317 /* NRHomeNovelMustReadTodayView.swift in Sources */, 039810AE2ED3F3400006E317 /* NRHomeNovelMustReadTodayView.swift in Sources */,
F34348E92ED7FA9100AA7E70 /* NRNovelDetailRecommandViewController.swift in Sources */, F34348E92ED7FA9100AA7E70 /* NRNovelDetailRecommandViewController.swift in Sources */,
85CF944F2EF0F04D006467E3 /* NRUpdatesAlert.swift in Sources */,
0373D95C2ED598890017DCC7 /* UIStackView+NRAdd.swift in Sources */, 0373D95C2ED598890017DCC7 /* UIStackView+NRAdd.swift in Sources */,
0398107A2ED054E40006E317 /* NRToast.swift in Sources */, 0398107A2ED054E40006E317 /* NRToast.swift in Sources */,
039810A82ED3E7DB0006E317 /* NRHomeNovelListCell.swift in Sources */, 039810A82ED3E7DB0006E317 /* NRHomeNovelListCell.swift in Sources */,
@ -1505,6 +1539,7 @@
F34991182EE1780A0039E939 /* NRNovelHistoryCell.swift in Sources */, F34991182EE1780A0039E939 /* NRNovelHistoryCell.swift in Sources */,
F34990F12EE00F5A0039E939 /* NRKeyedArchiver.swift in Sources */, F34990F12EE00F5A0039E939 /* NRKeyedArchiver.swift in Sources */,
F34349162EDA9FC700AA7E70 /* NRReadSettingBrightnessView.swift in Sources */, F34349162EDA9FC700AA7E70 /* NRReadSettingBrightnessView.swift in Sources */,
85CF943F2EEFF1B2006467E3 /* NRSettingCell.swift in Sources */,
F3B8598D2EEA51FE0095A9CC /* NRRewardCoinsCell.swift in Sources */, F3B8598D2EEA51FE0095A9CC /* NRRewardCoinsCell.swift in Sources */,
F34990F52EE0346B0039E939 /* NRNovelReadBaseViewController.swift in Sources */, F34990F52EE0346B0039E939 /* NRNovelReadBaseViewController.swift in Sources */,
039810642ED04F480006E317 /* NRTargetType.swift in Sources */, 039810642ED04F480006E317 /* NRTargetType.swift in Sources */,
@ -1537,6 +1572,7 @@
F34348D72ED6E7C600AA7E70 /* NRMyListNovelCell.swift in Sources */, F34348D72ED6E7C600AA7E70 /* NRMyListNovelCell.swift in Sources */,
F34349052ED9442300AA7E70 /* NRReadPageModel.swift in Sources */, F34349052ED9442300AA7E70 /* NRReadPageModel.swift in Sources */,
F34990F92EE118FC0039E939 /* NRNovelReadFinishHeaderView.swift in Sources */, F34990F92EE118FC0039E939 /* NRNovelReadFinishHeaderView.swift in Sources */,
85CF94412EEFF62E006467E3 /* NRSettingFooterView.swift in Sources */,
F3B8596F2EE9456E0095A9CC /* NRStoreVipCell.swift in Sources */, F3B8596F2EE9456E0095A9CC /* NRStoreVipCell.swift in Sources */,
F34990C52EDFCD4A0039E939 /* NRMeViewController.swift in Sources */, F34990C52EDFCD4A0039E939 /* NRMeViewController.swift in Sources */,
F349911C2EE190590039E939 /* NRNovelDetailCatalogViewController.swift in Sources */, F349911C2EE190590039E939 /* NRNovelDetailCatalogViewController.swift in Sources */,

View File

@ -13,3 +13,5 @@ let kNRUserInfoDefaultsKey = "kNRUserInfoDefaultsKey"
let kNRNovelReadSetDefaultsKey = "kNRNovelReadSetDefaultsKey" let kNRNovelReadSetDefaultsKey = "kNRNovelReadSetDefaultsKey"
let kNRWaitRestoreIAPDefaultsKey = "kNRWaitRestoreIAPDefaultsKey" let kNRWaitRestoreIAPDefaultsKey = "kNRWaitRestoreIAPDefaultsKey"
let kNRVersionUpdateAlertDefaultsKey = "kNRVersionUpdateAlertDefaultsKey"

View File

@ -44,4 +44,21 @@ struct NRSettingAPI {
} }
} }
static func requestVersionUpdateData() async -> NRVersionUpdateModel? {
await withCheckedContinuation { continuation in
var param = NRNetwork.Parameters(path: "/customer/versionControl")
param.method = .get
param.isToast = false
NRNetwork.request(parameters: param) { (response: NRNetwork.Response<NRVersionUpdateModel>) in
if response.isSuccess {
continuation.resume(returning: response.data)
} else {
continuation.resume(returning: nil)
}
}
}
}
} }

View File

@ -38,6 +38,8 @@ class NRTabBarController: UITabBarController {
self.tabBar.isTranslucent = false self.tabBar.isTranslucent = false
self.selectedIndex = 1 self.selectedIndex = 1
NRTool.checkUpdates()
} }

View File

@ -17,6 +17,15 @@ class NRTableView: UITableView, UIGestureRecognizerDelegate {
override init(frame: CGRect, style: UITableView.Style) { override init(frame: CGRect, style: UITableView.Style) {
super.init(frame: frame, style: style) super.init(frame: frame, style: style)
_init()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
_init()
}
private func _init() {
separatorInset = .init(top: 0, left: 16, bottom: 0, right: 16) separatorInset = .init(top: 0, left: 16, bottom: 0, right: 16)
self.backgroundColor = .clear self.backgroundColor = .clear
self.contentInsetAdjustmentBehavior = .never self.contentInsetAdjustmentBehavior = .never
@ -31,10 +40,6 @@ class NRTableView: UITableView, UIGestureRecognizerDelegate {
} }
} }
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var layoutMargins: UIEdgeInsets { override var layoutMargins: UIEdgeInsets {
set { set {
super.layoutMargins = newValue super.layoutMargins = newValue

View File

@ -34,7 +34,10 @@ class NRAppWebViewController: NRWebViewController {
override func nr_webViewDidFinishLoad(_ webView: NRWebView) { override func nr_webViewDidFinishLoad(_ webView: NRWebView) {
super.nr_webViewDidFinishLoad(webView) super.nr_webViewDidFinishLoad(webView)
receiveDataCount = 0 receiveDataCount = 0
receiveDataFromNative() DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
guard let self = self else { return }
self.receiveDataFromNative()
}
} }
} }
@ -45,7 +48,6 @@ extension NRAppWebViewController {
receiveDataCount += 1 receiveDataCount += 1
if receiveDataCount > 10 { return } if receiveDataCount > 10 { return }
var dic = [ var dic = [
"token" : NRLoginManager.manager.token?.token ?? "", "token" : NRLoginManager.manager.token?.token ?? "",
"time_zone" : NRTargetType.timeZone(), "time_zone" : NRTargetType.timeZone(),

View File

@ -16,9 +16,12 @@ struct NRMeItem {
case settings case settings
case web case web
case feedback case feedback
case wallet
case consumptionRecords case consumptionRecords
case purchaseRecords case purchaseRecords
case rewardCoins case rewardCoins
case accountDeletion
case myUnlocks
} }
var type: ItemType var type: ItemType

View File

@ -0,0 +1,39 @@
//
// NRVersionUpdateModel.swift
// ReaderHive
//
// Created by on 2025/12/16.
//
import UIKit
import SmartCodable
class NRVersionUpdateModel: NSObject, SmartCodable {
required override init() { }
var des: String?
var version_name: String?
var version_code: String?
var force: Bool?
func canUpdate() -> Bool {
guard let version = version_code else { return false }
let result = kNRAPPVersion.compare(version, options: .numeric)
if result == .orderedAscending {
return true
} else {
return false
}
}
static func mappingForKey() -> [SmartKeyTransformer]? {
return [
CodingKeys.des <--- ["description"]
]
}
}

View File

@ -23,6 +23,13 @@ class NRAboutCell: NRTableViewCell {
return label return label
}() }()
private lazy var lineView: NRDashedLineView = {
let view = NRDashedLineView()
view.direction = .horizontal
view.lineColor = UIColor.black.withAlphaComponent(0.15)
return view
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier) super.init(style: style, reuseIdentifier: reuseIdentifier)
@ -30,15 +37,22 @@ class NRAboutCell: NRTableViewCell {
contentView.addSubview(titleLabel) contentView.addSubview(titleLabel)
contentView.addSubview(nr_indicatorImageView) contentView.addSubview(nr_indicatorImageView)
contentView.addSubview(lineView)
titleLabel.snp.makeConstraints { make in titleLabel.snp.makeConstraints { make in
make.centerY.equalToSuperview() make.centerY.equalToSuperview()
make.left.equalToSuperview().offset(16) make.left.equalToSuperview().offset(28)
} }
nr_indicatorImageView.snp.makeConstraints { make in nr_indicatorImageView.snp.makeConstraints { make in
make.centerY.equalToSuperview() make.centerY.equalToSuperview()
make.right.equalToSuperview().offset(-16) make.right.equalToSuperview().offset(-28)
}
lineView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(28)
make.centerX.equalToSuperview()
make.bottom.equalToSuperview()
} }
} }

View File

@ -0,0 +1,60 @@
//
// NRSettingCell.swift
// ReaderHive
//
// Created by on 2025/12/15.
//
import UIKit
import SnapKit
class NRSettingCell: UICollectionViewCell {
var model: NRMeItem? {
didSet {
titleLabel.text = model?.title
}
}
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 14, weight: .medium)
label.textColor = .black
return label
}()
private lazy var indicatorImageView = UIImageView(image: UIImage(named: "arrow_right_icon_06"))
override init(frame: CGRect) {
super.init(frame: frame)
contentView.backgroundColor = .F_2_EFEE
contentView.layer.cornerRadius = 8
contentView.layer.masksToBounds = true
nr_setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension NRSettingCell {
private func nr_setupUI() {
contentView.addSubview(titleLabel)
contentView.addSubview(indicatorImageView)
titleLabel.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.left.equalToSuperview().offset(12)
}
indicatorImageView.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.right.equalToSuperview().offset(-12)
}
}
}

View File

@ -0,0 +1,77 @@
//
// NRSettingFooterView.swift
// ReaderHive
//
// Created by on 2025/12/15.
//
import UIKit
import SnapKit
import HWPanModal
class NRSettingFooterView: UICollectionReusableView {
var userInfo: NRUserInfo? {
didSet {
loginButton.setNeedsUpdateConfiguration()
}
}
private lazy var loginButton: UIButton = {
var configuration = UIButton.Configuration.plain()
configuration.background.image = UIImage(named: "gradient_color_01")
configuration.background.cornerRadius = 24
let button = UIButton(configuration: configuration, primaryAction: UIAction(handler: { [weak self] _ in
guard let self = self else { return }
self.loginButtonHandle()
}))
button.configurationUpdateHandler = { [weak self] button in
guard let self = self else { return }
var title = "Login".localized
if userInfo?.is_tourist == false {
title = "Log Out".localized
}
button.configuration?.attributedTitle = AttributedString(title, attributes: AttributeContainer([
.font : UIFont.font(ofSize: 14, weight: .medium),
.foregroundColor : UIColor.white
]))
}
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(loginButton)
loginButton.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.centerX.equalToSuperview()
make.bottom.equalToSuperview()
make.height.equalTo(48)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func loginButtonHandle() {
guard let userInfo = userInfo else { return }
if userInfo.is_tourist == false {
let alert = NRAlert(title: "Log Out".localized, detail: "logout_alert_text".localized, topIconImage: UIImage(named: "alert_top_icon_02"), highlightButtonText: "Confirm".localized)
alert.highlightHandle = {
NRLoginManager.manager.logout(completer: nil)
}
alert.show()
} else {
let view = NRLoginView()
view.present(in: nil)
}
}
}

View File

@ -13,9 +13,11 @@ class NRMeViewController: NRViewController {
private lazy var dataArr: [NRMeItem] = { private lazy var dataArr: [NRMeItem] = {
let arr = [ let arr = [
NRMeItem(type: .history, icon: UIImage(named: "history_icon_01"), title: "History".localized), NRMeItem(type: .history, icon: UIImage(named: "history_icon_01"), title: "History".localized),
NRMeItem(type: .wallet, icon: UIImage(named: "wallet_icon_01"), title: "My Wallet".localized),
// NRMeItem(type: .language, icon: UIImage(named: "language_icon_01"), title: "Language".localized), // NRMeItem(type: .language, icon: UIImage(named: "language_icon_01"), title: "Language".localized),
NRMeItem(type: .feedback, icon: UIImage(named: "feedback_icon_01"), title: "Feedback".localized),
NRMeItem(type: .settings, icon: UIImage(named: "settings_icon_02"), title: "Settings".localized),
NRMeItem(type: .about, icon: UIImage(named: "about_icon_01"), title: "About".localized), NRMeItem(type: .about, icon: UIImage(named: "about_icon_01"), title: "About".localized),
NRMeItem(type: .feedback, icon: UIImage(named: "feedback_icon_01"), title: "Feedback".localized)
] ]
return arr return arr
}() }()
@ -115,6 +117,14 @@ extension NRMeViewController: UITableViewDelegate, UITableViewDataSource {
let vc = NRFeedbackViewController() let vc = NRFeedbackViewController()
self.navigationController?.pushViewController(vc, animated: true) self.navigationController?.pushViewController(vc, animated: true)
case .wallet:
let vc = NRWalletViewController()
self.navigationController?.pushViewController(vc, animated: true)
case .settings:
let vc = NRSettingViewController()
self.navigationController?.pushViewController(vc, animated: true)
default: default:
break break
} }

View File

@ -0,0 +1,40 @@
//
// NRMyUnlocksCell.swift
// ReaderHive
//
// Created by on 2025/12/15.
//
import UIKit
class NRMyUnlocksCell: NRTableViewCell {
@IBOutlet weak var bgView: UIView!
@IBOutlet weak var coverImageView: NRImageView!
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var chLabel: UILabel!
@IBOutlet weak var countLabel: UILabel!
@IBOutlet weak var countTextLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
bgView.layer.borderColor = UIColor.F_2_EFEE.cgColor
countTextLabel.text = "Unlocked Chapters".localized
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}

View File

@ -0,0 +1,125 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="24412" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24405"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="101" id="KGk-i7-Jjw" customClass="NRMyUnlocksCell" customModule="ReaderHive" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="527" height="101"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="527" height="101"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="mr9-p0-36S">
<rect key="frame" x="16" y="0.0" width="495" height="89"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="ucn-1k-vaw" customClass="NRImageView" customModule="ReaderHive" customModuleProvider="target">
<rect key="frame" x="12" y="14.666666666666664" width="40" height="59.999999999999993"/>
<constraints>
<constraint firstAttribute="width" constant="40" id="OlZ-tQ-6Yl"/>
<constraint firstAttribute="height" constant="60" id="cZV-LM-OFV"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
<integer key="value" value="4"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="boolean" keyPath="layer.masksToBounds" value="YES"/>
</userDefinedRuntimeAttributes>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="KsK-Sd-ade">
<rect key="frame" x="63.999999999999993" y="17.666666666666668" width="31.666666666666664" height="14.333333333333332"/>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="12"/>
<color key="textColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="y6o-6t-VhX">
<rect key="frame" x="64" y="57.333333333333336" width="31" height="14.333333333333336"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<color key="textColor" name="black_0.25"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="g9j-Vy-q4R">
<rect key="frame" x="423" y="22.666666666666671" width="1" height="44"/>
<color key="backgroundColor" name="#F2EFEE"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="0CI-jg-Nah"/>
<constraint firstAttribute="width" constant="1" id="bsi-Sg-i1k"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="17" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="z8T-ym-9e3">
<rect key="frame" x="450.66666666666669" y="17" width="17" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Unlocked Chapters" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6Jv-O6-x8q">
<rect key="frame" x="435" y="50" width="48.333333333333314" height="24"/>
<fontDescription key="fontDescription" type="system" pointSize="10"/>
<color key="textColor" name="black_0.25"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="g9j-Vy-q4R" firstAttribute="centerY" secondItem="mr9-p0-36S" secondAttribute="centerY" id="0BF-IP-WeR"/>
<constraint firstItem="6Jv-O6-x8q" firstAttribute="centerX" secondItem="z8T-ym-9e3" secondAttribute="centerX" id="0WW-tU-0Wh"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="6Jv-O6-x8q" secondAttribute="trailing" id="1bU-Sh-8Gg"/>
<constraint firstItem="z8T-ym-9e3" firstAttribute="top" secondItem="mr9-p0-36S" secondAttribute="top" constant="17" id="3Dr-hL-l6z"/>
<constraint firstAttribute="bottom" secondItem="6Jv-O6-x8q" secondAttribute="bottom" constant="15" id="4ou-tu-khV"/>
<constraint firstItem="ucn-1k-vaw" firstAttribute="leading" secondItem="mr9-p0-36S" secondAttribute="leading" constant="12" id="CKV-G5-knr"/>
<constraint firstAttribute="trailing" secondItem="z8T-ym-9e3" secondAttribute="centerX" constant="36" id="IL0-Q6-oiG"/>
<constraint firstItem="ucn-1k-vaw" firstAttribute="centerY" secondItem="mr9-p0-36S" secondAttribute="centerY" id="OEJ-N6-Sal"/>
<constraint firstItem="KsK-Sd-ade" firstAttribute="leading" secondItem="ucn-1k-vaw" secondAttribute="trailing" constant="12" id="RhP-Ee-etx"/>
<constraint firstItem="KsK-Sd-ade" firstAttribute="top" secondItem="ucn-1k-vaw" secondAttribute="top" constant="3" id="VMo-Gd-Paa"/>
<constraint firstAttribute="trailing" secondItem="g9j-Vy-q4R" secondAttribute="trailing" constant="71" id="fZH-kx-wbn"/>
<constraint firstItem="y6o-6t-VhX" firstAttribute="bottom" secondItem="ucn-1k-vaw" secondAttribute="bottom" constant="-3" id="iEd-aO-PNt"/>
<constraint firstItem="g9j-Vy-q4R" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="KsK-Sd-ade" secondAttribute="trailing" constant="10" id="lxg-9l-zGl"/>
<constraint firstItem="y6o-6t-VhX" firstAttribute="leading" secondItem="KsK-Sd-ade" secondAttribute="leading" id="u2Z-iO-WtX"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
<integer key="value" value="8"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="boolean" keyPath="layer.masksToBounds" value="YES"/>
<userDefinedRuntimeAttribute type="number" keyPath="layer.borderWidth">
<integer key="value" value="1"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</view>
</subviews>
<constraints>
<constraint firstItem="mr9-p0-36S" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="16" id="NDz-ZR-vrc"/>
<constraint firstItem="mr9-p0-36S" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" id="NVs-LD-lK7"/>
<constraint firstAttribute="bottom" secondItem="mr9-p0-36S" secondAttribute="bottom" constant="12" id="ijg-tG-Ggs"/>
<constraint firstAttribute="trailing" secondItem="mr9-p0-36S" secondAttribute="trailing" constant="16" id="ltu-3u-4O8"/>
</constraints>
</tableViewCellContentView>
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
<connections>
<outlet property="bgView" destination="mr9-p0-36S" id="I00-Cc-E1d"/>
<outlet property="chLabel" destination="y6o-6t-VhX" id="m3N-9f-ft8"/>
<outlet property="countLabel" destination="z8T-ym-9e3" id="JcG-HX-bwQ"/>
<outlet property="countTextLabel" destination="6Jv-O6-x8q" id="vNd-9T-Puu"/>
<outlet property="coverImageView" destination="ucn-1k-vaw" id="dOz-Fi-qF1"/>
<outlet property="titleLabel" destination="KsK-Sd-ade" id="gDp-mE-nu6"/>
</connections>
<point key="canvasLocation" x="220.61068702290075" y="130.63380281690141"/>
</tableViewCell>
</objects>
<resources>
<namedColor name="#F2EFEE">
<color red="0.94901960784313721" green="0.93725490196078431" blue="0.93333333333333335" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<namedColor name="black_0.25">
<color red="0.0" green="0.0" blue="0.0" alpha="0.25" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
</resources>
</document>

View File

@ -0,0 +1,50 @@
//
// NRMyUnlocksViewController.swift
// ReaderHive
//
// Created by on 2025/12/15.
//
import UIKit
class NRMyUnlocksViewController: NRViewController {
@IBOutlet weak var tableView: NRTableView!
override func viewDidLoad() {
super.viewDidLoad()
self.title = "My Unlocks".localized
self.backgroundImageView.isHidden = true
configNavigationBack("arrow_left_icon_05")
tableView.contentInset = .init(top: 24, left: 0, bottom: UIScreen.safeBottom, right: 0)
tableView.register(UINib(nibName: "NRMyUnlocksCell", bundle: nil), forCellReuseIdentifier: "cell")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: true)
self.nr_setNavigationStyle(titleColor: UINavigationBar.titleBlackColor)
}
}
//MARK: UITableViewDelegate UITableViewDataSource
extension NRMyUnlocksViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! NRMyUnlocksCell
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
}

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="24412" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24405"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="NRMyUnlocksViewController" customModule="ReaderHive" customModuleProvider="target">
<connections>
<outlet property="tableView" destination="IID-sC-OxN" id="kxa-AT-Qtc"/>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" style="plain" separatorStyle="none" rowHeight="96" estimatedRowHeight="-1" sectionHeaderHeight="-1" estimatedSectionHeaderHeight="-1" sectionFooterHeight="-1" estimatedSectionFooterHeight="-1" translatesAutoresizingMaskIntoConstraints="NO" id="IID-sC-OxN" customClass="NRTableView" customModule="ReaderHive" customModuleProvider="target">
<rect key="frame" x="0.0" y="118" width="393" height="734"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<connections>
<outlet property="dataSource" destination="-1" id="MXx-r6-xNG"/>
<outlet property="delegate" destination="-1" id="KZs-7w-zNd"/>
</connections>
</tableView>
</subviews>
<viewLayoutGuide key="safeArea" id="fnl-2z-Ty3"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="IID-sC-OxN" secondAttribute="bottom" id="3i9-8l-59c"/>
<constraint firstItem="IID-sC-OxN" firstAttribute="top" secondItem="fnl-2z-Ty3" secondAttribute="top" id="Dw7-ae-b9O"/>
<constraint firstItem="IID-sC-OxN" firstAttribute="leading" secondItem="fnl-2z-Ty3" secondAttribute="leading" id="IKk-Qb-pdG"/>
<constraint firstItem="fnl-2z-Ty3" firstAttribute="trailing" secondItem="IID-sC-OxN" secondAttribute="trailing" id="OhJ-fD-Zd0"/>
</constraints>
<point key="canvasLocation" x="140" y="94"/>
</view>
</objects>
<resources>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>

View File

@ -0,0 +1,131 @@
//
// NRSettingViewController.swift
// ReaderHive
//
// Created by on 2025/12/15.
//
import UIKit
import SnapKit
class NRSettingViewController: NRViewController {
private lazy var dataArr: [NRMeItem] = getDataArr()
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
let layout = UICollectionViewFlowLayout()
layout.itemSize = .init(width: UIScreen.width - 32, height: 60)
layout.footerReferenceSize = .init(width: UIScreen.width, height: 72)
layout.minimumLineSpacing = 12
return layout
}()
private lazy var collectionView: NRCollectionView = {
let collectionView = NRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.contentInset = .init(top: 24, left: 0, bottom: UIScreen.safeBottom + 10, right: 0)
collectionView.register(NRSettingCell.self, forCellWithReuseIdentifier: "cell")
collectionView.register(NRSettingFooterView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "footer")
return collectionView
}()
@MainActor deinit {
NotificationCenter.default.removeObserver(self)
}
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Settings".localized
self.backgroundImageView.isHidden = true
configNavigationBack("arrow_left_icon_05")
NotificationCenter.default.addObserver(self, selector: #selector(userInfoUpdateNotification), name: NRLoginManager.userInfoUpdateNotification, object: nil)
nr_setupUI()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: true)
self.nr_setNavigationStyle(titleColor: UINavigationBar.titleBlackColor)
}
@objc private func userInfoUpdateNotification() {
dataArr = getDataArr()
self.collectionView.reloadData()
}
}
extension NRSettingViewController {
private func nr_setupUI() {
view.addSubview(collectionView)
collectionView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.top.equalToSuperview().offset(UIScreen.navBarHeight)
}
}
}
//MARK: UICollectionViewDelegate UICollectionViewDataSource
extension NRSettingViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! NRSettingCell
cell.model = dataArr[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.dataArr.count
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
if kind == UICollectionView.elementKindSectionFooter {
let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "footer", for: indexPath) as! NRSettingFooterView
view.userInfo = NRLoginManager.manager.userInfo
return view
}
return UICollectionReusableView()
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let model = dataArr[indexPath.row]
switch model.type {
case .accountDeletion:
let vc = NRAppWebViewController()
vc.webUrl = kNRLogoutWebUrl
self.navigationController?.pushViewController(vc, animated: true)
case .myUnlocks:
let vc = NRMyUnlocksViewController()
self.navigationController?.pushViewController(vc, animated: true)
default:
break
}
}
}
extension NRSettingViewController {
private func getDataArr() -> [NRMeItem] {
if NRLoginManager.manager.userInfo?.is_tourist == true {
return [
NRMeItem(type: .myUnlocks, title: "My Unlocks".localized),
]
} else {
return [
NRMeItem(type: .accountDeletion, title: "Account Deletion".localized),
NRMeItem(type: .myUnlocks, title: "My Unlocks".localized),
]
}
}
}

View File

@ -23,6 +23,13 @@ class NRWalletCell: NRTableViewCell {
return label return label
}() }()
private lazy var lineView: NRDashedLineView = {
let view = NRDashedLineView()
view.direction = .horizontal
view.lineColor = UIColor.black.withAlphaComponent(0.15)
return view
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier) super.init(style: style, reuseIdentifier: reuseIdentifier)
@ -30,15 +37,22 @@ class NRWalletCell: NRTableViewCell {
contentView.addSubview(titleLabel) contentView.addSubview(titleLabel)
contentView.addSubview(nr_indicatorImageView) contentView.addSubview(nr_indicatorImageView)
contentView.addSubview(lineView)
titleLabel.snp.makeConstraints { make in titleLabel.snp.makeConstraints { make in
make.centerY.equalToSuperview() make.centerY.equalToSuperview()
make.left.equalToSuperview().offset(16) make.left.equalToSuperview().offset(28)
} }
nr_indicatorImageView.snp.makeConstraints { make in nr_indicatorImageView.snp.makeConstraints { make in
make.centerY.equalToSuperview() make.centerY.equalToSuperview()
make.right.equalToSuperview().offset(-16) make.right.equalToSuperview().offset(-28)
}
lineView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(28)
make.centerX.equalToSuperview()
make.bottom.equalToSuperview()
} }
} }

View File

@ -55,6 +55,12 @@ class NRWalletHeaderView: UITableViewHeaderFooterView {
return view return view
}() }()
private lazy var lineView: UIView = {
let view = UIView()
view.backgroundColor = .black.withAlphaComponent(0.15)
return view
}()
override init(reuseIdentifier: String?) { override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier) super.init(reuseIdentifier: reuseIdentifier)
@ -74,6 +80,7 @@ extension NRWalletHeaderView {
bgView.addSubview(storeButton) bgView.addSubview(storeButton)
bgView.addSubview(coinsView) bgView.addSubview(coinsView)
bgView.addSubview(bonusCoinsView) bgView.addSubview(bonusCoinsView)
bgView.addSubview(lineView)
bgView.snp.makeConstraints { make in bgView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16) make.left.equalToSuperview().offset(16)
@ -101,6 +108,13 @@ extension NRWalletHeaderView {
make.width.equalTo(coinsView) make.width.equalTo(coinsView)
make.left.equalTo(coinsView.snp.right).offset(0) make.left.equalTo(coinsView.snp.right).offset(0)
} }
lineView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.centerY.equalTo(coinsView)
make.width.equalTo(1)
make.height.equalTo(36)
}
} }
} }

View File

@ -13,27 +13,46 @@ class NRAlert: NRBaseAlert {
var closeHandle: (() -> Void)? var closeHandle: (() -> Void)?
var highlightHandle: (() -> Void)? var highlightHandle: (() -> Void)?
private lazy var bgView: UIImageView = { private(set) lazy var bgView: UIImageView = {
let view = UIImageView(image: UIImage(named: "alert_bg_image_01")) let view = UIImageView(image: UIImage(named: "alert_bg_image_01"))
view.isUserInteractionEnabled = true view.isUserInteractionEnabled = true
view.setContentHuggingPriority(.required, for: .horizontal)
view.setContentCompressionResistancePriority(.required, for: .horizontal)
return view return view
}() }()
private lazy var topIconImageView: UIImageView = { private(set) lazy var topIconImageView: UIImageView = {
let imageView = UIImageView() let imageView = UIImageView()
return imageView return imageView
}() }()
private lazy var closeButton: UIButton = { private(set) lazy var closeButton: UIButton = {
let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in
self?.closeHandle?() self?.handleCloseButton()
self?.dismiss()
})) }))
button.setImage(UIImage(named: "close_icon_01"), for: .normal) button.setImage(UIImage(named: "close_icon_01"), for: .normal)
return button return button
}() }()
private(set) lazy var highlightButton: UIButton = {
let button = NRGradientButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in
guard let self = self else { return }
self.handleHighlightButton()
}))
button.setTitleColor(.white, for: .normal)
button.titleLabel?.font = .font(ofSize: 14, weight: .medium)
button.colors = [UIColor.F_3912_F.cgColor, UIColor.FF_4_A_4_A.cgColor, UIColor.FA_9_B_1_F.cgColor]
button.startPoint = .init(x: 0, y: 0.5)
button.endPoint = .init(x: 1, y: 0.5)
button.layer.cornerRadius = 24
button.layer.masksToBounds = true
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
nr_setupUI()
}
init(title: String?, detail: String?, topIconImage: UIImage?, highlightButtonText: String?) { init(title: String?, detail: String?, topIconImage: UIImage?, highlightButtonText: String?) {
super.init(frame: .zero) super.init(frame: .zero)
@ -74,20 +93,8 @@ class NRAlert: NRBaseAlert {
if let text = highlightButtonText { if let text = highlightButtonText {
let button = NRGradientButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in highlightButton.setTitle(text, for: .normal)
guard let self = self else { return } buttonStackView.addArrangedSubview(highlightButton)
self.highlightHandle?()
self.dismiss()
}))
button.setTitle(text, for: .normal)
button.setTitleColor(.white, for: .normal)
button.titleLabel?.font = .font(ofSize: 14, weight: .medium)
button.colors = [UIColor.F_3912_F.cgColor, UIColor.FF_4_A_4_A.cgColor, UIColor.FA_9_B_1_F.cgColor]
button.startPoint = .init(x: 0, y: 0.5)
button.endPoint = .init(x: 1, y: 0.5)
button.layer.cornerRadius = 24
button.layer.masksToBounds = true
buttonStackView.addArrangedSubview(button)
} }
bgView.addSubview(stackView) bgView.addSubview(stackView)
@ -106,11 +113,21 @@ class NRAlert: NRBaseAlert {
make.bottom.equalToSuperview().offset(-16) make.bottom.equalToSuperview().offset(-16)
make.height.equalTo(48) make.height.equalTo(48)
} }
} }
@MainActor required init?(coder: NSCoder) { @MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented") super.init(coder: coder)
}
func handleHighlightButton() {
self.highlightHandle?()
self.dismiss()
}
func handleCloseButton() {
self.closeHandle?()
self.dismiss()
} }
} }

View File

@ -0,0 +1,126 @@
//
// NRUpdatesAlert.swift
// ReaderHive
//
// Created by on 2025/12/16.
//
import UIKit
import SnapKit
class NRUpdatesAlert: NRAlert {
init(model: NRVersionUpdateModel) {
super.init(frame: .zero)
closeButton.isHidden = model.force ?? false
topIconImageView.image = UIImage(named: "alert_top_icon_03")
let stackView = UIStackView()
stackView.axis = .vertical
stackView.alignment = .center
stackView.spacing = 12
let titleView = UIView()
let titleLabel = UILabel()
titleLabel.textAlignment = .center
titleLabel.numberOfLines = 0
titleLabel.font = .font(ofSize: 20, weight: .medium)
titleLabel.textColor = .F_9710_D
titleLabel.text = "New Version".localized
let versionView = UIView()
versionView.backgroundColor = .F_9710_D
versionView.layer.cornerRadius = 9
versionView.layer.masksToBounds = true
let versionLabel = UILabel()
versionLabel.font = .font(ofSize: 12, weight: .bold)
versionLabel.textColor = .FFEFD_4
versionLabel.text = "v\(model.version_name ?? "")"
titleView.addSubview(titleLabel)
titleView.addSubview(versionView)
versionView.addSubview(versionLabel)
titleLabel.snp.makeConstraints { make in
make.left.top.bottom.equalToSuperview()
}
versionView.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.height.equalTo(18)
make.left.equalTo(titleLabel.snp.right).offset(8)
make.right.equalToSuperview()
}
versionLabel.snp.makeConstraints { make in
make.center.equalToSuperview()
make.left.equalToSuperview().offset(7)
}
stackView.addArrangedSubview(titleView)
if let detail = model.des {
let label = UILabel()
label.textAlignment = .center
label.numberOfLines = 0
label.font = .font(ofSize: 14, weight: .regular)
label.textColor = .black.withAlphaComponent(0.5)
label.text = detail.localized
stackView.addArrangedSubview(label)
}
let buttonStackView = UIStackView()
buttonStackView.axis = .horizontal
buttonStackView.spacing = 19
buttonStackView.distribution = .fillEqually
highlightButton.setTitle("Update Now".localized, for: .normal)
buttonStackView.addArrangedSubview(highlightButton)
bgView.addSubview(stackView)
bgView.addSubview(buttonStackView)
stackView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.right.lessThanOrEqualToSuperview().offset(-16)
make.top.equalToSuperview().offset(100)
}
buttonStackView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.centerX.equalToSuperview()
make.top.equalTo(stackView.snp.bottom).offset(36)
make.bottom.equalToSuperview().offset(-16)
make.height.equalTo(48)
}
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func handleHighlightButton() {
// super.handleHighlightButton()
guard let url = URL(string: "https://apps.apple.com/app/id6756297764") else { return }
let application = UIApplication.shared
if application.canOpenURL(url) {
application.open(url)
}
}
override func handleCloseButton() {
super.handleCloseButton()
}
}

View File

@ -54,3 +54,55 @@ class NRTool {
} }
} }
extension NRTool {
static weak var updateAlert: UIView?
static func checkUpdates() {
guard self.self.updateAlert == nil else { return }
Task {
guard let model = await NRSettingAPI.requestVersionUpdateData() else {
// FAAdjustStateManager.manager.upgradeAlertFinish = true
// FATool.sceneDelegate?.retryHandleOpenAppMessage()
return
}
self.showUpdatesAlert(model)
}
}
static func showUpdatesAlert(_ model: NRVersionUpdateModel) {
guard self.self.updateAlert == nil else { return }
#if !DEBUG
if model.force != true {
if let date = UserDefaults.standard.object(forKey: kNRVersionUpdateAlertDefaultsKey) as? Date {
if date.nr_isToday {
// FAAdjustStateManager.manager.upgradeAlertFinish = true
// FATool.sceneDelegate?.retryHandleOpenAppMessage()
return
}
}
UserDefaults.standard.set(Date(), forKey: kNRVersionUpdateAlertDefaultsKey)
}
#endif // !DEBUG
if model.canUpdate() {
// FAStatAPI.requestEventStat(orderCode: nil, shortPlayId: nil, videoId: nil, eventKey: .forceUpdate, errorMsg: nil, otherParamenters: [
// "is_force" : model.force == true ? "true" : "false"
// ])
let view = NRUpdatesAlert(model: model)
view.show()
self.updateAlert = view
} else {
// FAAdjustStateManager.manager.upgradeAlertFinish = true
// FATool.sceneDelegate?.retryHandleOpenAppMessage()
}
}
}

View File

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "0.250",
"blue" : "0x00",
"green" : "0x00",
"red" : "0x00"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "logout@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "logout@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "new version@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "new version@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

View File

@ -5,12 +5,12 @@
"scale" : "1x" "scale" : "1x"
}, },
{ {
"filename" : "My Wallet@2x.png", "filename" : "Feedback@2x.png",
"idiom" : "universal", "idiom" : "universal",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"filename" : "My Wallet@3x.png", "filename" : "Feedback@3x.png",
"idiom" : "universal", "idiom" : "universal",
"scale" : "3x" "scale" : "3x"
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Settings@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Settings@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "My Wallet@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "My Wallet@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -131,8 +131,18 @@
"Buy Now" = "Buy Now"; "Buy Now" = "Buy Now";
"Login with Apple" = "Login with Apple"; "Login with Apple" = "Login with Apple";
"Login with Facebook" = "Login with Facebook"; "Login with Facebook" = "Login with Facebook";
"My Wallet" = "My Wallet";
"Account Deletion" = "Account Deletion";
"My Unlocks" = "My Unlocks";
"Login" = "Login";
"Log Out" = "Log Out";
"Confirm" = "Confirm";
"Unlocked Chapters" = "Unlocked Chapters";
"Update Now" = "Update Now";
"New Version" = "New Version";
"retain_alert_text" = "Unlock every show you love!"; "retain_alert_text" = "Unlock every show you love!";
"logout_alert_text" = "Are you sure you want to log out?";
"vip_tip_01" = "Unlimited access to all series"; "vip_tip_01" = "Unlimited access to all series";