diff --git a/dimensionhub/Core/API.swift b/dimensionhub/Core/API.swift index 471cfaa..78a266f 100644 --- a/dimensionhub/Core/API.swift +++ b/dimensionhub/Core/API.swift @@ -120,6 +120,31 @@ struct API { return await doRequest(request: request, as: T.self) } + // 消息管理 + static func getUserUnreadNum(userId: String, as: T.Type) async -> APIResponse { + let request = URLRequest(url: URL(string: baseUrl + "/api/get_unread_num?user_id=\(userId)")!) + + return await doRequest(request: request, as: T.self) + } + + static func updateUserUnreadNum(userId: String, dramaId: Int, as: T.Type) async -> APIResponse { + let request = URLRequest(url: URL(string: baseUrl + "/api/update_unread_num?user_id=\(userId)&drama_id=\(dramaId)")!) + + return await doRequest(request: request, as: T.self) + } + + static func resetUserUnreadNum(userId: String, dramaIds: [Int], as: T.Type) async -> APIResponse { + let jsonData = try! JSONSerialization.data(withJSONObject: ["drama_ids": dramaIds], options: []) + + let url = URL(string: baseUrl + "/api/reset_unread_num?user_id=\(userId)")! + var request = URLRequest(url: url) + request.httpMethod = "POST" + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + request.httpBody = jsonData + + return await doRequest(request: request, as: T.self) + } + // 执行http请求 private static func doRequest(request: URLRequest, as: T.Type) async -> APIResponse { do { diff --git a/dimensionhub/Views/Index/IndexMainView.swift b/dimensionhub/Views/Index/IndexMainView.swift index 54ca239..8c33739 100644 --- a/dimensionhub/Views/Index/IndexMainView.swift +++ b/dimensionhub/Views/Index/IndexMainView.swift @@ -150,6 +150,8 @@ struct IndexMainView: View { } .task { await self.indexModel.loadData(userId: self.userId) + + } } diff --git a/dimensionhub/dimensionhubApp.swift b/dimensionhub/dimensionhubApp.swift index a99052d..ea06db0 100644 --- a/dimensionhub/dimensionhubApp.swift +++ b/dimensionhub/dimensionhubApp.swift @@ -14,6 +14,8 @@ struct dimensionhubApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate @StateObject var appNav = AppNavigation.shared + private var userId: String + var sharedModelContainer: ModelContainer = { let schema = Schema([ Item.self, @@ -33,11 +35,11 @@ struct dimensionhubApp: App { }() init() { + self.userId = KeychainHelper.getPersistentUserId() + // 尽可能早设置代理对象 UNUserNotificationCenter.current().delegate = appDelegate - - let userId = KeychainHelper.getPersistentUserId() - print("user_id is: \(userId)") + print("user_id is: \(self.userId)") } var body: some Scene { @@ -60,6 +62,11 @@ struct dimensionhubApp: App { withAnimation { appNav.append(dest: .detail(id: dramaId)) } + + Task { + await self.updateUserUnreadNum(userId: self.userId, dramaId: dramaId) + } + appNav.targetDetailId = 0 } } @@ -67,13 +74,25 @@ struct dimensionhubApp: App { } .navigationViewStyle(.stack) .tint(.black) - .environment(\.userId, KeychainHelper.getPersistentUserId()) + .environment(\.userId, self.userId) .environmentObject(appNav) .preferredColorScheme(.light) } .modelContainer(sharedModelContainer) } + private func updateUserUnreadNum(userId: String, dramaId: Int) async { + let response = await API.updateUserUnreadNum(userId: userId, dramaId: dramaId, as: Int.self) + switch response { + case .result(let newUnreadNum): + NSLog("updateUserUnreadNum success, new unread_num:\(newUnreadNum)") + Task {@MainActor in + try? await UNUserNotificationCenter.current().setBadgeCount(newUnreadNum) + } + case .error(let errorCode, let error): + NSLog("updateUserUnreadNum error: \(error), error_code: \(errorCode)") + } + } } class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate { @@ -81,6 +100,26 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { Task.detached { await self.registerForPushNotifications() + + // 更新未读消息数 + let unreadDramaIds = await self.getSystemUnreadDramaIds() + let userId = KeychainHelper.getPersistentUserId() + let response = await API.resetUserUnreadNum(userId: userId, dramaIds: unreadDramaIds, as: String.self) + switch response { + case .result(let result): + NSLog("reset user unread num result is: \(result)") + case .error(let errorCode, let message): + NSLog("reset user unread error_code: \(errorCode), message: \(message)") + } + + Task {@MainActor in + do { + try await UNUserNotificationCenter.current().setBadgeCount(unreadDramaIds.count) + NSLog("UNUserNotificationCenter setBadgeCount num: \(unreadDramaIds.count)") + } catch let err { + NSLog("UNUserNotificationCenter setBadgeCount get error: \(err)") + } + } } return true } @@ -108,6 +147,34 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } } } + + // 从服务器获取初始未读数 + private func fetchUserUnreadCount() async -> Int? { + let userId = KeychainHelper.getPersistentUserId() + let response = await API.getUserUnreadNum(userId: userId, as: Int.self) + switch response { + case .result(let unreadNum): + return unreadNum + case .error(let errorCode, let error): + NSLog("fetchUserUnreadCount get error: \(error), code: \(errorCode)") + return nil + } + } + + // 通系统通知列表获取未读取的消息总数 + private func getSystemUnreadDramaIds() async -> [Int] { + let notifications = await UNUserNotificationCenter.current().deliveredNotifications() + let unreadDramaIds = notifications.flatMap { n -> [Int] in + let userInfo = n.request.content.userInfo + guard let customData = userInfo["custom_data"] as? [String: AnyObject], + let params = customData["params"] as? [String : AnyObject], + let dramaId = params["drama_id"] as? Int else { + return [] + } + return [dramaId] + } + return unreadDramaIds + } // 成功注册远程通知,获取到 deviceToken func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { @@ -136,7 +203,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD // 收到远程通知(App 在前台) func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { - completionHandler(.newData) }