mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-03-19 15:18:02 -04:00
Update native iOS search filter to use AND/OR (#1298)
This commit is contained in:
committed by
Leendert de Borst
parent
9da88cc7e7
commit
4ba2c8e6ab
@@ -227,38 +227,44 @@ public class PasskeyProviderViewModel: ObservableObject {
|
||||
}
|
||||
|
||||
func filterCredentials() {
|
||||
if searchText.isEmpty {
|
||||
let lowercasedSearch = searchText.lowercased().trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
if lowercasedSearch.isEmpty {
|
||||
filteredCredentials = credentials
|
||||
} else {
|
||||
let lowercasedSearch = searchText.lowercased()
|
||||
filteredCredentials = credentials.filter { credential in
|
||||
// Filter by service name
|
||||
if let serviceName = credential.service.name?.lowercased(),
|
||||
serviceName.contains(lowercasedSearch) {
|
||||
return true
|
||||
return
|
||||
}
|
||||
|
||||
// Split search term into words for AND search
|
||||
let searchWords = lowercasedSearch
|
||||
.components(separatedBy: .whitespacesAndNewlines)
|
||||
.filter { !$0.isEmpty }
|
||||
|
||||
if searchWords.isEmpty {
|
||||
filteredCredentials = credentials
|
||||
return
|
||||
}
|
||||
|
||||
// Filter credentials where ALL search words match (each in at least one field)
|
||||
filteredCredentials = credentials.filter { credential in
|
||||
// Prepare searchable fields including passkey rpIds
|
||||
var searchableFields = [
|
||||
credential.service.name?.lowercased() ?? "",
|
||||
credential.service.url?.lowercased() ?? "",
|
||||
credential.username?.lowercased() ?? "",
|
||||
credential.alias?.email?.lowercased() ?? "",
|
||||
credential.notes?.lowercased() ?? ""
|
||||
]
|
||||
|
||||
// Add passkey rpIds to searchable fields
|
||||
if let passkeys = credential.passkeys {
|
||||
searchableFields.append(contentsOf: passkeys.map { $0.rpId.lowercased() })
|
||||
}
|
||||
|
||||
// All search words must be found (each in at least one field)
|
||||
return searchWords.allSatisfy { word in
|
||||
searchableFields.contains { field in
|
||||
field.contains(word)
|
||||
}
|
||||
// Filter by service URL
|
||||
if let serviceUrl = credential.service.url?.lowercased(),
|
||||
serviceUrl.contains(lowercasedSearch) {
|
||||
return true
|
||||
}
|
||||
// Filter by username
|
||||
if let username = credential.username?.lowercased(),
|
||||
username.contains(lowercasedSearch) {
|
||||
return true
|
||||
}
|
||||
// Filter by email
|
||||
if let email = credential.alias?.email?.lowercased(),
|
||||
email.contains(lowercasedSearch) {
|
||||
return true
|
||||
}
|
||||
// Filter by passkey rpId
|
||||
if let passkeys = credential.passkeys {
|
||||
return passkeys.contains { passkey in
|
||||
passkey.rpId.lowercased().contains(lowercasedSearch)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,30 +231,34 @@ public class CredentialFilter {
|
||||
|
||||
return Array(matches)
|
||||
} else {
|
||||
// Non-URL fallback: Extract words from search text for better matching
|
||||
let searchWords = extractWords(from: searchText)
|
||||
// Non-URL fallback: Multi-word AND search for better matching
|
||||
let lowercasedSearch = searchText.lowercased().trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
// Split search term into words for AND search
|
||||
let searchWords = lowercasedSearch
|
||||
.components(separatedBy: .whitespacesAndNewlines)
|
||||
.filter { !$0.isEmpty }
|
||||
|
||||
if searchWords.isEmpty {
|
||||
// If no meaningful words after extraction, fall back to simple contains
|
||||
let lowercasedSearch = searchText.lowercased()
|
||||
return credentials.filter { credential in
|
||||
(credential.service.name?.lowercased().contains(lowercasedSearch) ?? false) ||
|
||||
(credential.username?.lowercased().contains(lowercasedSearch) ?? false) ||
|
||||
(credential.notes?.lowercased().contains(lowercasedSearch) ?? false)
|
||||
}
|
||||
return credentials
|
||||
}
|
||||
|
||||
// Match using extracted words
|
||||
// Filter credentials where ALL search words match (each in at least one field)
|
||||
return credentials.filter { credential in
|
||||
let serviceNameWords = credential.service.name.map { extractWords(from: $0) } ?? []
|
||||
let usernameWords = credential.username.map { extractWords(from: $0) } ?? []
|
||||
let notesWords = credential.notes.map { extractWords(from: $0) } ?? []
|
||||
// Prepare searchable fields
|
||||
let searchableFields = [
|
||||
credential.service.name?.lowercased() ?? "",
|
||||
credential.username?.lowercased() ?? "",
|
||||
credential.alias?.email?.lowercased() ?? "",
|
||||
credential.service.url?.lowercased() ?? "",
|
||||
credential.notes?.lowercased() ?? ""
|
||||
]
|
||||
|
||||
// Check if any search word matches any credential word exactly
|
||||
return searchWords.contains { searchWord in
|
||||
serviceNameWords.contains(searchWord) ||
|
||||
usernameWords.contains(searchWord) ||
|
||||
notesWords.contains(searchWord)
|
||||
// All search words must be found (each in at least one field)
|
||||
return searchWords.allSatisfy { word in
|
||||
searchableFields.contains { field in
|
||||
field.contains(word)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user