// MyPass/Views/EntryListView.swift import SwiftUI import MyPassCore struct EntryListView: View { @ObservedObject var vm: VaultViewModel @State private var isSearching = false @State private var showAddEntry = false var body: some View { List { ForEach(vm.displayedEntries) { flatEntry in NavigationLink( destination: EntryDetailView( entry: flatEntry.entry, groupId: flatEntry.parentGroupId, session: vm.session ) ) { EntryRow(flatEntry: flatEntry) } } .onDelete { offsets in offsets.map { vm.displayedEntries[$0] }.forEach(vm.deleteEntry) } } .navigationTitle("MyPass") .searchable(text: $vm.searchQuery, isPresented: $isSearching, prompt: "Search entries…") .toolbar { ToolbarItem(placement: .primaryAction) { Button { if isSearching { vm.searchQuery = "" } isSearching.toggle() } label: { Image(systemName: "magnifyingglass") } } ToolbarItem(placement: .primaryAction) { Button { showAddEntry = true } label: { Image(systemName: "plus") } } } .sheet(isPresented: $showAddEntry) { EntryEditView(vm: EntryEditViewModel(session: vm.session, groupId: defaultGroupId)) } .alert("Error", isPresented: Binding( get: { vm.errorMessage != nil }, set: { if !$0 { vm.errorMessage = nil } } )) { Button("OK", role: .cancel) { vm.errorMessage = nil } } message: { Text(vm.errorMessage ?? "") } } /// The most recently toggled-on filter group, if any filter is still active via it, /// otherwise the vault root. private var defaultGroupId: UUID { vm.lastToggledGroupId ?? vm.session.database?.root.id ?? UUID() } } private struct EntryRow: View { let flatEntry: FlatEntry private var breadcrumbText: String? { guard !flatEntry.breadcrumb.isEmpty else { return nil } return flatEntry.breadcrumb.map(\.name).joined(separator: " > ") } var body: some View { HStack(alignment: .top, spacing: 12) { Image(systemName: KeePassIconMapper.symbolName(forIconIndex: flatEntry.entry.iconIndex)) .foregroundStyle(.secondary) .frame(width: 24) VStack(alignment: .leading, spacing: 2) { Text(flatEntry.entry.title).font(.body) if let breadcrumbText { Text("\(flatEntry.entry.username) · \(breadcrumbText)") .font(.caption) .foregroundStyle(.secondary) .lineLimit(1) .truncationMode(.middle) } else { Text(flatEntry.entry.username) .font(.caption) .foregroundStyle(.secondary) } } } } }