From 7f820b590c93ed3920fe64f0739110c1ecc980d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferm=C3=ADn=20Cirella?= Date: Tue, 20 May 2025 05:32:41 -0300 Subject: [PATCH] feat(library): add category library filter (#895) * feat(library): add category library filter * feat(library): pre-calculate filter categories map * feat(library): ungroup library items if category filter is enabled * refactor `combine` function and improve flow handling * fix: Exclude system's `Default` category from the list (as no other category would match with it) * refactor: categories conversion * feat: correctly restore categories filter preferences * feat: improve DB ops performance * chore: add KMK comment --------- Co-authored-by: Cuong-Tran --- .../java/eu/kanade/domain/DomainModule.kt | 2 + .../library/LibrarySettingsDialog.kt | 85 +++++++++++++++ .../ui/library/LibraryScreenModel.kt | 102 ++++++++++++++++-- .../kanade/tachiyomi/ui/library/LibraryTab.kt | 3 + .../CategoryPreferencesCleanupMigration.kt | 4 + .../data/category/CategoryRepositoryImpl.kt | 13 +++ .../tachiyomi/data/mangas_categories.sq | 11 +- .../category/interactor/DeleteCategory.kt | 4 + .../GetCategoriesPerLibraryManga.kt | 13 +++ .../category/repository/CategoryRepository.kt | 4 + .../library/service/LibraryPreferences.kt | 21 ++++ .../moko-resources/base/strings.xml | 1 + 12 files changed, 252 insertions(+), 11 deletions(-) create mode 100644 domain/src/main/java/tachiyomi/domain/category/interactor/GetCategoriesPerLibraryManga.kt diff --git a/app/src/main/java/eu/kanade/domain/DomainModule.kt b/app/src/main/java/eu/kanade/domain/DomainModule.kt index 9ed2be9b8..7bbba69ff 100644 --- a/app/src/main/java/eu/kanade/domain/DomainModule.kt +++ b/app/src/main/java/eu/kanade/domain/DomainModule.kt @@ -48,6 +48,7 @@ import tachiyomi.data.updates.UpdatesRepositoryImpl import tachiyomi.domain.category.interactor.CreateCategoryWithName import tachiyomi.domain.category.interactor.DeleteCategory import tachiyomi.domain.category.interactor.GetCategories +import tachiyomi.domain.category.interactor.GetCategoriesPerLibraryManga import tachiyomi.domain.category.interactor.HideCategory import tachiyomi.domain.category.interactor.RenameCategory import tachiyomi.domain.category.interactor.ReorderCategory @@ -116,6 +117,7 @@ class DomainModule : InjektModule { addFactory { DeleteCategory(get(), get(), get()) } // KMK --> addFactory { HideCategory(get()) } + addFactory { GetCategoriesPerLibraryManga(get()) } // KMK <-- addSingletonFactory { MangaRepositoryImpl(get()) } diff --git a/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt b/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt index 0b80e8257..3cd46f5fc 100644 --- a/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt @@ -1,27 +1,40 @@ package eu.kanade.presentation.library import android.content.res.Configuration +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Refresh +import androidx.compose.material3.Checkbox import androidx.compose.material3.FilterChip import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp import androidx.compose.ui.util.fastForEach import dev.icerock.moko.resources.StringResource +import eu.kanade.presentation.category.visualName import eu.kanade.presentation.components.TabbedDialog import eu.kanade.presentation.components.TabbedDialogPaddings +import eu.kanade.presentation.more.settings.widget.TriStateListDialog import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.library.LibrarySettingsScreenModel import eu.kanade.tachiyomi.util.system.isReleaseBuildType @@ -29,6 +42,7 @@ import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.map import tachiyomi.core.common.preference.TriState +import tachiyomi.core.common.preference.toggle import tachiyomi.domain.category.model.Category import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.library.model.LibraryGroup @@ -43,9 +57,11 @@ import tachiyomi.presentation.core.components.CheckboxItem import tachiyomi.presentation.core.components.HeadingItem import tachiyomi.presentation.core.components.IconItem import tachiyomi.presentation.core.components.SettingsChipRow +import tachiyomi.presentation.core.components.SettingsItemsPaddings import tachiyomi.presentation.core.components.SliderItem import tachiyomi.presentation.core.components.SortItem import tachiyomi.presentation.core.components.TriStateItem +import tachiyomi.presentation.core.components.material.TextButton import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.util.collectAsState @@ -57,6 +73,9 @@ fun LibrarySettingsDialog( // SY --> hasCategories: Boolean, // SY <-- + // KMK --> + categories: List, + // KMK <-- ) { TabbedDialog( onDismissRequest = onDismissRequest, @@ -77,6 +96,9 @@ fun LibrarySettingsDialog( when (page) { 0 -> FilterPage( screenModel = screenModel, + // KMK --> + categories = categories, + // KMK <-- ) 1 -> SortPage( category = category, @@ -100,6 +122,7 @@ fun LibrarySettingsDialog( @Composable private fun ColumnScope.FilterPage( screenModel: LibrarySettingsScreenModel, + categories: List, ) { val filterDownloaded by screenModel.libraryPreferences.filterDownloaded().collectAsState() val downloadedOnly by screenModel.preferences.downloadedOnly().collectAsState() @@ -157,6 +180,13 @@ private fun ColumnScope.FilterPage( ) // SY <-- + // KMY --> + CategoriesFilter( + libraryPreferences = screenModel.libraryPreferences, + categories = categories, + ) + // KMK <-- + val trackers by screenModel.trackersFlow.collectAsState() when (trackers.size) { 0 -> { @@ -434,3 +464,58 @@ private fun ColumnScope.GroupPage( } } // SY <-- + +// KMK --> +@Composable +private fun CategoriesFilter( + libraryPreferences: LibraryPreferences, + categories: List, +) { + val filterCategories by libraryPreferences.filterCategories().collectAsState() + + val filterCategoriesInclude = libraryPreferences.filterCategoriesInclude() + val filterCategoriesExclude = libraryPreferences.filterCategoriesExclude() + val included by filterCategoriesInclude.collectAsState() + val excluded by filterCategoriesExclude.collectAsState() + + var showCategoriesDialog by rememberSaveable { mutableStateOf(false) } + if (showCategoriesDialog) { + TriStateListDialog( + title = stringResource(MR.strings.categories), + message = stringResource(KMR.strings.pref_library_filter_categories_details), + items = categories, + initialChecked = included.mapNotNull { id -> categories.find { it.id.toString() == id } }, + initialInversed = excluded.mapNotNull { id -> categories.find { it.id.toString() == id } }, + itemLabel = { it.visualName }, + onDismissRequest = { showCategoriesDialog = false }, + onValueChanged = { newIncluded, newExcluded -> + filterCategoriesInclude.set(newIncluded.map { it.id.toString() }.toSet()) + filterCategoriesExclude.set(newExcluded.map { it.id.toString() }.toSet()) + showCategoriesDialog = false + }, + ) + } + + Row( + modifier = Modifier + .clickable(onClick = { libraryPreferences.filterCategories().toggle() }) + .fillMaxWidth() + .padding(horizontal = SettingsItemsPaddings.Horizontal), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(24.dp), + ) { + Checkbox( + checked = filterCategories, + onCheckedChange = null, + ) + Text( + text = stringResource(MR.strings.categories), + style = MaterialTheme.typography.bodyMedium, + ) + Spacer(Modifier.weight(1f)) + TextButton(onClick = { showCategoriesDialog = true }) { + Text(stringResource(MR.strings.action_edit)) + } + } +} +// KMK <-- diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt index bb923b3ac..157fbbabd 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt @@ -57,10 +57,12 @@ import exh.util.cancellable import exh.util.isLewd import exh.util.nullIfBlank import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.mutate import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList +import kotlinx.collections.immutable.toImmutableSet import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.asFlow @@ -87,6 +89,7 @@ import tachiyomi.core.common.util.lang.launchNonCancellable import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.domain.UnsortedPreferences import tachiyomi.domain.category.interactor.GetCategories +import tachiyomi.domain.category.interactor.GetCategoriesPerLibraryManga import tachiyomi.domain.category.interactor.SetMangaCategories import tachiyomi.domain.category.model.Category import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId @@ -159,6 +162,7 @@ class LibraryScreenModel( // SY <-- // KMK --> private val smartSearchMerge: SmartSearchMerge = Injekt.get(), + private val getCategoriesPerLibraryManga: GetCategoriesPerLibraryManga = Injekt.get(), // KMK <-- ) : StateScreenModel(State()) { @@ -171,12 +175,15 @@ class LibraryScreenModel( init { screenModelScope.launchIO { combine( - state.map { it.searchQuery }.distinctUntilChanged().debounce(SEARCH_DEBOUNCE_MILLIS), - getLibraryFlow(), - getTracksPerManga.subscribe(), combine( - getTrackingFilterFlow(), + state.map { it.searchQuery }.distinctUntilChanged().debounce(SEARCH_DEBOUNCE_MILLIS), + getLibraryFlow(), downloadCache.changes, + ::Triple, + ), + combine( + getTracksPerManga.subscribe(), + getTrackingFilterFlow(), ::Pair, ), // SY --> @@ -186,12 +193,25 @@ class LibraryScreenModel( ::Pair, ), // SY <-- - ) { searchQuery, library, tracks, (trackingFilter, _), (groupType, sort) -> + // KMK --> + combine( + getCategoriesPerLibraryManga.subscribe(), + state.map { it.filterCategory }.distinctUntilChanged(), + ::Pair, + ), + // KMK <-- + ) { (searchQuery, library, _), (tracks, trackingFilter), (groupType, sort), (categoriesPerManga, filterCategory) -> library // SY --> - .applyGrouping(groupType) + .applyGrouping(/* KMK --> */ if (filterCategory) LibraryGroup.UNGROUPED else /* KMK <-- */ groupType) // SY <-- - .applyFilters(tracks, trackingFilter) + .applyFilters( + tracks, + trackingFilter, + // KMK --> + categoriesPerManga, + // KMK <-- + ) .applySort( tracks, trackingFilter.keys, @@ -253,7 +273,10 @@ class LibraryScreenModel( prefs.filterLewd, // SY <-- ) + trackFilter.values - ).any { it != TriState.DISABLED } + ).any { it != TriState.DISABLED } || + // KMK --> + prefs.filterCategories + // KMK <-- } .distinctUntilChanged() .onEach { @@ -294,6 +317,25 @@ class LibraryScreenModel( } .launchIn(screenModelScope) // SY <-- + + // KMK --> + libraryPreferences.filterCategories().changes() + .onEach { + mutableState.update { state -> + state.copy(filterCategory = it) + } + }.launchIn(screenModelScope) + + screenModelScope.launchIO { + getCategories + .subscribe() + .collect { categories -> + mutableState.update { state -> + state.copy(libraryCategories = categories.filterNot(Category::isSystemCategory)) + } + } + } + // KMK <-- } /** @@ -302,6 +344,9 @@ class LibraryScreenModel( private suspend fun LibraryMap.applyFilters( trackMap: Map>, trackingFilter: Map, + // KMK --> + categoriesPerManga: Map>, + // KMK <-- ): LibraryMap { val prefs = getLibraryItemPreferencesFlow().first() val downloadedOnly = prefs.globalFilterDownloaded @@ -323,6 +368,12 @@ class LibraryScreenModel( val filterLewd = prefs.filterLewd // SY <-- + // KMK --> + val filterCategories = prefs.filterCategories + val includedCategories = prefs.filterCategoriesInclude + val excludedCategories = prefs.filterCategoriesExclude + // KMK <-- + val filterFnDownloaded: (LibraryItem) -> Boolean = { applyFilter(filterDownloaded) { it.libraryManga.manga.isLocal() || @@ -374,6 +425,19 @@ class LibraryScreenModel( !isExcluded && isIncluded } + // KMK --> + val filterFnCategories: (LibraryItem) -> Boolean = categories@{ item -> + if (!filterCategories) return@categories true + + val mangaCategories = categoriesPerManga[item.libraryManga.id].orEmpty() + + val isExcluded = excludedCategories.any { it in mangaCategories } + val isIncluded = includedCategories.isEmpty() || includedCategories.all { it in mangaCategories } + + !isExcluded && isIncluded + } + // KMK <-- + val filterFn: (LibraryItem) -> Boolean = { filterFnDownloaded(it) && filterFnUnread(it) && @@ -383,8 +447,11 @@ class LibraryScreenModel( filterFnIntervalCustom(it) && filterFnTracking(it) && // SY --> - filterFnLewd(it) - // SY <-- + filterFnLewd(it) && + // SY <-- + // KMK --> + filterFnCategories(it) + // KMK <--- } return mapValues { (_, value) -> value.fastFilter(filterFn) } @@ -524,6 +591,9 @@ class LibraryScreenModel( // KMK --> libraryPreferences.sourceBadge().changes(), libraryPreferences.useLangIcon().changes(), + libraryPreferences.filterCategories().changes(), + libraryPreferences.filterCategoriesInclude().changes(), + libraryPreferences.filterCategoriesExclude().changes(), // KMK <-- ) { ItemPreferences( @@ -545,6 +615,9 @@ class LibraryScreenModel( // KMK --> sourceBadge = it[13] as Boolean, useLangIcon = it[14] as Boolean, + filterCategories = it[15] as Boolean, + filterCategoriesInclude = (it[16] as Set<*>).filterIsInstance().mapNotNull(String::toLongOrNull).toImmutableSet(), + filterCategoriesExclude = (it[17] as Set<*>).filterIsInstance().mapNotNull(String::toLongOrNull).toImmutableSet(), // KMK <-- ) } @@ -1435,6 +1508,11 @@ class LibraryScreenModel( // SY --> val filterLewd: TriState, // SY <-- + // KMK --> + val filterCategories: Boolean, + val filterCategoriesInclude: ImmutableSet, + val filterCategoriesExclude: ImmutableSet, + // KMK <-- ) @Immutable @@ -1454,6 +1532,10 @@ class LibraryScreenModel( val ogCategories: List = emptyList(), val groupType: Int = LibraryGroup.BY_DEFAULT, // SY <-- + // KMK --> + val libraryCategories: List = emptyList(), + val filterCategory: Boolean = false, + // KMK <-- ) { private val libraryCount by lazy { library.values diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt index 367fb4edc..50fed4564 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt @@ -328,6 +328,9 @@ data object LibraryTab : Tab { // SY --> hasCategories = state.categories.fastAny { !it.isSystemCategory }, // SY <-- + // KMK --> + categories = state.libraryCategories, + // KMK <-- ) } is LibraryScreenModel.Dialog.ChangeCategory -> { diff --git a/app/src/main/java/mihon/core/migration/migrations/CategoryPreferencesCleanupMigration.kt b/app/src/main/java/mihon/core/migration/migrations/CategoryPreferencesCleanupMigration.kt index e528238f4..33d1b73fa 100644 --- a/app/src/main/java/mihon/core/migration/migrations/CategoryPreferencesCleanupMigration.kt +++ b/app/src/main/java/mihon/core/migration/migrations/CategoryPreferencesCleanupMigration.kt @@ -28,6 +28,10 @@ class CategoryPreferencesCleanupMigration : Migration { downloadPreferences.removeExcludeCategories(), downloadPreferences.downloadNewChapterCategories(), downloadPreferences.downloadNewChapterCategoriesExclude(), + // KMK --> + libraryPreferences.filterCategoriesInclude(), + libraryPreferences.filterCategoriesExclude(), + // KMK <-- ) categoryPreferences.forEach { preference -> val ids = preference.get() diff --git a/data/src/main/java/tachiyomi/data/category/CategoryRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/category/CategoryRepositoryImpl.kt index 4d3ed4df6..8a08acb0f 100644 --- a/data/src/main/java/tachiyomi/data/category/CategoryRepositoryImpl.kt +++ b/data/src/main/java/tachiyomi/data/category/CategoryRepositoryImpl.kt @@ -1,6 +1,7 @@ package tachiyomi.data.category import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map import tachiyomi.data.Database import tachiyomi.data.DatabaseHandler import tachiyomi.domain.category.model.Category @@ -35,6 +36,18 @@ class CategoryRepositoryImpl( } } + // KMK --> + override fun getCategoriesPerLibraryMangaAsFlow(): Flow>> { + return handler.subscribeToList { + mangas_categoriesQueries.getCategoriesPerLibraryManga { mangaId, categoryId -> + mangaId to categoryId + } + }.map { list -> + list.groupBy({ it.first }, { it.second }).mapValues { it.value.toSet() } + } + } + // KMK <-- + // SY --> override suspend fun insert(category: Category): Long { return handler.awaitOneExecutable(true) { diff --git a/data/src/main/sqldelight/tachiyomi/data/mangas_categories.sq b/data/src/main/sqldelight/tachiyomi/data/mangas_categories.sq index 3d8c815c9..848a921fa 100644 --- a/data/src/main/sqldelight/tachiyomi/data/mangas_categories.sq +++ b/data/src/main/sqldelight/tachiyomi/data/mangas_categories.sq @@ -21,4 +21,13 @@ VALUES (:mangaId, :categoryId); deleteMangaCategoryByMangaId: DELETE FROM mangas_categories -WHERE manga_id = :mangaId; \ No newline at end of file +WHERE manga_id = :mangaId; + +-- KMK --> +getCategoriesPerLibraryManga: +SELECT MC.manga_id, MC.category_id +FROM mangas_categories MC +JOIN mangas M +ON MC.manga_id = M._id +WHERE M.favorite = 1; +-- KMK <-- diff --git a/domain/src/main/java/tachiyomi/domain/category/interactor/DeleteCategory.kt b/domain/src/main/java/tachiyomi/domain/category/interactor/DeleteCategory.kt index 279b1c9af..c4636b1d6 100644 --- a/domain/src/main/java/tachiyomi/domain/category/interactor/DeleteCategory.kt +++ b/domain/src/main/java/tachiyomi/domain/category/interactor/DeleteCategory.kt @@ -41,6 +41,10 @@ class DeleteCategory( downloadPreferences.removeExcludeCategories(), downloadPreferences.downloadNewChapterCategories(), downloadPreferences.downloadNewChapterCategoriesExclude(), + // KMK --> + libraryPreferences.filterCategoriesInclude(), + libraryPreferences.filterCategoriesExclude(), + // KMK <-- ) val categoryIdString = categoryId.toString() categoryPreferences.forEach { preference -> diff --git a/domain/src/main/java/tachiyomi/domain/category/interactor/GetCategoriesPerLibraryManga.kt b/domain/src/main/java/tachiyomi/domain/category/interactor/GetCategoriesPerLibraryManga.kt new file mode 100644 index 000000000..dcab9defa --- /dev/null +++ b/domain/src/main/java/tachiyomi/domain/category/interactor/GetCategoriesPerLibraryManga.kt @@ -0,0 +1,13 @@ +package tachiyomi.domain.category.interactor + +import kotlinx.coroutines.flow.Flow +import tachiyomi.domain.category.repository.CategoryRepository + +class GetCategoriesPerLibraryManga( + private val categoryRepository: CategoryRepository, +) { + + fun subscribe(): Flow>> { + return categoryRepository.getCategoriesPerLibraryMangaAsFlow() + } +} diff --git a/domain/src/main/java/tachiyomi/domain/category/repository/CategoryRepository.kt b/domain/src/main/java/tachiyomi/domain/category/repository/CategoryRepository.kt index 8ef20517b..d389798c8 100644 --- a/domain/src/main/java/tachiyomi/domain/category/repository/CategoryRepository.kt +++ b/domain/src/main/java/tachiyomi/domain/category/repository/CategoryRepository.kt @@ -16,6 +16,10 @@ interface CategoryRepository { fun getCategoriesByMangaIdAsFlow(mangaId: Long): Flow> + // KMK --> + fun getCategoriesPerLibraryMangaAsFlow(): Flow>> + // KMK <-- + // SY --> suspend fun insert(category: Category): Long // SY <-- diff --git a/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt b/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt index f667642c0..0c9883bbf 100644 --- a/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt +++ b/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt @@ -116,6 +116,17 @@ class LibraryPreferences( ) // SY <-- + // KMK --> + fun filterCategories() = preferenceStore.getBoolean( + "pref_filter_library_categories", + false, + ) + + fun filterCategoriesInclude() = preferenceStore.getStringSet(FILTER_LIBRARY_CATEGORIES_INCLUDE_PREF_KEY, emptySet()) + + fun filterCategoriesExclude() = preferenceStore.getStringSet(FILTER_LIBRARY_CATEGORIES_EXCLUDE_PREF_KEY, emptySet()) + // KMK <-- + fun filterTracking(id: Int) = preferenceStore.getEnum( "pref_filter_library_tracked_${id}_v2", TriState.DISABLED, @@ -263,10 +274,20 @@ class LibraryPreferences( const val DEFAULT_CATEGORY_PREF_KEY = "default_category" private const val LIBRARY_UPDATE_CATEGORIES_PREF_KEY = "library_update_categories" private const val LIBRARY_UPDATE_CATEGORIES_EXCLUDE_PREF_KEY = "library_update_categories_exclude" + + // KMK --> + private const val FILTER_LIBRARY_CATEGORIES_INCLUDE_PREF_KEY = "pref_filter_library_categories_include" + private const val FILTER_LIBRARY_CATEGORIES_EXCLUDE_PREF_KEY = "pref_filter_library_categories_exclude" + // KMK <-- + val categoryPreferenceKeys = setOf( DEFAULT_CATEGORY_PREF_KEY, LIBRARY_UPDATE_CATEGORIES_PREF_KEY, LIBRARY_UPDATE_CATEGORIES_EXCLUDE_PREF_KEY, + // KMK --> + FILTER_LIBRARY_CATEGORIES_INCLUDE_PREF_KEY, + FILTER_LIBRARY_CATEGORIES_EXCLUDE_PREF_KEY, + // KMK <-- ) } } diff --git a/i18n-kmk/src/commonMain/moko-resources/base/strings.xml b/i18n-kmk/src/commonMain/moko-resources/base/strings.xml index 32780d675..48524c5e5 100644 --- a/i18n-kmk/src/commonMain/moko-resources/base/strings.xml +++ b/i18n-kmk/src/commonMain/moko-resources/base/strings.xml @@ -75,6 +75,7 @@ Show updating progress banner Source icon Use language icon + Entries in excluded categories will not be shown even if they are also in included categories. Refresh extension page to update list. More extensions...