Add instrument region filtering

This commit is contained in:
diyaa
2026-09-14 01:08:30 +02:00
parent 9ddac746e5
commit 6c6aeae5bb
4 changed files with 47 additions and 9 deletions
@@ -6,6 +6,7 @@ struct InstrumentBrowserView: View {
@State private var searchText = "" @State private var searchText = ""
@State private var selectedFamilyCategory: String? @State private var selectedFamilyCategory: String?
@State private var selectedRegionOrigin: String?
@Environment(\.dismiss) private var dismiss @Environment(\.dismiss) private var dismiss
init(catalog: LocalInstrumentCatalog = LocalInstrumentCatalog()) { init(catalog: LocalInstrumentCatalog = LocalInstrumentCatalog()) {
@@ -15,6 +16,7 @@ struct InstrumentBrowserView: View {
var body: some View { var body: some View {
NavigationStack { NavigationStack {
VStack(spacing: 0) { VStack(spacing: 0) {
HStack {
Picker("Family", selection: $selectedFamilyCategory) { Picker("Family", selection: $selectedFamilyCategory) {
Text("All families").tag(String?.none) Text("All families").tag(String?.none)
@@ -23,6 +25,18 @@ struct InstrumentBrowserView: View {
} }
} }
.pickerStyle(.menu) .pickerStyle(.menu)
Spacer()
Picker("Region", selection: $selectedRegionOrigin) {
Text("All regions").tag(String?.none)
ForEach(catalog.regionsOfOrigin, id: \.self) { regionOrigin in
Text(regionOrigin).tag(Optional(regionOrigin))
}
}
.pickerStyle(.menu)
}
.padding(.horizontal) .padding(.horizontal)
.padding(.vertical, 8) .padding(.vertical, 8)
@@ -31,7 +45,8 @@ struct InstrumentBrowserView: View {
List( List(
catalog.instruments( catalog.instruments(
matching: searchText, matching: searchText,
familyCategory: selectedFamilyCategory familyCategory: selectedFamilyCategory,
regionOrigin: selectedRegionOrigin
), ),
id: \.id id: \.id
) { instrument in ) { instrument in
@@ -12,12 +12,19 @@ public struct LocalInstrumentCatalog: Sendable {
.sorted { $0.localizedCaseInsensitiveCompare($1) == .orderedAscending } .sorted { $0.localizedCaseInsensitiveCompare($1) == .orderedAscending }
} }
public var regionsOfOrigin: [String] {
Array(Set(items.compactMap(\.regionOrigin)))
.sorted { $0.localizedCaseInsensitiveCompare($1) == .orderedAscending }
}
public func instruments( public func instruments(
matching query: String, matching query: String,
familyCategory: String? = nil familyCategory: String? = nil,
regionOrigin: String? = nil
) -> [InstrumentCatalogItem] { ) -> [InstrumentCatalogItem] {
let searchTokens = Self.searchTokens(for: query) let searchTokens = Self.searchTokens(for: query)
let selectedFamilyCategory = familyCategory?.trimmingCharacters(in: .whitespacesAndNewlines) let selectedFamilyCategory = familyCategory?.trimmingCharacters(in: .whitespacesAndNewlines)
let selectedRegionOrigin = regionOrigin?.trimmingCharacters(in: .whitespacesAndNewlines)
return items return items
.filter { item in .filter { item in
@@ -25,7 +32,8 @@ public struct LocalInstrumentCatalog: Sendable {
Self.searchableTerms(for: item).contains { $0.contains(token) } Self.searchableTerms(for: item).contains { $0.contains(token) }
} }
let matchesFamily = selectedFamilyCategory?.isEmpty != false || item.familyCategory == selectedFamilyCategory let matchesFamily = selectedFamilyCategory?.isEmpty != false || item.familyCategory == selectedFamilyCategory
return matchesSearch && matchesFamily let matchesRegion = selectedRegionOrigin?.isEmpty != false || item.regionOrigin == selectedRegionOrigin
return matchesSearch && matchesFamily && matchesRegion
} }
.sorted { $0.name.localizedCaseInsensitiveCompare($1.name) == .orderedAscending } .sorted { $0.name.localizedCaseInsensitiveCompare($1.name) == .orderedAscending }
} }
@@ -47,4 +47,19 @@ final class LocalInstrumentCatalogTests: XCTestCase {
.allSatisfy { $0.familyCategory == "Plucked strings" } .allSatisfy { $0.familyCategory == "Plucked strings" }
) )
} }
func testRegionsAndFilteringComeFromCatalogMetadata() {
let catalog = LocalInstrumentCatalog()
let middleEasternPluckedStrings = catalog.instruments(
matching: "",
familyCategory: "Plucked strings",
regionOrigin: "Middle East"
)
XCTAssertTrue(catalog.regionsOfOrigin.contains("Middle East"))
XCTAssertFalse(middleEasternPluckedStrings.isEmpty)
XCTAssertTrue(middleEasternPluckedStrings.contains { $0.id == "oud" })
XCTAssertTrue(middleEasternPluckedStrings.allSatisfy { $0.regionOrigin == "Middle East" })
XCTAssertTrue(middleEasternPluckedStrings.allSatisfy { $0.familyCategory == "Plucked strings" })
}
} }
+1 -1
View File
@@ -84,7 +84,7 @@ requirement is missing and blocks implementation, record it in
- [x] Keep catalog data out of SwiftUI views. - [x] Keep catalog data out of SwiftUI views.
- [x] Build searchable instrument browser. - [x] Build searchable instrument browser.
- [x] Add browsing/filtering by family/category. - [x] Add browsing/filtering by family/category.
- [ ] Add browsing/filtering by region/origin where useful. - [x] Add browsing/filtering by region/origin where useful.
- [ ] Add checkbox-based multi-select and deselect behavior. - [ ] Add checkbox-based multi-select and deselect behavior.
- [ ] Persist selected instruments on the current Song Project. - [ ] Persist selected instruments on the current Song Project.
- [ ] Make selected instruments available to OpenAI/song-generation - [ ] Make selected instruments available to OpenAI/song-generation