Refactor home to single collection view and add all-functions customization.

Replace scroll/stack layout with one UICollectionView, fix grid column math and Diffable item IDs so location report and quick actions render, and add the customizable all-functions page with persistence and tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-07 09:57:41 +08:00
parent d8329d19fd
commit 715393e78c
14 changed files with 1222 additions and 209 deletions

View File

@ -0,0 +1,82 @@
//
// AllFunctionsViewModel.swift
// suixinkan
//
import Foundation
/// ViewModel
final class AllFunctionsViewModel {
private(set) var commonMenus: [HomeMenuItem] = []
private(set) var moreMenus: [HomeMenuItem] = []
var onStateChange: (() -> Void)?
private(set) var didCustomize = false
private let appStore: AppStore
private let commonMenuStore: HomeCommonMenuStore
init(
appStore: AppStore = .shared,
commonMenuStore: HomeCommonMenuStore = HomeCommonMenuStore()
) {
self.appStore = appStore
self.commonMenuStore = commonMenuStore
}
///
func loadFunctions() {
let permissions = appStore.permissionItems()
let allMenus = HomeMenuCatalog.visibleMenus(from: permissions)
let accountScope = appStore.accountCachePrefix
let roleCode = appStore.roleCode
let savedURIs = commonMenuStore.savedCommonURIs(accountScope: accountScope, roleCode: roleCode)
let defaultURIs = HomeCommonMenuStore.defaultCommonURIs(from: permissions)
let commonURIs = savedURIs.isEmpty ? defaultURIs : savedURIs
let split = HomeCommonMenuStore.splitMenus(from: allMenus, commonURIs: commonURIs)
commonMenus = split.common
moreMenus = split.more
if savedURIs.isEmpty, !split.common.isEmpty {
commonMenuStore.saveCommonURIs(split.common.map(\.uri), accountScope: accountScope, roleCode: roleCode)
}
notifyStateChange()
}
///
func addToCommon(_ menu: HomeMenuItem) {
guard !commonMenus.contains(where: { $0.uri == menu.uri }) else { return }
commonMenus.append(menu)
moreMenus.removeAll { $0.uri == menu.uri }
persistCommonMenus()
didCustomize = true
notifyStateChange()
}
///
func removeFromCommon(_ menu: HomeMenuItem) {
guard commonMenus.contains(where: { $0.uri == menu.uri }) else { return }
commonMenus.removeAll { $0.uri == menu.uri }
if !moreMenus.contains(where: { $0.uri == menu.uri }) {
moreMenus.append(menu)
}
persistCommonMenus()
didCustomize = true
notifyStateChange()
}
private func persistCommonMenus() {
commonMenuStore.saveCommonURIs(
commonMenus.map(\.uri),
accountScope: appStore.accountCachePrefix,
roleCode: appStore.roleCode
)
}
private func notifyStateChange() {
onStateChange?()
}
}

View File

@ -223,11 +223,20 @@ final class HomeViewModel {
func rebuildCommonMenus() {
let permissions = appStore.permissionItems()
let allMenus = HomeMenuCatalog.visibleMenus(from: permissions)
let accountScope = appStore.accountCachePrefix
let roleCode = appStore.roleCode
let savedURIs = commonMenuStore.savedCommonURIs(accountScope: accountScope, roleCode: roleCode)
if savedURIs.isEmpty, !permissions.isEmpty {
let defaultURIs = HomeCommonMenuStore.defaultCommonURIs(from: permissions)
commonMenuStore.saveCommonURIs(defaultURIs, accountScope: accountScope, roleCode: roleCode)
}
commonMenus = commonMenuStore.commonMenus(
from: allMenus,
permissions: permissions,
accountScope: appStore.accountCachePrefix,
roleCode: appStore.roleCode
accountScope: accountScope,
roleCode: roleCode
)
}