- Custom copper AccentColor and warm VaultBackground color assets (light/dark), applied to both the main app and the AutoFill extension (which needed its own copy of the asset catalog plus ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME and an explicit .tint() modifier, since extensions don't pick up the app-wide accent color the same way a full SwiftUI App/Scene does). - GroupFilterView no longer uses DisclosureGroup for subgroups -- the whole tree renders always-expanded (indented per depth) so active filters are visible at a glance instead of requiring tap-to-expand. - Added a placeholder app icon (padlock glyph, light/dark/tinted variants plus macOS sizes) -- previously the icon slot existed but had no actual images, which blocks App Store/TestFlight archive validation. - Added ITSAppUsesNonExemptEncryption = NO (standard encryption only, via KeePassKit) to skip the export-compliance prompt on each upload. - Renamed the app from MyPass to KeeVault throughout: Xcode project/targets/ schemes, the MyPassCore package (now KeeVaultCore) and every import site, folder and file names, bundle identifiers (org.antiloop222.keevault) and their App Group/Keychain-group entitlements, and remaining UI/string references -- MyPass was already taken as an App Store app name.
65 lines
2.1 KiB
Swift
65 lines
2.1 KiB
Swift
// AutoFill/Views/CredentialListView.swift
|
|
import SwiftUI
|
|
import KeeVaultCore
|
|
|
|
struct CredentialListView: View {
|
|
@ObservedObject var vm: ExtensionViewModel
|
|
@State private var searchQuery: String = ""
|
|
|
|
private var displayedSuggested: [Entry] {
|
|
searchQuery.isEmpty ? vm.suggestedEntries : []
|
|
}
|
|
|
|
private var displayedAll: [Entry] {
|
|
let entries = searchQuery.isEmpty ? vm.allEntries : vm.session.allEntries()
|
|
guard !searchQuery.isEmpty else { return entries }
|
|
let q = searchQuery.lowercased()
|
|
return entries.filter {
|
|
$0.title.lowercased().contains(q)
|
|
|| $0.username.lowercased().contains(q)
|
|
|| $0.url.lowercased().contains(q)
|
|
}
|
|
}
|
|
|
|
var body: some View {
|
|
List {
|
|
if !displayedSuggested.isEmpty {
|
|
Section("Suggested") {
|
|
ForEach(displayedSuggested) { entry in
|
|
CredentialRow(entry: entry) { vm.select(entry) }
|
|
}
|
|
}
|
|
}
|
|
Section(displayedSuggested.isEmpty ? "" : "All Entries") {
|
|
if displayedAll.isEmpty {
|
|
Text("No entries found").foregroundStyle(.secondary)
|
|
} else {
|
|
ForEach(displayedAll) { entry in
|
|
CredentialRow(entry: entry) { vm.select(entry) }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.scrollContentBackground(.hidden)
|
|
.background(Color("VaultBackground"))
|
|
.searchable(text: $searchQuery, prompt: "Search…")
|
|
}
|
|
}
|
|
|
|
private struct CredentialRow: View {
|
|
let entry: Entry
|
|
let onSelect: () -> Void
|
|
|
|
var body: some View {
|
|
Button(action: onSelect) {
|
|
VStack(alignment: .leading, spacing: 2) {
|
|
Text(entry.title).font(.body).foregroundStyle(.primary)
|
|
Text(entry.username).font(.caption).foregroundStyle(.secondary)
|
|
if !entry.url.isEmpty {
|
|
Text(entry.url).font(.caption2).foregroundStyle(.tertiary)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|