// MyPass/ViewModels/VaultViewModel.swift import Foundation import Combine import MyPassCore @MainActor final class VaultViewModel: ObservableObject { @Published var searchQuery: String = "" @Published var selectedGroupIds: Set = [] @Published var lastToggledGroupId: UUID? @Published var errorMessage: String? let session: VaultSession private var sessionCancellable: AnyCancellable? init(session: VaultSession) { self.session = session sessionCancellable = session.objectWillChange.sink { [weak self] in self?.objectWillChange.send() } } /// All entries in the vault, flattened with their group breadcrumb, before any filtering. private var allFlatEntries: [FlatEntry] { guard let root = session.database?.root else { return [] } return flattenEntries(in: root) } /// Entries after applying the group filter, then the search filter (AND semantics). var displayedEntries: [FlatEntry] { var entries = allFlatEntries if !selectedGroupIds.isEmpty { entries = entries.filter { selectedGroupIds.contains($0.parentGroupId) } } if !searchQuery.isEmpty { let q = searchQuery.lowercased() entries = entries.filter { $0.entry.title.lowercased().contains(q) || $0.entry.username.lowercased().contains(q) || $0.entry.url.lowercased().contains(q) } } return entries } func deleteEntry(_ flatEntry: FlatEntry) { Task { do { try session.deleteEntry(id: flatEntry.entry.id, fromGroupId: flatEntry.parentGroupId) } catch { errorMessage = error.localizedDescription } } } }