From 1ec0d02e44700bc5ff2059e36ec680234a98cc1e Mon Sep 17 00:00:00 2001 From: Christophe Vila Date: Sat, 19 Sep 2026 17:02:03 +0200 Subject: [PATCH] feat: require groupId in EntryEditViewModel, add group picker to EntryEditView Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01WYqycDFsynHH9VnnK7LNSf --- MyPass/ViewModels/EntryEditViewModel.swift | 27 ++++++++++------------ MyPass/Views/EntryDetailView.swift | 3 ++- MyPass/Views/EntryEditView.swift | 20 ++++++++++++---- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/MyPass/ViewModels/EntryEditViewModel.swift b/MyPass/ViewModels/EntryEditViewModel.swift index e307cc0..9480604 100644 --- a/MyPass/ViewModels/EntryEditViewModel.swift +++ b/MyPass/ViewModels/EntryEditViewModel.swift @@ -1,8 +1,4 @@ -// -// EntryEditViewModel.swift -// MyPass -// - +// MyPass/ViewModels/EntryEditViewModel.swift import Foundation import Combine import SwiftUI @@ -16,16 +12,17 @@ final class EntryEditViewModel: ObservableObject { @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 - private let session: VaultSession - private let groupId: UUID? + let session: VaultSession private let existingEntry: Entry? - /// `groupId` is required when adding a new entry, and unused when editing an existing one - /// (an existing entry's group is looked up internally by `VaultSession.updateEntry`). - init(session: VaultSession, groupId: UUID? = nil, existing: Entry? = nil) { + /// `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 @@ -39,6 +36,11 @@ final class EntryEditViewModel: ObservableObject { 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 @@ -53,11 +55,6 @@ final class EntryEditViewModel: ObservableObject { entry.customFields = customFields try session.updateEntry(entry) } else { - guard let groupId else { - errorMessage = "No group selected for the new entry." - isSaving = false - return - } let entry = Entry( title: title, username: username, diff --git a/MyPass/Views/EntryDetailView.swift b/MyPass/Views/EntryDetailView.swift index c948f6f..b817444 100644 --- a/MyPass/Views/EntryDetailView.swift +++ b/MyPass/Views/EntryDetailView.swift @@ -9,6 +9,7 @@ import MyPassCore struct EntryDetailView: View { let entry: Entry + let groupId: UUID let session: VaultSession @State private var showPassword = false @@ -30,7 +31,7 @@ struct EntryDetailView: View { } } .sheet(isPresented: $showEditSheet) { - EntryEditView(vm: EntryEditViewModel(session: session, existing: entry)) + EntryEditView(vm: EntryEditViewModel(session: session, groupId: groupId, existing: entry)) } #if os(macOS) .keyboardShortcut("e", modifiers: .command) diff --git a/MyPass/Views/EntryEditView.swift b/MyPass/Views/EntryEditView.swift index d6b1265..094b23c 100644 --- a/MyPass/Views/EntryEditView.swift +++ b/MyPass/Views/EntryEditView.swift @@ -1,8 +1,4 @@ -// -// EntryEditView.swift -// MyPass -// - +// MyPass/Views/EntryEditView.swift import SwiftUI import MyPassCore @@ -13,6 +9,16 @@ struct EntryEditView: View { var body: some View { NavigationStack { Form { + if !vm.isEditing { + Section("Group") { + Picker("Group", selection: $vm.groupId) { + ForEach(vm.flatGroups) { flatGroup in + Text(groupLabel(for: flatGroup)).tag(flatGroup.group.id) + } + } + } + } + Section("Credentials") { TextField("Title", text: $vm.title) TextField("Username", text: $vm.username) @@ -69,4 +75,8 @@ struct EntryEditView: View { } } } + + private func groupLabel(for flatGroup: FlatGroup) -> String { + (flatGroup.breadcrumb.map(\.name) + [flatGroup.group.name]).joined(separator: " > ") + } }