import Foundation import Observation @MainActor @Observable final class OLTListViewModel { var phase: LoadPhase = .idle var olts: [OLTStateDoc] = [] var search = "" private let connection: MCMSConnection /// Bumped per load so an overlapping/stale fetch can't commit over a newer one. private var loadGeneration = 0 init(connection: MCMSConnection) { self.connection = connection } var filtered: [OLTStateDoc] { let query = search.trimmingCharacters(in: .whitespaces) guard !query.isEmpty else { return olts } return olts.filter { $0.id.localizedCaseInsensitiveContains(query) || ($0.resolvedName?.localizedCaseInsensitiveContains(query) ?? false) } } func load() async { loadGeneration += 1 let gen = loadGeneration phase = .loading do { let fetched = try await connection.olt.allStates() let names = (try? await connection.olt.nameMap()) ?? [:] let merged: [OLTStateDoc] = fetched .map { olt in var olt = olt; olt.resolvedName = names[olt.id]; return olt } .sorted { $0.displayName.localizedCaseInsensitiveCompare($1.displayName) == .orderedAscending } guard gen == loadGeneration, !Task.isCancelled else { return } olts = merged phase = .loaded } catch { guard gen == loadGeneration, !Task.isCancelled else { return } phase = .failed((error as? APIError)?.localizedDescription ?? error.localizedDescription) } } }