Files
suixinkan_ios_new/suixinkan/Features/Home/Services/HomeCommonMenuStore.swift

182 lines
7.2 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// HomeCommonMenuStore.swift
// suixinkan
//
// Created by Codex on 2026/6/22.
//
import Foundation
/// URI
struct HomeCommonMenuStore {
static let storageKeyPrefix = "home.common.menu.uris.role."
static let accountScopedStorageKeyPrefix = "home.common.menu.uris.account."
/// Android `Constants.menuList` URI
static let androidHomeMenuURIs: Set<String> = [
"space_settings",
"wallet",
"cloud_management",
"asset_management",
"task_management",
"schedule_management",
"system_settings",
"message_center",
"checkin_points",
"sample_management",
"live_stream_management",
"verification_order",
"live_album",
"pm",
"location_report",
"registration_invitation",
"store",
"fly",
"pilot_cert",
"task_management_editor",
"pm_manager",
"pilot_controller",
"/scenic-queue",
"operating-area",
"/scenic-order-manage",
"cooperation_order"
]
private let defaults: UserDefaults
/// UserDefaults
init(defaults: UserDefaults = .standard) {
self.defaults = defaults
}
/// Android URI 4 4
static func defaultCommonURIs(
orderedTopLevelURIs: [String],
availableURIs: Set<String>
) -> [String] {
let resolved = orderedTopLevelURIs.compactMap { uri -> String? in
let trimmed = uri.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else { return nil }
let canonical = HomeMenuRouter.canonicalURI(for: trimmed, availableURIs: availableURIs)
return Self.isAndroidHomeMenuURI(canonical) && availableURIs.contains(canonical) ? canonical : nil
}
let deduplicated = unique(resolved)
if deduplicated.count > 4 {
return Array(deduplicated.prefix(4))
}
return deduplicated
}
/// URI Android
func load(
menuItems: [HomeMenuItem],
roleCode: String?,
accountScope: String? = nil,
legacyRoleId: Int? = nil,
topLevelPermissionURIs: [String]
) -> [String] {
guard let roleCode, !roleCode.isEmpty else { return [] }
migrateLegacyStorageIfNeeded(roleCode: roleCode, accountScope: accountScope, legacyRoleId: legacyRoleId)
let availableURIs = Set(menuItems.map(\.uri))
let key = Self.storageKey(roleCode: roleCode, accountScope: accountScope)
let saved = defaults.stringArray(forKey: key)
if let saved, !saved.isEmpty {
let filtered = Self.unique(saved.compactMap { uri -> String? in
let resolved = HomeMenuRouter.canonicalURI(for: uri, availableURIs: availableURIs)
return Self.isAndroidHomeMenuURI(resolved) && availableURIs.contains(resolved) ? resolved : nil
})
if !filtered.isEmpty {
save(filtered, roleCode: roleCode, accountScope: accountScope)
return filtered
}
}
let defaultURIs = Self.defaultCommonURIs(
orderedTopLevelURIs: topLevelPermissionURIs,
availableURIs: availableURIs
)
save(defaultURIs, roleCode: roleCode, accountScope: accountScope)
return defaultURIs
}
/// URI
func add(_ uri: String, current: [String], menuItems: [HomeMenuItem], roleCode: String?, accountScope: String? = nil) -> [String] {
guard let roleCode, !roleCode.isEmpty else { return current }
let availableURIs = Set(menuItems.map(\.uri))
let resolved = HomeMenuRouter.canonicalURI(for: uri, availableURIs: availableURIs)
guard Self.isAndroidHomeMenuURI(resolved), availableURIs.contains(resolved) else { return current }
guard !current.contains(where: { HomeMenuRouter.menuAliasKey(for: $0) == HomeMenuRouter.menuAliasKey(for: resolved) }) else {
return current
}
let next = current + [resolved]
save(next, roleCode: roleCode, accountScope: accountScope)
return next
}
/// URI URI
func remove(_ uri: String, current: [String], roleCode: String?, accountScope: String? = nil) -> [String] {
guard let roleCode, !roleCode.isEmpty else { return current }
let aliasKey = HomeMenuRouter.menuAliasKey(for: uri)
let next = current.filter { HomeMenuRouter.menuAliasKey(for: $0) != aliasKey }
save(next, roleCode: roleCode, accountScope: accountScope)
return next
}
/// URI
func save(_ uris: [String], roleCode: String, accountScope: String? = nil) {
defaults.set(Self.unique(uris), forKey: Self.storageKey(roleCode: roleCode, accountScope: accountScope))
}
/// key roleCode legacy role id key
func migrateLegacyStorageIfNeeded(roleCode: String, accountScope: String? = nil, legacyRoleId: Int?) {
let newKey = Self.storageKey(roleCode: roleCode, accountScope: accountScope)
guard defaults.stringArray(forKey: newKey) == nil else { return }
let legacyKeys = [
legacyStorageKey(for: roleCode),
legacyRoleId.map { legacyStorageKey(for: $0) }
].compactMap { $0 }
for legacyKey in legacyKeys {
guard let legacyData = defaults.stringArray(forKey: legacyKey), !legacyData.isEmpty else { continue }
defaults.set(legacyData, forKey: newKey)
defaults.removeObject(forKey: legacyKey)
return
}
}
/// role_code UserDefaults key
static func storageKey(roleCode: String, accountScope: String?) -> String {
let normalizedScope = accountScope?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
if normalizedScope.isEmpty {
return storageKeyPrefix + roleCode
}
return accountScopedStorageKeyPrefix + normalizedScope + ".role." + roleCode
}
/// numeric role id key
private func legacyStorageKey(for roleId: Int) -> String {
Self.storageKeyPrefix + String(roleId)
}
/// role_code key
private func legacyStorageKey(for roleCode: String) -> String {
Self.storageKeyPrefix + roleCode
}
/// URI Android
private static func isAndroidHomeMenuURI(_ uri: String) -> Bool {
androidHomeMenuURIs.contains(uri)
}
/// URI URI
private static func unique(_ uris: [String]) -> [String] {
var seen = Set<String>()
return uris.filter { uri in
seen.insert(HomeMenuRouter.menuAliasKey(for: uri)).inserted
}
}
}