登录功能完成

This commit is contained in:
澜声世纪 2025-12-15 15:02:56 +08:00
parent c943a249e5
commit 5667b11a55
31 changed files with 744 additions and 10 deletions

View File

@ -33,5 +33,6 @@ target 'ReaderHive' do
pod 'Toast' pod 'Toast'
pod 'SVProgressHUD' pod 'SVProgressHUD'
pod 'FDFullscreenPopGesture' pod 'FDFullscreenPopGesture'
pod 'ZLPhotoBrowser'
end end

View File

@ -96,6 +96,12 @@
85CF94292EED4664006467E3 /* NRVipRetainAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF94282EED4664006467E3 /* NRVipRetainAlert.swift */; }; 85CF94292EED4664006467E3 /* NRVipRetainAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF94282EED4664006467E3 /* NRVipRetainAlert.swift */; };
85CF942B2EED47F2006467E3 /* NRVipRetainItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF942A2EED47F2006467E3 /* NRVipRetainItemView.swift */; }; 85CF942B2EED47F2006467E3 /* NRVipRetainItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF942A2EED47F2006467E3 /* NRVipRetainItemView.swift */; };
85CF942D2EED5204006467E3 /* NRPayAlertModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF942C2EED5204006467E3 /* NRPayAlertModel.swift */; }; 85CF942D2EED5204006467E3 /* NRPayAlertModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF942C2EED5204006467E3 /* NRPayAlertModel.swift */; };
85CF942F2EEFB2CF006467E3 /* NRLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF942E2EEFB2CF006467E3 /* NRLoginView.swift */; };
85CF94312EEFDA88006467E3 /* NRLoginManager+Apple.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF94302EEFDA82006467E3 /* NRLoginManager+Apple.swift */; };
85CF94332EEFDAF4006467E3 /* NRThirdSignModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CF94322EEFDAF4006467E3 /* NRThirdSignModel.swift */; };
85CF94372EEFE27E006467E3 /* FacebookLogin in Frameworks */ = {isa = PBXBuildFile; productRef = 85CF94362EEFE27E006467E3 /* FacebookLogin */; };
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 */; };
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 */; };
@ -327,6 +333,12 @@
85CF94282EED4664006467E3 /* NRVipRetainAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRVipRetainAlert.swift; sourceTree = "<group>"; }; 85CF94282EED4664006467E3 /* NRVipRetainAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRVipRetainAlert.swift; sourceTree = "<group>"; };
85CF942A2EED47F2006467E3 /* NRVipRetainItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRVipRetainItemView.swift; sourceTree = "<group>"; }; 85CF942A2EED47F2006467E3 /* NRVipRetainItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRVipRetainItemView.swift; sourceTree = "<group>"; };
85CF942C2EED5204006467E3 /* NRPayAlertModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRPayAlertModel.swift; sourceTree = "<group>"; }; 85CF942C2EED5204006467E3 /* NRPayAlertModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRPayAlertModel.swift; sourceTree = "<group>"; };
85CF942E2EEFB2CF006467E3 /* NRLoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRLoginView.swift; sourceTree = "<group>"; };
85CF94302EEFDA82006467E3 /* NRLoginManager+Apple.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NRLoginManager+Apple.swift"; sourceTree = "<group>"; };
85CF94322EEFDAF4006467E3 /* NRThirdSignModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRThirdSignModel.swift; 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>"; };
85CF943A2EEFE947006467E3 /* AppDelegate+Open.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Open.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>"; };
@ -469,6 +481,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
85CF94372EEFE27E006467E3 /* FacebookLogin in Frameworks */,
67DC33BD353DB9F2D4C0FFE8 /* Pods_ReaderHive.framework in Frameworks */, 67DC33BD353DB9F2D4C0FFE8 /* Pods_ReaderHive.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@ -588,6 +601,7 @@
03980F862ED009EB0006E317 /* ReaderHive */ = { 03980F862ED009EB0006E317 /* ReaderHive */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
85CF94342EEFDFAC006467E3 /* ReaderHive.entitlements */,
03980F8F2ED00ACD0006E317 /* Delegate */, 03980F8F2ED00ACD0006E317 /* Delegate */,
039810562ED046030006E317 /* Base */, 039810562ED046030006E317 /* Base */,
039810752ED054090006E317 /* Class */, 039810752ED054090006E317 /* Class */,
@ -615,6 +629,7 @@
03980F7D2ED009EB0006E317 /* AppDelegate.swift */, 03980F7D2ED009EB0006E317 /* AppDelegate.swift */,
03980F842ED009EB0006E317 /* SceneDelegate.swift */, 03980F842ED009EB0006E317 /* SceneDelegate.swift */,
0398107B2ED0551C0006E317 /* AppDelegate+Config.swift */, 0398107B2ED0551C0006E317 /* AppDelegate+Config.swift */,
85CF943A2EEFE947006467E3 /* AppDelegate+Open.swift */,
); );
path = Delegate; path = Delegate;
sourceTree = "<group>"; sourceTree = "<group>";
@ -728,8 +743,11 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
0398107E2ED055D10006E317 /* NRLoginManager.swift */, 0398107E2ED055D10006E317 /* NRLoginManager.swift */,
85CF94302EEFDA82006467E3 /* NRLoginManager+Apple.swift */,
85CF94382EEFE34F006467E3 /* NRLoginManager+Facebook.swift */,
039810802ED056090006E317 /* NRLoginToken.swift */, 039810802ED056090006E317 /* NRLoginToken.swift */,
039810822ED0563D0006E317 /* NRUserInfo.swift */, 039810822ED0563D0006E317 /* NRUserInfo.swift */,
85CF94322EEFDAF4006467E3 /* NRThirdSignModel.swift */,
); );
path = Login; path = Login;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1027,6 +1045,7 @@
F349910A2EE16B520039E939 /* NRAboutCell.swift */, F349910A2EE16B520039E939 /* NRAboutCell.swift */,
F34991172EE1780A0039E939 /* NRNovelHistoryCell.swift */, F34991172EE1780A0039E939 /* NRNovelHistoryCell.swift */,
F3B859382EE676610095A9CC /* NRLanguageCell.swift */, F3B859382EE676610095A9CC /* NRLanguageCell.swift */,
85CF942E2EEFB2CF006467E3 /* NRLoginView.swift */,
); );
path = V; path = V;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1222,6 +1241,9 @@
); );
mainGroup = 03980F5C2ED009E30006E317; mainGroup = 03980F5C2ED009E30006E317;
minimizedProjectReferenceProxies = 1; minimizedProjectReferenceProxies = 1;
packageReferences = (
85CF94352EEFE27E006467E3 /* XCRemoteSwiftPackageReference "facebook-ios-sdk" */,
);
preferredProjectObjectVersion = 77; preferredProjectObjectVersion = 77;
productRefGroup = 03980F662ED009E30006E317 /* Products */; productRefGroup = 03980F662ED009E30006E317 /* Products */;
projectDirPath = ""; projectDirPath = "";
@ -1299,9 +1321,11 @@
files = ( files = (
F34991252EE27DB60039E939 /* NRGradientButton.swift in Sources */, F34991252EE27DB60039E939 /* NRGradientButton.swift in Sources */,
F34348FD2ED8561300AA7E70 /* NRNovelReadContentBottomView.swift in Sources */, F34348FD2ED8561300AA7E70 /* NRNovelReadContentBottomView.swift in Sources */,
85CF94332EEFDAF4006467E3 /* NRThirdSignModel.swift in Sources */,
F34348C52ED6CA4D00AA7E70 /* NRExploreNovelMenuDataSource.swift in Sources */, F34348C52ED6CA4D00AA7E70 /* NRExploreNovelMenuDataSource.swift in Sources */,
F343492A2EDD93AD00AA7E70 /* NRReadSettingFontView.swift in Sources */, F343492A2EDD93AD00AA7E70 /* NRReadSettingFontView.swift in Sources */,
039810982ED066B20006E317 /* NRTool.swift in Sources */, 039810982ED066B20006E317 /* NRTool.swift in Sources */,
85CF943B2EEFE94C006467E3 /* AppDelegate+Open.swift in Sources */,
F34991272EE2826D0039E939 /* NRNovelReadViewModel+Data.swift in Sources */, F34991272EE2826D0039E939 /* NRNovelReadViewModel+Data.swift in Sources */,
F34348CD2ED6DD0900AA7E70 /* NRNovelGenresCell.swift in Sources */, F34348CD2ED6DD0900AA7E70 /* NRNovelGenresCell.swift in Sources */,
F34348FF2ED85BF200AA7E70 /* NRReadBatteryView.swift in Sources */, F34348FF2ED85BF200AA7E70 /* NRReadBatteryView.swift in Sources */,
@ -1309,6 +1333,7 @@
039810BA2ED4377E0006E317 /* NRHomeNovelReadWhatCell.swift in Sources */, 039810BA2ED4377E0006E317 /* NRHomeNovelReadWhatCell.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 */,
F34348C72ED6CCBC00AA7E70 /* NRExploreNovelContentListViewController.swift in Sources */, F34348C72ED6CCBC00AA7E70 /* NRExploreNovelContentListViewController.swift in Sources */,
039810B82ED431780006E317 /* NRHomeNovelReadWhatView.swift in Sources */, 039810B82ED431780006E317 /* NRHomeNovelReadWhatView.swift in Sources */,
F34349142EDA9AE900AA7E70 /* NRNovelReadSettingView.swift in Sources */, F34349142EDA9AE900AA7E70 /* NRNovelReadSettingView.swift in Sources */,
@ -1435,10 +1460,12 @@
F3B859422EE678FB0095A9CC /* NRSettingAPI.swift in Sources */, F3B859422EE678FB0095A9CC /* NRSettingAPI.swift in Sources */,
0373D9522ED58A950017DCC7 /* NRSearchViewModel.swift in Sources */, 0373D9522ED58A950017DCC7 /* NRSearchViewModel.swift in Sources */,
F34990BF2EDEDDCF0039E939 /* NRCategoryModel.swift in Sources */, F34990BF2EDEDDCF0039E939 /* NRCategoryModel.swift in Sources */,
85CF942F2EEFB2CF006467E3 /* NRLoginView.swift in Sources */,
F34349242EDD3C2400AA7E70 /* NRNovelReadBottomView.swift in Sources */, F34349242EDD3C2400AA7E70 /* NRNovelReadBottomView.swift in Sources */,
039810622ED04F250006E317 /* NRNetwork.swift in Sources */, 039810622ED04F250006E317 /* NRNetwork.swift in Sources */,
039810782ED054740006E317 /* NRHud.swift in Sources */, 039810782ED054740006E317 /* NRHud.swift in Sources */,
F34991032EE160F00039E939 /* NRUserAPI.swift in Sources */, F34991032EE160F00039E939 /* NRUserAPI.swift in Sources */,
85CF94312EEFDA88006467E3 /* NRLoginManager+Apple.swift in Sources */,
85606A9F2EEBE95A005D989D /* NRDashedLineView.swift in Sources */, 85606A9F2EEBE95A005D989D /* NRDashedLineView.swift in Sources */,
039810C42ED459440006E317 /* NRHomeNovelHotTagView.swift in Sources */, 039810C42ED459440006E317 /* NRHomeNovelHotTagView.swift in Sources */,
F34991122EE170E20039E939 /* NRWebView.swift in Sources */, F34991122EE170E20039E939 /* NRWebView.swift in Sources */,
@ -1546,6 +1573,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = ReaderHive/ReaderHive.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
@ -1555,6 +1583,9 @@
INFOPLIST_FILE = ReaderHive/Source/Info.plist; INFOPLIST_FILE = ReaderHive/Source/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = ReaderHive; INFOPLIST_KEY_CFBundleDisplayName = ReaderHive;
INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO;
INFOPLIST_KEY_LSApplicationCategoryType = "";
INFOPLIST_KEY_NSCameraUsageDescription = "The APP needs to access your album to provide screenshots for feedback.";
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "The APP needs to access your album to provide screenshots for feedback.";
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UIMainStoryboardFile = ""; INFOPLIST_KEY_UIMainStoryboardFile = "";
@ -1587,6 +1618,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = ReaderHive/ReaderHive.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
@ -1595,6 +1627,9 @@
INFOPLIST_FILE = ReaderHive/Source/Info.plist; INFOPLIST_FILE = ReaderHive/Source/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = ReaderHive; INFOPLIST_KEY_CFBundleDisplayName = ReaderHive;
INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO;
INFOPLIST_KEY_LSApplicationCategoryType = "";
INFOPLIST_KEY_NSCameraUsageDescription = "The APP needs to access your album to provide screenshots for feedback.";
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "The APP needs to access your album to provide screenshots for feedback.";
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UIMainStoryboardFile = ""; INFOPLIST_KEY_UIMainStoryboardFile = "";
@ -1764,6 +1799,25 @@
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
/* End XCConfigurationList section */ /* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
85CF94352EEFE27E006467E3 /* XCRemoteSwiftPackageReference "facebook-ios-sdk" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/facebook/facebook-ios-sdk";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 14.1.0;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
85CF94362EEFE27E006467E3 /* FacebookLogin */ = {
isa = XCSwiftPackageProductDependency;
package = 85CF94352EEFE27E006467E3 /* XCRemoteSwiftPackageReference "facebook-ios-sdk" */;
productName = FacebookLogin;
};
/* End XCSwiftPackageProductDependency section */
}; };
rootObject = 03980F5D2ED009E30006E317 /* Project object */; rootObject = 03980F5D2ED009E30006E317 /* Project object */;
} }

View File

@ -7,6 +7,7 @@
import UIKit import UIKit
import Alamofire import Alamofire
import SmartCodable
struct NRUserAPI { struct NRUserAPI {
@ -26,4 +27,35 @@ struct NRUserAPI {
} }
} }
static func requestSignThirdLogin(model: NRThirdSignModel, completer: ((_ token: NRLoginToken?) -> Void)?) {
var param = NRNetwork.Parameters(path: "/customer/login")
param.method = .post
param.parameters = model.toDictionary()
NRNetwork.request(parameters: param) { (response: NRNetwork.Response<NRLoginToken>) in
if response.isSuccess {
completer?(response.data)
} else {
completer?(nil)
}
}
}
static func requestLogout(completer: ((_ token: NRLoginToken?) -> Void)?) {
var param = NRNetwork.Parameters(path: "/customer/signout")
param.method = .post
param.isLoding = true
NRNetwork.request(parameters: param) { (response: NRNetwork.Response<NRLoginToken>) in
if response.isSuccess {
completer?(response.data)
} else {
completer?(nil)
}
}
}
} }

View File

@ -7,6 +7,7 @@
import UIKit import UIKit
internal import WebKit internal import WebKit
import ZLPhotoBrowser
///APP ///APP
let kNRWebMessageAPP = "js2app" let kNRWebMessageAPP = "js2app"
@ -23,6 +24,76 @@ let kNRWebMessageAccountDeletionFinish = "accountLogout"
extension NRWebViewController { extension NRWebViewController {
func nr_webViewUserContentController(didReceive message: WKScriptMessage) { func nr_webViewUserContentController(didReceive message: WKScriptMessage) {
let name = message.name
let body = message.body
switch name {
case kNRWebMessageOpenFeedbackList:
let vc = NRAppWebViewController()
vc.webUrl = kNRFeedBackListWebUrl
self.navigationController?.pushViewController(vc, animated: true)
case kNRWebMessageOpenFeedbackDetail:
guard let body = body as? [String : Any] else { return }
guard let id = body["id"] as? Int else { return }
let vc = NRAppWebViewController()
vc.id = "\(id)"
vc.webUrl = kNRFeedBackDetailWebUrl
self.navigationController?.pushViewController(vc, animated: true)
case kNRWebMessageOpenPhotoPicker:
openPhotoPicker()
// case kNRWebMessageAPP:
// guard let body = message.body as? [String : Any] else { return }
// guard let model = FAWebMessageModel.deserialize(from: body) else { return }
// let type = model.type
// let data = model.data
//
// if type == "login" {
//// let view = FALoginView()
//// view.present(in: nil)
//
// } else {
//
// guard let urlStr = data?.link else { return }
// guard let url = URL(string: urlStr) else { return }
// if UIApplication.shared.canOpenURL(url) {
// UIApplication.shared.open(url)
// }
// }
case kNRWebMessageAccountDeletionFinish:
self.navigationController?.popToRootViewController(animated: true)
NotificationCenter.default.post(name: NRLoginManager.loginStateDidChangeNotification, object: nil)
default:
break
}
}
///
private func openPhotoPicker() {
ZLPhotoConfiguration.default().allowSelectOriginal = false
ZLPhotoConfiguration.default().maxSelectCount = 1
ZLPhotoConfiguration.default().allowEditImage = false
ZLPhotoConfiguration.default().allowSelectVideo = false
ZLPhotoConfiguration.default().allowSelectGif = false
ZLPhotoConfiguration.default().allowTakePhotoInLibrary = false
let picker = ZLPhotoPicker()
picker.selectImageBlock = { [weak self] (results, _) in
guard let self = self else { return }
guard let image = results.first?.image else { return }
guard let imageData = image.jpegData(compressionQuality: 0.8) else { return }
let imageDataStr = imageData.base64EncodedString(options: .endLineWithCarriageReturn)
let js = "uploadConvertImage('\(imageDataStr)')"
self.webView.evaluateJavaScript(js)
}
picker.showPhotoLibrary(sender: self)
} }
} }

View File

@ -115,6 +115,7 @@ extension NRHomeNovelNewArrivalsCell {
categoryView.snp.makeConstraints { make in categoryView.snp.makeConstraints { make in
make.left.equalToSuperview() make.left.equalToSuperview()
make.right.lessThanOrEqualToSuperview()
make.top.equalTo(titleLabel.snp.bottom).offset(4) make.top.equalTo(titleLabel.snp.bottom).offset(4)
make.height.equalTo(20) make.height.equalTo(20)
} }

View File

@ -0,0 +1,110 @@
//
// NRLoginView.swift
// ReaderHive
//
// Created by on 2025/12/15.
//
import UIKit
import HWPanModal
import SnapKit
class NRLoginView: NRPanModalContentView {
private lazy var bgView = UIImageView(image: UIImage(named: "bg_image_01"))
private lazy var titleView = UIImageView(image: UIImage(named: "login_logo_icon"))
private lazy var appleLoginButton: UIButton = {
var configuration = UIButton.Configuration.plain()
configuration.background.backgroundColor = .F_2_EFEE
configuration.background.cornerRadius = 24
configuration.image = UIImage(named: "apple_login_logo_icon")
configuration.imagePadding = 8
configuration.attributedTitle = AttributedString("Login with Apple".localized, attributes: AttributeContainer([
.font : UIFont.font(ofSize: 14, weight: .medium),
.foregroundColor : UIColor.black
]))
let button = UIButton(configuration: configuration, primaryAction: UIAction(handler: { [weak self] _ in
guard let self = self else { return }
self.login(type: .apple)
}))
return button
}()
private lazy var facebookLoginButton: UIButton = {
var configuration = UIButton.Configuration.plain()
configuration.background.backgroundColor = .F_2_EFEE
configuration.background.cornerRadius = 24
configuration.image = UIImage(named: "facebook_login_logo_icon")
configuration.imagePadding = 8
configuration.attributedTitle = AttributedString("Login with Facebook".localized, attributes: AttributeContainer([
.font : UIFont.font(ofSize: 14, weight: .medium),
.foregroundColor : UIColor.black
]))
let button = UIButton(configuration: configuration, primaryAction: UIAction(handler: { [weak self] _ in
guard let self = self else { return }
self.login(type: .faceBook)
}))
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentHeight = 220 + UIScreen.safeBottom
nr_setupUI()
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func login(type: NRLoginManager.LoginType) {
NRHud.show()
NRLoginManager.manager.thirdLogin(type: type, presentingViewController: nil) { [weak self] isFinish in
NRHud.dismiss()
guard let self = self else { return }
guard isFinish else { return }
self.dismiss(animated: true) {
}
}
}
}
extension NRLoginView {
private func nr_setupUI() {
addSubview(bgView)
addSubview(titleView)
addSubview(appleLoginButton)
addSubview(facebookLoginButton)
bgView.snp.makeConstraints { make in
make.left.right.top.equalToSuperview()
}
titleView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(24)
}
appleLoginButton.snp.makeConstraints { make in
make.left.equalToSuperview().offset(44)
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(84)
make.height.equalTo(48)
}
facebookLoginButton.snp.makeConstraints { make in
make.left.right.height.equalTo(appleLoginButton)
make.top.equalTo(appleLoginButton.snp.bottom).offset(16)
}
}
}

View File

@ -8,6 +8,7 @@
import UIKit import UIKit
import SnapKit import SnapKit
import YYCategories import YYCategories
import HWPanModal
class NRMeHeaderView: UITableViewHeaderFooterView { class NRMeHeaderView: UITableViewHeaderFooterView {
@ -21,6 +22,7 @@ class NRMeHeaderView: UITableViewHeaderFooterView {
coinsView.userInfo = userInfo coinsView.userInfo = userInfo
loginButton.isHidden = !(userInfo?.is_tourist ?? false)
stackView.nr_removeAllArrangedSubview() stackView.nr_removeAllArrangedSubview()
stackView.addArrangedSubview(coinsView) stackView.addArrangedSubview(coinsView)
@ -72,6 +74,16 @@ class NRMeHeaderView: UITableViewHeaderFooterView {
return button return button
}() }()
private lazy var loginButton: UIButton = {
let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in
guard let self = self else { return }
let view = NRLoginView()
view.present(in: nil)
}))
button.setImage(UIImage(named: "login_button"), for: .normal)
return button
}()
private lazy var stackView: UIStackView = { private lazy var stackView: UIStackView = {
let view = UIStackView() let view = UIStackView()
view.axis = .vertical view.axis = .vertical
@ -121,6 +133,7 @@ extension NRMeHeaderView {
contentView.addSubview(idBgView) contentView.addSubview(idBgView)
idBgView.addSubview(idLabel) idBgView.addSubview(idLabel)
idBgView.addSubview(copyButton) idBgView.addSubview(copyButton)
contentView.addSubview(loginButton)
contentView.addSubview(stackView) contentView.addSubview(stackView)
avatarImageView.snp.makeConstraints { make in avatarImageView.snp.makeConstraints { make in
@ -152,6 +165,11 @@ extension NRMeHeaderView {
make.left.equalTo(idLabel.snp.right).offset(8) make.left.equalTo(idLabel.snp.right).offset(8)
} }
loginButton.snp.makeConstraints { make in
make.centerY.equalTo(avatarImageView)
make.right.equalToSuperview().offset(-16)
}
stackView.snp.makeConstraints { make in stackView.snp.makeConstraints { make in
make.left.right.equalToSuperview() make.left.right.equalToSuperview()
make.top.equalTo(avatarImageView.snp.bottom).offset(16) make.top.equalTo(avatarImageView.snp.bottom).offset(16)

View File

@ -13,18 +13,14 @@ class NRFeedbackViewController: NRAppWebViewController {
self.webUrl = kNRFeedBackHomeWebUrl self.webUrl = kNRFeedBackHomeWebUrl
super.viewDidLoad() super.viewDidLoad()
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(named: "feedback_list_icon_01"), style: .plain, target: self, action: #selector(openFeedbackList))
} }
@objc private func openFeedbackList() {
/* let vc = NRAppWebViewController()
// MARK: - Navigation vc.webUrl = kNRFeedBackListWebUrl
self.navigationController?.pushViewController(vc, animated: true)
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
} }
*/
} }

View File

@ -8,6 +8,7 @@ import UIKit
import IQKeyboardManagerSwift import IQKeyboardManagerSwift
import IQKeyboardToolbarManager import IQKeyboardToolbarManager
import MJRefresh import MJRefresh
import FacebookCore
extension AppDelegate { extension AppDelegate {
@ -30,4 +31,9 @@ extension AppDelegate {
UIDevice.current.isBatteryMonitoringEnabled = true UIDevice.current.isBatteryMonitoringEnabled = true
} }
func nr_registThirdparty(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) {
ApplicationDelegate.shared.application(application, didFinishLaunchingWithOptions: launchOptions)
}
} }

View File

@ -0,0 +1,33 @@
//
// AppDelegate+Open.swift
// ReaderHive
//
// Created by on 2025/12/15.
//
import UIKit
import FacebookCore
extension SceneDelegate {
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let url = URLContexts.first?.url else {
return
}
var result = false
result = ApplicationDelegate.shared.application(UIApplication.shared, open: url, sourceApplication: nil, annotation: [UIApplication.OpenURLOptionsKey.annotation])
}
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
guard let webpageURL = userActivity.webpageURL else { return }
var result = false
result = ApplicationDelegate.shared.application(UIApplication.shared, continue: userActivity)
}
}

View File

@ -6,15 +6,16 @@
// //
import UIKit import UIKit
import FacebookCore
@main @main
class AppDelegate: UIResponder, UIApplicationDelegate { class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
NRTool.appDelegate = self NRTool.appDelegate = self
NRNetworkReachableManager.manager.startMonitoring() NRNetworkReachableManager.manager.startMonitoring()
nr_registThirdparty(application, didFinishLaunchingWithOptions: launchOptions)
NotificationCenter.default.addObserver(self, selector: #selector(networkStatusDidChangeNotification), name: NRNetworkReachableManager.networkStatusDidChangeNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(networkStatusDidChangeNotification), name: NRNetworkReachableManager.networkStatusDidChangeNotification, object: nil)

View File

@ -0,0 +1,107 @@
//
// NRLoginManager+Apple.swift
// ReaderHive
//
// Created by on 2025/12/15.
//
import UIKit
import AuthenticationServices
extension NRLoginManager {
private struct AssociatedKeys {
static var appleLoginHandle: Int?
}
private var appleLoginHandle: ((_ model: NRThirdSignModel?) -> Void)? {
set {
objc_setAssociatedObject(self, &AssociatedKeys.appleLoginHandle, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
}
get {
return objc_getAssociatedObject(self, &AssociatedKeys.appleLoginHandle) as? ((_ model: NRThirdSignModel?) -> Void)
}
}
func nr_appleLogin(completer: ((_ model: NRThirdSignModel?) -> Void)?) {
self.appleLoginHandle = completer
let appleIDProvider = ASAuthorizationAppleIDProvider()
let request = appleIDProvider.createRequest()
request.requestedScopes = [.fullName, .email]
let authorizationController = ASAuthorizationController(authorizationRequests: [request])
authorizationController.delegate = self
authorizationController.presentationContextProvider = self
authorizationController.performRequests()
}
private func jwtDecode(jwtStr: String) -> [String: Any]? {
let segments = jwtStr.components(separatedBy: ".")
guard segments.count > 1 else { return nil }
var base64String = segments[1]
// Base64
let requiredLength = 4 * Int(ceil(Double(base64String.count) / 4.0))
let paddingLength = requiredLength - base64String.count
if paddingLength > 0 {
base64String += String(repeating: "=", count: paddingLength)
}
// URL
base64String = base64String.replacingOccurrences(of: "-", with: "+")
base64String = base64String.replacingOccurrences(of: "_", with: "/")
// Base64
guard let data = Data(base64Encoded: base64String),
let jsonObject = try? JSONSerialization.jsonObject(with: data, options: []),
let payload = jsonObject as? [String: Any] else {
return nil
}
return payload
}
}
//MARK: ASAuthorizationControllerDelegate
extension NRLoginManager: ASAuthorizationControllerDelegate {
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
let userIdentifier = appleIDCredential.user
let fullName = appleIDCredential.fullName
let identityToken = appleIDCredential.identityToken.flatMap { String(data: $0, encoding: .utf8) }
let identityTokenParams = self.jwtDecode(jwtStr: identityToken ?? "")
var model = NRThirdSignModel()
model.platform = .apple
model.third_id = userIdentifier
model.giving_name = fullName?.givenName
model.family_name = fullName?.familyName
model.avator = identityTokenParams?["picture"] as? String
model.email = identityTokenParams?["email"] as? String
appleLoginHandle?(model)
appleLoginHandle = nil
}
}
func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
appleLoginHandle?(nil)
appleLoginHandle = nil
}
}
//MARK: ASAuthorizationControllerPresentationContextProviding
extension NRLoginManager: ASAuthorizationControllerPresentationContextProviding {
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
return NRTool.keyWindow!
}
}

View File

@ -0,0 +1,59 @@
//
// NRLoginManager+Facebook.swift
// ReaderHive
//
// Created by on 2025/12/15.
//
import UIKit
import FacebookLogin
extension NRLoginManager {
func nr_facebookLogin(presentingViewController: UIViewController?, completer: ((_ model: NRThirdSignModel?) -> Void)?) {
let loginManager = LoginManager()
loginManager.logOut()
loginManager.defaultAudience = .everyone
loginManager.logIn(permissions: ["public_profile", "email"], from: presentingViewController) { result, error in
guard error == nil, let result = result else {
completer?(nil)
return
}
if result.isCancelled {
completer?(nil)
return
}
let request = GraphRequest(graphPath: "me", parameters: ["fields" : "id,name,email,picture"], httpMethod: .get)
request.start { connection, result, error in
guard let result = result as? [String : Any] else {
completer?(nil)
return
}
var model = NRThirdSignModel()
model.platform = .faceBook
model.third_id = result["id"] as? String
model.email = result["email"] as? String
if let picture = result["picture"] as? [String : Any],
let data = picture["data"] as? [String : Any],
let url = data["url"] as? String
{
model.avator = url
}
if let name = result["name"] as? String {
model.family_name = name
} else {
model.family_name = result["first_name"] as? String
model.giving_name = result["last_name"] as? String
}
completer?(model)
}
}
}
}

View File

@ -6,9 +6,17 @@
// //
import UIKit import UIKit
import SmartCodable
class NRLoginManager: NSObject { class NRLoginManager: NSObject {
enum LoginType: String, SmartCaseDefaultable {
case apple = "Apple"
case faceBook = "Facebook"
case google = "Google"
case tiktok = "Tiktok"
}
static let manager = NRLoginManager() static let manager = NRLoginManager()
private(set) var token = UserDefaults.nr_object(forKey: kNRLoginTokenDefaultsKey, as: NRLoginToken.self) private(set) var token = UserDefaults.nr_object(forKey: kNRLoginTokenDefaultsKey, as: NRLoginToken.self)
@ -30,6 +38,72 @@ class NRLoginManager: NSObject {
} }
} }
///
func thirdLogin(type: LoginType, presentingViewController: UIViewController?, completer: ((_ isFinish: Bool) -> Void)?) {
switch type {
case .apple:
nr_appleLogin { [weak self] model in
self?.requestSignThirdLogin(thirdSignModel: model, completer: completer)
}
case .faceBook:
nr_facebookLogin(presentingViewController: presentingViewController) { [weak self] model in
self?.requestSignThirdLogin(thirdSignModel: model, completer: completer)
}
default:
break
}
}
///
private func requestSignThirdLogin(thirdSignModel: NRThirdSignModel?, completer: ((_ isFinish: Bool) -> Void)?) {
guard let thirdSignModel = thirdSignModel else {
completer?(false)
return
}
// FAStatAPI.requestLeaveApp()
NRUserAPI.requestSignThirdLogin(model: thirdSignModel) { [weak self] token in
guard let self = self else { return }
guard let token = token else {
completer?(false)
return
}
self.setAccountToken(token)
self.userInfo?.is_tourist = false
Task {
await self.updateUserInfo()
}
// FAStatAPI.requestEnterApp()
// FAStatAPI.requestStatOnLine()
completer?(true)
NotificationCenter.default.post(name: NRLoginManager.userInfoUpdateNotification, object: nil)
NotificationCenter.default.post(name: NRLoginManager.loginStateDidChangeNotification, object: nil)
}
}
func logout(completer: ((_ isFinish: Bool) -> Void)?) {
// FAStatAPI.requestLeaveApp()
NRUserAPI.requestLogout { [weak self] token in
guard let self = self else { return }
if let token = token {
self.setAccountToken(token)
self.userInfo?.is_tourist = true
Task {
await self.updateUserInfo()
}
// FAStatAPI.requestEnterApp()
// FAStatAPI.requestStatOnLine()
completer?(true)
NotificationCenter.default.post(name: NRLoginManager.userInfoUpdateNotification, object: nil)
NotificationCenter.default.post(name: NRLoginManager.loginStateDidChangeNotification, object: nil)
} else {
completer?(false)
}
}
}
} }

View File

@ -0,0 +1,24 @@
//
// NRThirdSignModel.swift
// ReaderHive
//
// Created by on 2025/12/15.
//
import UIKit
import SmartCodable
struct NRThirdSignModel: SmartCodable {
var third_id: String?
var email: String?
//
var family_name: String?
//
var giving_name: String?
var avator: String?
var platform: NRLoginManager.LoginType?
}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.applesignin</key>
<array>
<string>Default</string>
</array>
<key>keychain-access-groups</key>
<array/>
</dict>
</plist>

Binary file not shown.

After

Width:  |  Height:  |  Size: 479 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 B

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 804 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,20 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -2,6 +2,31 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>fb1155849519865831</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>readerhiveapp</string>
</array>
</dict>
</array>
<key>FacebookAppID</key>
<string>1155849519865831</string>
<key>FacebookClientToken</key>
<string>696d03750f4bc7a888c815b071be555d</string>
<key>FacebookDisplayName</key>
<string>$(PRODUCT_NAME)</string>
<key>UIApplicationSceneManifest</key> <key>UIApplicationSceneManifest</key>
<dict> <dict>
<key>UIApplicationSupportsMultipleScenes</key> <key>UIApplicationSupportsMultipleScenes</key>

View File

@ -129,6 +129,8 @@
"Balance" = "Balance"; "Balance" = "Balance";
"Daily Coins" = "Daily Coins"; "Daily Coins" = "Daily Coins";
"Buy Now" = "Buy Now"; "Buy Now" = "Buy Now";
"Login with Apple" = "Login with Apple";
"Login with Facebook" = "Login with Facebook";
"retain_alert_text" = "Unlock every show you love!"; "retain_alert_text" = "Unlock every show you love!";