Cleanup extension screen search query predicate

This commit is contained in:
AntsyLich
2025-12-27 01:34:00 +06:00
parent a39b5a56e8
commit e059190fab
@@ -50,62 +50,30 @@ class ExtensionsScreenModel(
ExtensionUiModel.Item(it, map[it.pkgName] ?: InstallStep.Idle) ExtensionUiModel.Item(it, map[it.pkgName] ?: InstallStep.Idle)
} }
} }
val queryFilter: (String) -> ((Extension) -> Boolean) = { query ->
filter@{ extension ->
if (query.isEmpty()) return@filter true
query.split(",").any { _input ->
val input = _input.trim()
if (input.isEmpty()) return@any false
when (extension) {
is Extension.Available -> {
extension.sources.any {
it.name.contains(input, ignoreCase = true) ||
it.baseUrl.contains(input, ignoreCase = true) ||
it.id == input.toLongOrNull()
} ||
extension.name.contains(input, ignoreCase = true)
}
is Extension.Installed -> {
extension.sources.any {
it.name.contains(input, ignoreCase = true) ||
it.id == input.toLongOrNull() ||
if (it is HttpSource) {
it.baseUrl.contains(input, ignoreCase = true)
} else {
false
}
} ||
extension.name.contains(input, ignoreCase = true)
}
is Extension.Untrusted -> extension.name.contains(input, ignoreCase = true)
}
}
}
}
screenModelScope.launchIO { screenModelScope.launchIO {
combine( combine(
state.map { it.searchQuery }.distinctUntilChanged().debounce(SEARCH_DEBOUNCE_MILLIS), state.map { it.searchQuery }
.distinctUntilChanged()
.debounce(SEARCH_DEBOUNCE_MILLIS)
.map { searchQueryPredicate(it ?: "") },
currentDownloads, currentDownloads,
getExtensions.subscribe(), getExtensions.subscribe(),
) { query, downloads, (_updates, _installed, _available, _untrusted) -> ) { predicate, downloads, (_updates, _installed, _available, _untrusted) ->
val searchQuery = query ?: "" buildMap {
val updates = _updates.filter(predicate).map(extensionMapper(downloads))
val itemsGroups: ItemGroups = mutableMapOf()
val updates = _updates.filter(queryFilter(searchQuery)).map(extensionMapper(downloads))
if (updates.isNotEmpty()) { if (updates.isNotEmpty()) {
itemsGroups[ExtensionUiModel.Header.Resource(MR.strings.ext_updates_pending)] = updates put(ExtensionUiModel.Header.Resource(MR.strings.ext_updates_pending), updates)
} }
val installed = _installed.filter(queryFilter(searchQuery)).map(extensionMapper(downloads)) val installed = _installed.filter(predicate).map(extensionMapper(downloads))
val untrusted = _untrusted.filter(queryFilter(searchQuery)).map(extensionMapper(downloads)) val untrusted = _untrusted.filter(predicate).map(extensionMapper(downloads))
if (installed.isNotEmpty() || untrusted.isNotEmpty()) { if (installed.isNotEmpty() || untrusted.isNotEmpty()) {
itemsGroups[ExtensionUiModel.Header.Resource(MR.strings.ext_installed)] = installed + untrusted put(ExtensionUiModel.Header.Resource(MR.strings.ext_installed), installed + untrusted)
} }
val languagesWithExtensions = _available val languagesWithExtensions = _available
.filter(queryFilter(searchQuery)) .filter(predicate)
.groupBy { it.lang } .groupBy { it.lang }
.toSortedMap(LocaleHelper.comparator) .toSortedMap(LocaleHelper.comparator)
.map { (lang, exts) -> .map { (lang, exts) ->
@@ -113,16 +81,15 @@ class ExtensionsScreenModel(
exts.map(extensionMapper(downloads)) exts.map(extensionMapper(downloads))
} }
if (languagesWithExtensions.isNotEmpty()) { if (languagesWithExtensions.isNotEmpty()) {
itemsGroups.putAll(languagesWithExtensions) putAll(languagesWithExtensions)
} }
itemsGroups
} }
.collectLatest { }
.collectLatest { items ->
mutableState.update { state -> mutableState.update { state ->
state.copy( state.copy(
isLoading = false, isLoading = false,
items = it, items = items,
) )
} }
} }
@@ -139,6 +106,36 @@ class ExtensionsScreenModel(
.launchIn(screenModelScope) .launchIn(screenModelScope)
} }
fun searchQueryPredicate(query: String): (Extension) -> Boolean {
val subqueries = query.split(",")
.map { it.trim() }
.filterNot { it.isBlank() }
if (subqueries.isEmpty()) return { true }
return { extension ->
subqueries.any { subquery ->
if (extension.name.contains(subquery, ignoreCase = true)) return@any true
when (extension) {
is Extension.Installed -> extension.sources.any { source ->
source.name.contains(subquery, ignoreCase = true) ||
(source as? HttpSource)?.baseUrl?.contains(subquery, ignoreCase = true) == true ||
source.id == subquery.toLongOrNull()
}
is Extension.Available -> extension.sources.any {
it.name.contains(subquery, ignoreCase = true) ||
it.baseUrl.contains(subquery, ignoreCase = true) ||
it.id == subquery.toLongOrNull()
}
else -> false
}
}
}
}
fun search(query: String?) { fun search(query: String?) {
mutableState.update { mutableState.update {
it.copy(searchQuery = query) it.copy(searchQuery = query)
@@ -222,7 +219,7 @@ class ExtensionsScreenModel(
} }
} }
typealias ItemGroups = MutableMap<ExtensionUiModel.Header, List<ExtensionUiModel.Item>> typealias ItemGroups = Map<ExtensionUiModel.Header, List<ExtensionUiModel.Item>>
object ExtensionUiModel { object ExtensionUiModel {
sealed interface Header { sealed interface Header {