diff --git a/composeApp/src/commonMain/kotlin/de/kitshn/Utils.kt b/composeApp/src/commonMain/kotlin/de/kitshn/Utils.kt index 9a3282d0..7a943aae 100644 --- a/composeApp/src/commonMain/kotlin/de/kitshn/Utils.kt +++ b/composeApp/src/commonMain/kotlin/de/kitshn/Utils.kt @@ -462,4 +462,37 @@ expect fun closeAppHandler(): () -> Unit fun String.extractUrl(delimiters: String = " ") = this .split(delimiters) - .firstOrNull { it.startsWith("http://") || it.startsWith("https://") } \ No newline at end of file + .firstOrNull { it.startsWith("http://") || it.startsWith("https://") } + +/** + * filters and scores a list of elements by a text query on a selected property. + * The priorities are + * 1. exact query match + * 2. a prefix of the property matches the query + * 3. a word boundary substring matches + * 4. a regular substring matches + * + * everything else is not included in the resulting score sorted list + */ +fun filterBySearchQuery( + list: List, + query: String, + selector: (T) -> String +): List { + if (query.isBlank()) return list + + return list + .mapNotNull { item -> + val name = selector(item) + val score = when { + name.equals(query, ignoreCase = true) -> 0 + name.startsWith(query, ignoreCase = true) -> 1 + name.contains(" $query", ignoreCase = true) -> 2 + name.contains(query, ignoreCase = true) -> 3 + else -> return@mapNotNull null + } + item to score + } + .sortedBy { it.second } + .map { it.first } +} diff --git a/composeApp/src/commonMain/kotlin/de/kitshn/ui/component/input/MealTypeSearchField.kt b/composeApp/src/commonMain/kotlin/de/kitshn/ui/component/input/MealTypeSearchField.kt index 0079c529..e517157a 100644 --- a/composeApp/src/commonMain/kotlin/de/kitshn/ui/component/input/MealTypeSearchField.kt +++ b/composeApp/src/commonMain/kotlin/de/kitshn/ui/component/input/MealTypeSearchField.kt @@ -37,6 +37,7 @@ import co.touchlab.kermit.Logger import de.kitshn.api.tandoor.TandoorClient import de.kitshn.api.tandoor.TandoorRequestsError import de.kitshn.api.tandoor.model.TandoorMealType +import de.kitshn.filterBySearchQuery import kitshn.composeapp.generated.resources.Res import kitshn.composeapp.generated.resources.common_unknown_meal_type import org.jetbrains.compose.resources.getString @@ -81,6 +82,16 @@ fun BaseMealTypeSearchField( } } + + val filteredMealTypes = remember(mealTypeList, searchText, selectedMealType) { + if (selectedMealType != null || searchText.isBlank()){ + mealTypeList + } else { + // use a priority filtering for better first results + filterBySearchQuery(mealTypeList, searchText) { it.name } + } + } + ExposedDropdownMenuBox( expanded = isExpanded, onExpandedChange = { @@ -112,7 +123,7 @@ fun BaseMealTypeSearchField( focus.clearFocus() } ) { - mealTypeList.forEach { + filteredMealTypes.forEach { DropdownMenuItem( text = { Text(it.name) }, onClick = {