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 <cuongtran.tm@gmail.com>
This commit is contained in:
parent
70fcc3dff0
commit
7f820b590c
12 changed files with 252 additions and 11 deletions
|
|
@ -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<MangaRepository> { MangaRepositoryImpl(get()) }
|
||||
|
|
|
|||
|
|
@ -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<Category>,
|
||||
// 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<Category>,
|
||||
) {
|
||||
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<Category>,
|
||||
) {
|
||||
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 <--
|
||||
|
|
|
|||
|
|
@ -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<LibraryScreenModel.State>(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<Long, List<Track>>,
|
||||
trackingFilter: Map<Long, TriState>,
|
||||
// KMK -->
|
||||
categoriesPerManga: Map<Long, Set<Long>>,
|
||||
// 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<String>().mapNotNull(String::toLongOrNull).toImmutableSet(),
|
||||
filterCategoriesExclude = (it[17] as Set<*>).filterIsInstance<String>().mapNotNull(String::toLongOrNull).toImmutableSet(),
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
|
|
@ -1435,6 +1508,11 @@ class LibraryScreenModel(
|
|||
// SY -->
|
||||
val filterLewd: TriState,
|
||||
// SY <--
|
||||
// KMK -->
|
||||
val filterCategories: Boolean,
|
||||
val filterCategoriesInclude: ImmutableSet<Long>,
|
||||
val filterCategoriesExclude: ImmutableSet<Long>,
|
||||
// KMK <--
|
||||
)
|
||||
|
||||
@Immutable
|
||||
|
|
@ -1454,6 +1532,10 @@ class LibraryScreenModel(
|
|||
val ogCategories: List<Category> = emptyList(),
|
||||
val groupType: Int = LibraryGroup.BY_DEFAULT,
|
||||
// SY <--
|
||||
// KMK -->
|
||||
val libraryCategories: List<Category> = emptyList(),
|
||||
val filterCategory: Boolean = false,
|
||||
// KMK <--
|
||||
) {
|
||||
private val libraryCount by lazy {
|
||||
library.values
|
||||
|
|
|
|||
|
|
@ -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 -> {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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<Map<Long, Set<Long>>> {
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -21,4 +21,13 @@ VALUES (:mangaId, :categoryId);
|
|||
|
||||
deleteMangaCategoryByMangaId:
|
||||
DELETE FROM mangas_categories
|
||||
WHERE manga_id = :mangaId;
|
||||
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 <--
|
||||
|
|
|
|||
|
|
@ -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 ->
|
||||
|
|
|
|||
|
|
@ -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<Map<Long, Set<Long>>> {
|
||||
return categoryRepository.getCategoriesPerLibraryMangaAsFlow()
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,10 @@ interface CategoryRepository {
|
|||
|
||||
fun getCategoriesByMangaIdAsFlow(mangaId: Long): Flow<List<Category>>
|
||||
|
||||
// KMK -->
|
||||
fun getCategoriesPerLibraryMangaAsFlow(): Flow<Map<Long, Set<Long>>>
|
||||
// KMK <--
|
||||
|
||||
// SY -->
|
||||
suspend fun insert(category: Category): Long
|
||||
// SY <--
|
||||
|
|
|
|||
|
|
@ -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 <--
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@
|
|||
<string name="pref_show_updating_progress_banner">Show updating progress banner</string>
|
||||
<string name="action_display_source_badge">Source icon</string>
|
||||
<string name="action_display_language_icon">Use language icon</string>
|
||||
<string name="pref_library_filter_categories_details">Entries in excluded categories will not be shown even if they are also in included categories.</string>
|
||||
<!-- Extension section -->
|
||||
<string name="extensions_page_need_refresh">Refresh extension page to update list.</string>
|
||||
<string name="extensions_page_more">More extensions...</string>
|
||||
|
|
|
|||
Loading…
Reference in a new issue