feat: 接入消息未读数、全部已读与首页红点角标同步。
固定消息中心为首页入口,并在推送到达、已读后刷新桌面角标。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -17,18 +17,23 @@ final class HomeCommonMenuStore {
|
||||
/// 读取已保存的常用 URI;无记录时返回空数组。
|
||||
func savedCommonURIs(accountScope: String, roleCode: String) -> [String] {
|
||||
guard let key = storageKey(accountScope: accountScope, roleCode: roleCode) else { return [] }
|
||||
return defaults.stringArray(forKey: key) ?? []
|
||||
let saved = defaults.stringArray(forKey: key) ?? []
|
||||
let sanitized = saved.filter(HomeMenuCatalog.isCustomizable(uri:))
|
||||
if sanitized != saved {
|
||||
defaults.set(sanitized, forKey: key)
|
||||
}
|
||||
return sanitized
|
||||
}
|
||||
|
||||
/// 保存常用 URI 列表。
|
||||
func saveCommonURIs(_ uris: [String], accountScope: String, roleCode: String) {
|
||||
guard let key = storageKey(accountScope: accountScope, roleCode: roleCode) else { return }
|
||||
defaults.set(uris, forKey: key)
|
||||
defaults.set(uris.filter(HomeMenuCatalog.isCustomizable(uri:)), forKey: key)
|
||||
}
|
||||
|
||||
/// 根据权限生成默认常用 URI(最多 4 个)。
|
||||
static func defaultCommonURIs(from permissions: [HomePermissionItem]) -> [String] {
|
||||
let uris = permissions.map(\.uri)
|
||||
let uris = permissions.map(\.uri).filter(HomeMenuCatalog.isCustomizable(uri:))
|
||||
return uris.count > 4 ? Array(uris.prefix(4)) : uris
|
||||
}
|
||||
|
||||
@ -51,8 +56,9 @@ final class HomeCommonMenuStore {
|
||||
commonURIs: [String]
|
||||
) -> (common: [HomeMenuItem], more: [HomeMenuItem]) {
|
||||
let commonSet = Set(commonURIs)
|
||||
let common = allMenus.filter { commonSet.contains($0.uri) }
|
||||
let more = allMenus.filter { !commonSet.contains($0.uri) }
|
||||
let customizableMenus = allMenus.filter { HomeMenuCatalog.isCustomizable(uri: $0.uri) }
|
||||
let common = customizableMenus.filter { commonSet.contains($0.uri) }
|
||||
let more = customizableMenus.filter { !commonSet.contains($0.uri) }
|
||||
return (common, more)
|
||||
}
|
||||
|
||||
|
||||
@ -8,6 +8,8 @@ import Foundation
|
||||
/// 首页菜单静态 catalog,与服务端 permission URI 取交集后展示。
|
||||
enum HomeMenuCatalog {
|
||||
|
||||
private static let fixedHomeEntryURIs: Set<String> = ["message_center"]
|
||||
|
||||
/// 全部已知菜单项,对齐 Android `Constants.menuList`。
|
||||
static let allItems: [HomeMenuItem] = [
|
||||
HomeMenuItem(uri: "space_settings", title: "空间设置", iconName: "home_menu_space"),
|
||||
@ -45,6 +47,7 @@ enum HomeMenuCatalog {
|
||||
/// 将服务端权限映射为可展示菜单,保持服务端顺序。
|
||||
static func visibleMenus(from permissions: [HomePermissionItem]) -> [HomeMenuItem] {
|
||||
permissions.compactMap { permission in
|
||||
guard isCustomizable(uri: permission.uri) else { return nil }
|
||||
guard let catalog = allItems.first(where: { $0.uri == permission.uri }) else { return nil }
|
||||
return HomeMenuItem(
|
||||
uri: catalog.uri,
|
||||
@ -59,4 +62,9 @@ enum HomeMenuCatalog {
|
||||
static func item(for uri: String) -> HomeMenuItem? {
|
||||
allItems.first { $0.uri == uri }
|
||||
}
|
||||
|
||||
/// 判断入口是否允许出现在首页常用应用及其自定义页面中。
|
||||
static func isCustomizable(uri: String) -> Bool {
|
||||
!fixedHomeEntryURIs.contains(uri)
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,6 +48,7 @@ final class AllFunctionsViewModel {
|
||||
|
||||
/// 将菜单加入常用应用。
|
||||
func addToCommon(_ menu: HomeMenuItem) {
|
||||
guard HomeMenuCatalog.isCustomizable(uri: menu.uri) else { return }
|
||||
guard !commonMenus.contains(where: { $0.uri == menu.uri }) else { return }
|
||||
commonMenus.append(menu)
|
||||
moreMenus.removeAll { $0.uri == menu.uri }
|
||||
|
||||
@ -25,6 +25,7 @@ final class HomeViewModel {
|
||||
private(set) var reportTimeText = ""
|
||||
private(set) var isLocationReporting = false
|
||||
private(set) var needsPermissionReload = false
|
||||
private(set) var unreadMessageCount = 0
|
||||
|
||||
var onStateChange: (() -> Void)?
|
||||
var onShowMessage: ((String) -> Void)?
|
||||
@ -65,6 +66,7 @@ final class HomeViewModel {
|
||||
var isOnline: Bool { locationStateStore.isOnline }
|
||||
var countdownDisplayText: String { locationStateStore.countdownDisplayText }
|
||||
var reminderMinutes: Int { locationStateStore.reminderMinutes }
|
||||
var hasUnreadMessages: Bool { unreadMessageCount > 0 }
|
||||
|
||||
/// 首次进入首页时加载权限并恢复在线状态。
|
||||
func initialize(api: HomeAPI) async {
|
||||
@ -76,6 +78,28 @@ final class HomeViewModel {
|
||||
/// 账号切换后标记需要重新拉取权限。
|
||||
func markNeedsPermissionReload() {
|
||||
needsPermissionReload = true
|
||||
unreadMessageCount = 0
|
||||
}
|
||||
|
||||
/// 查询未读消息总数,成功时更新首页状态并返回 `true`。
|
||||
func refreshUnreadMessageStatus(api: any MessageCenterServing) async -> Bool {
|
||||
do {
|
||||
let response = try await api.unreadCount()
|
||||
unreadMessageCount = response.unreadCount
|
||||
notifyStateChange()
|
||||
return true
|
||||
} catch is CancellationError {
|
||||
return false
|
||||
} catch {
|
||||
// 查询失败时保留上一次状态,避免网络抖动导致红点错误消失。
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/// 使用已知的最新未读消息数量立即刷新首页状态。
|
||||
func updateUnreadMessageCount(_ count: Int) {
|
||||
unreadMessageCount = max(count, 0)
|
||||
notifyStateChange()
|
||||
}
|
||||
|
||||
/// 查询待审核记录并决定进入申请页或审核状态页。
|
||||
|
||||
Reference in New Issue
Block a user