Add instrument region filtering
This commit is contained in:
@@ -6,6 +6,7 @@ struct InstrumentBrowserView: View {
|
||||
|
||||
@State private var searchText = ""
|
||||
@State private var selectedFamilyCategory: String?
|
||||
@State private var selectedRegionOrigin: String?
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
init(catalog: LocalInstrumentCatalog = LocalInstrumentCatalog()) {
|
||||
@@ -15,14 +16,27 @@ struct InstrumentBrowserView: View {
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
VStack(spacing: 0) {
|
||||
Picker("Family", selection: $selectedFamilyCategory) {
|
||||
Text("All families").tag(String?.none)
|
||||
HStack {
|
||||
Picker("Family", selection: $selectedFamilyCategory) {
|
||||
Text("All families").tag(String?.none)
|
||||
|
||||
ForEach(catalog.familyCategories, id: \.self) { familyCategory in
|
||||
Text(familyCategory).tag(Optional(familyCategory))
|
||||
ForEach(catalog.familyCategories, id: \.self) { familyCategory in
|
||||
Text(familyCategory).tag(Optional(familyCategory))
|
||||
}
|
||||
}
|
||||
.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)
|
||||
}
|
||||
.pickerStyle(.menu)
|
||||
.padding(.horizontal)
|
||||
.padding(.vertical, 8)
|
||||
|
||||
@@ -31,7 +45,8 @@ struct InstrumentBrowserView: View {
|
||||
List(
|
||||
catalog.instruments(
|
||||
matching: searchText,
|
||||
familyCategory: selectedFamilyCategory
|
||||
familyCategory: selectedFamilyCategory,
|
||||
regionOrigin: selectedRegionOrigin
|
||||
),
|
||||
id: \.id
|
||||
) { instrument in
|
||||
|
||||
@@ -12,12 +12,19 @@ public struct LocalInstrumentCatalog: Sendable {
|
||||
.sorted { $0.localizedCaseInsensitiveCompare($1) == .orderedAscending }
|
||||
}
|
||||
|
||||
public var regionsOfOrigin: [String] {
|
||||
Array(Set(items.compactMap(\.regionOrigin)))
|
||||
.sorted { $0.localizedCaseInsensitiveCompare($1) == .orderedAscending }
|
||||
}
|
||||
|
||||
public func instruments(
|
||||
matching query: String,
|
||||
familyCategory: String? = nil
|
||||
familyCategory: String? = nil,
|
||||
regionOrigin: String? = nil
|
||||
) -> [InstrumentCatalogItem] {
|
||||
let searchTokens = Self.searchTokens(for: query)
|
||||
let selectedFamilyCategory = familyCategory?.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let selectedRegionOrigin = regionOrigin?.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
return items
|
||||
.filter { item in
|
||||
@@ -25,7 +32,8 @@ public struct LocalInstrumentCatalog: Sendable {
|
||||
Self.searchableTerms(for: item).contains { $0.contains(token) }
|
||||
}
|
||||
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 }
|
||||
}
|
||||
|
||||
@@ -47,4 +47,19 @@ final class LocalInstrumentCatalogTests: XCTestCase {
|
||||
.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
@@ -84,7 +84,7 @@ requirement is missing and blocks implementation, record it in
|
||||
- [x] Keep catalog data out of SwiftUI views.
|
||||
- [x] Build searchable instrument browser.
|
||||
- [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.
|
||||
- [ ] Persist selected instruments on the current Song Project.
|
||||
- [ ] Make selected instruments available to OpenAI/song-generation
|
||||
|
||||
Reference in New Issue
Block a user