Add search to the currency picker

Filters by code or display name, case-insensitively, across all three
sections (current, selected, and remaining currencies) — a section
disappears entirely once nothing in it matches. The matching logic
lives in shared code (matchesCurrencySearch) so Compose and SwiftUI
stay in sync; SwiftUI wires it into the native .searchable() field,
Compose gets a plain search text field with a clear button.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FXcrACM5sok3M1KQJ8kByy
This commit is contained in:
2026-09-10 08:22:07 +03:00
co-authored by Claude Sonnet 5
parent e7af3e6fc8
commit 25a69d8f69
5 changed files with 109 additions and 38 deletions
+22 -7
View File
@@ -11,28 +11,43 @@ struct CurrencyPickerSheet: View {
let onToggle: (String) -> Void
@Environment(\.dismiss) private var dismiss
@State private var searchQuery = ""
private func matches(_ info: CurrencyInfo) -> Bool {
SystemCurrenciesKt.matchesCurrencySearch(info: info, query: searchQuery)
}
var body: some View {
NavigationStack {
List {
Section(IosLocalizationKt.localizedString(resource: MR.strings.shared.current_currency_section_title)) {
currencyRow(for: currentCurrency, isSelected: true, onTap: nil)
if matches(currentCurrency) {
Section(IosLocalizationKt.localizedString(resource: MR.strings.shared.current_currency_section_title)) {
currencyRow(for: currentCurrency, isSelected: true, onTap: nil)
}
}
if !selectedOtherCurrencies.isEmpty {
let matchingSelectedOthers = selectedOtherCurrencies.filter(matches)
if !matchingSelectedOthers.isEmpty {
Section(IosLocalizationKt.localizedString(resource: MR.strings.shared.selected_currencies_section_title)) {
ForEach(selectedOtherCurrencies, id: \.code) { info in
ForEach(matchingSelectedOthers, id: \.code) { info in
currencyRow(for: info, isSelected: true, onTap: { onToggle(info.code) })
}
}
}
Section(IosLocalizationKt.localizedString(resource: MR.strings.shared.currencies_section_title)) {
ForEach(unselectedCurrencies, id: \.code) { info in
currencyRow(for: info, isSelected: false, onTap: { onToggle(info.code) })
let matchingUnselected = unselectedCurrencies.filter(matches)
if !matchingUnselected.isEmpty {
Section(IosLocalizationKt.localizedString(resource: MR.strings.shared.currencies_section_title)) {
ForEach(matchingUnselected, id: \.code) { info in
currencyRow(for: info, isSelected: false, onTap: { onToggle(info.code) })
}
}
}
}
.searchable(
text: $searchQuery,
prompt: IosLocalizationKt.localizedString(resource: MR.strings.shared.search_currencies_placeholder)
)
.navigationTitle(IosLocalizationKt.localizedString(resource: MR.strings.shared.currencies_section_title))
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)