// MyPass/ViewModels/EntryEditViewModel.swift import Foundation import Combine import SwiftUI import MyPassCore @MainActor final class EntryEditViewModel: ObservableObject { @Published var title: String @Published var username: String @Published var password: String @Published var url: String @Published var notes: String @Published var customFields: [CustomField] @Published var groupId: UUID @Published var errorMessage: String? @Published var isSaving: Bool = false let session: VaultSession private let existingEntry: Entry? /// `groupId` is the target group for a new entry, or the entry's current group when /// editing (shown read-only in that case -- MyPass doesn't support re-parenting an /// existing entry to a different group yet). init(session: VaultSession, groupId: UUID, existing: Entry? = nil) { self.session = session self.groupId = groupId self.existingEntry = existing title = existing?.title ?? "" username = existing?.username ?? "" password = existing?.password.reveal() ?? "" url = existing?.url ?? "" notes = existing?.notes ?? "" customFields = existing?.customFields ?? [] } var isEditing: Bool { existingEntry != nil } var flatGroups: [FlatGroup] { guard let root = session.database?.root else { return [] } return flattenGroups(in: root) } func save(dismiss: @escaping () -> Void) { Task { isSaving = true errorMessage = nil do { if var entry = existingEntry { entry.title = title entry.username = username entry.password = ProtectedString(password, isProtected: true) entry.url = url entry.notes = notes entry.customFields = customFields try session.updateEntry(entry) } else { let entry = Entry( title: title, username: username, password: ProtectedString(password, isProtected: true), url: url, notes: notes, customFields: customFields ) try session.addEntry(entry, toGroupId: groupId) } dismiss() } catch { errorMessage = error.localizedDescription } isSaving = false } } func addCustomField() { customFields.append(CustomField(key: "", value: ProtectedString("", isProtected: false))) } func removeCustomField(at offsets: IndexSet) { customFields.remove(atOffsets: offsets) } }