feat(library): Keep tabs when exclude-only categories filtering (#1074)

* feat(library): Keep tabs when exclude-only categories filtering

* Fix updating activeFilter status

* Avoid redundant state updates if unchanged sets are emitted

* improve
This commit is contained in:
Cuong-Tran 2025-07-23 14:23:20 +07:00 committed by GitHub
parent 0d34d2f8bc
commit a9f6f7e8b8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -65,6 +65,7 @@ import kotlinx.collections.immutable.ImmutableSet
import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.mutate import kotlinx.collections.immutable.mutate
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentSetOf
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toImmutableSet import kotlinx.collections.immutable.toImmutableSet
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
@ -198,21 +199,41 @@ class LibraryScreenModel(
combine( combine(
state.map { it.groupType }.distinctUntilChanged(), state.map { it.groupType }.distinctUntilChanged(),
libraryPreferences.sortingMode().changes(), libraryPreferences.sortingMode().changes(),
::Pair, // KMK -->
state.map { it.searchQuery.isNullOrBlank() && !it.hasActiveFilters }.distinctUntilChanged(),
::Triple,
// KMK <--
), ),
// SY <-- // SY <--
// KMK --> // KMK -->
combine( combine(
getCategoriesPerLibraryManga.subscribe(), combine(
state.map { it.filterCategory }.distinctUntilChanged(), getCategoriesPerLibraryManga.subscribe(),
state.map { it.searchQuery.isNullOrBlank() && !it.hasActiveFilters }.distinctUntilChanged(), state.map { it.filterCategory }.distinctUntilChanged(),
::Triple, ::Pair,
),
combine(
state.map { it.includedCategories }.distinctUntilChanged(),
state.map { it.excludedCategories }.distinctUntilChanged(),
::Pair,
),
::Pair,
), ),
// KMK <-- // KMK <--
) { (searchQuery, library, _), (tracks, trackingFilter), (groupType, sort), (categoriesPerManga, filterCategory, noActiveFilterOrSearch) -> ) { (searchQuery, library, _), (tracks, trackingFilter), (groupType, sort, noActiveFilterOrSearch), (categoryFilters, filteredCategories) ->
val (categoriesPerManga, filterCategory) = categoryFilters
val (includedCategories, _) = filteredCategories
library library
// SY --> // SY -->
.applyGrouping(/* KMK --> */ if (filterCategory) LibraryGroup.UNGROUPED else /* KMK <-- */ groupType) .applyGrouping(
// KMK -->
if (filterCategory && includedCategories.isNotEmpty()) {
LibraryGroup.UNGROUPED
} else {
// KMK <--
groupType
},
)
// SY <-- // SY <--
.applyFilters( .applyFilters(
tracks, tracks,
@ -347,11 +368,26 @@ class LibraryScreenModel(
// KMK --> // KMK -->
libraryPreferences.filterCategories().changes() libraryPreferences.filterCategories().changes()
.distinctUntilChanged()
.onEach { .onEach {
mutableState.update { state -> mutableState.update { state ->
state.copy(filterCategory = it) state.copy(filterCategory = it)
} }
}.launchIn(screenModelScope) }.launchIn(screenModelScope)
libraryPreferences.filterCategoriesInclude().changes()
.distinctUntilChanged()
.onEach {
mutableState.update { state ->
state.copy(includedCategories = it.mapNotNull(String::toLongOrNull).toImmutableSet())
}
}.launchIn(screenModelScope)
libraryPreferences.filterCategoriesExclude().changes()
.distinctUntilChanged()
.onEach {
mutableState.update { state ->
state.copy(excludedCategories = it.mapNotNull(String::toLongOrNull).toImmutableSet())
}
}.launchIn(screenModelScope)
screenModelScope.launchIO { screenModelScope.launchIO {
getCategories getCategories
@ -401,12 +437,6 @@ class LibraryScreenModel(
val filterLewd = prefs.filterLewd val filterLewd = prefs.filterLewd
// SY <-- // SY <--
// KMK -->
val filterCategories = prefs.filterCategories
val includedCategories = prefs.filterCategoriesInclude
val excludedCategories = prefs.filterCategoriesExclude
// KMK <--
val filterFnDownloaded: (LibraryItem) -> Boolean = { val filterFnDownloaded: (LibraryItem) -> Boolean = {
applyFilter(filterDownloaded) { applyFilter(filterDownloaded) {
it.libraryManga.manga.isLocal() || it.libraryManga.manga.isLocal() ||
@ -460,12 +490,12 @@ class LibraryScreenModel(
// KMK --> // KMK -->
val filterFnCategories: (LibraryItem) -> Boolean = categories@{ item -> val filterFnCategories: (LibraryItem) -> Boolean = categories@{ item ->
if (!filterCategories) return@categories true if (!state.value.filterCategory) return@categories true
val mangaCategories = categoriesPerManga[item.libraryManga.id].orEmpty() val mangaCategories = categoriesPerManga[item.libraryManga.id].orEmpty()
val isExcluded = excludedCategories.any { it in mangaCategories } val isExcluded = state.value.excludedCategories.any { it in mangaCategories }
val isIncluded = includedCategories.isEmpty() || includedCategories.all { it in mangaCategories } val isIncluded = state.value.includedCategories.isEmpty() || state.value.includedCategories.all { it in mangaCategories }
!isExcluded && isIncluded !isExcluded && isIncluded
} }
@ -625,8 +655,6 @@ class LibraryScreenModel(
libraryPreferences.sourceBadge().changes(), libraryPreferences.sourceBadge().changes(),
libraryPreferences.useLangIcon().changes(), libraryPreferences.useLangIcon().changes(),
libraryPreferences.filterCategories().changes(), libraryPreferences.filterCategories().changes(),
libraryPreferences.filterCategoriesInclude().changes(),
libraryPreferences.filterCategoriesExclude().changes(),
// KMK <-- // KMK <--
) { ) {
ItemPreferences( ItemPreferences(
@ -649,9 +677,6 @@ class LibraryScreenModel(
sourceBadge = it[13] as Boolean, sourceBadge = it[13] as Boolean,
useLangIcon = it[14] as Boolean, useLangIcon = it[14] as Boolean,
filterCategories = it[15] as Boolean, filterCategories = it[15] as Boolean,
filterCategoriesInclude = (it[16] as Set<*>).filterIsInstance<String>().mapNotNull(String::toLongOrNull).toImmutableSet(),
filterCategoriesExclude = (it[17] as Set<*>).filterIsInstance<String>().mapNotNull(String::toLongOrNull).toImmutableSet(),
// KMK <--
) )
} }
} }
@ -1585,8 +1610,6 @@ class LibraryScreenModel(
// SY <-- // SY <--
// KMK --> // KMK -->
val filterCategories: Boolean, val filterCategories: Boolean,
val filterCategoriesInclude: ImmutableSet<Long>,
val filterCategoriesExclude: ImmutableSet<Long>,
// KMK <-- // KMK <--
) )
@ -1610,6 +1633,8 @@ class LibraryScreenModel(
// KMK --> // KMK -->
val libraryCategories: List<Category> = emptyList(), val libraryCategories: List<Category> = emptyList(),
val filterCategory: Boolean = false, val filterCategory: Boolean = false,
val includedCategories: ImmutableSet<Long> = persistentSetOf(),
val excludedCategories: ImmutableSet<Long> = persistentSetOf(),
// KMK <-- // KMK <--
) { ) {
private val libraryCount by lazy { private val libraryCount by lazy {