From b536adb8ec4274bfee7c32eb8bb628e631a92f0e Mon Sep 17 00:00:00 2001 From: Jeffery Orazulike Date: Fri, 15 Sep 2023 19:24:08 +0100 Subject: [PATCH] feat(category): Add ability to hide categories from library --- .../java/eu/kanade/domain/DomainModule.kt | 8 + .../category/AnimeCategoryScreen.kt | 2 + .../category/MangaCategoryScreen.kt | 2 + .../category/components/CategoryContent.kt | 2 + .../category/components/CategoryListItem.kt | 26 ++- .../settings/screen/SettingsLibraryScreen.kt | 42 +++-- .../data/backup/models/BackupCategory.kt | 1 + .../anime/AnimeCategoryScreenModel.kt | 41 ++++- .../ui/category/anime/AnimeCategoryTab.kt | 1 + .../manga/MangaCategoryScreenModel.kt | 41 ++++- .../ui/category/manga/MangaCategoryTab.kt | 1 + .../library/anime/AnimeLibraryScreenModel.kt | 4 +- .../library/manga/MangaLibraryScreenModel.kt | 4 +- .../tachiyomi/data/category/CategoryMapper.kt | 18 +- .../anime/AnimeCategoryRepositoryImpl.kt | 21 +++ .../manga/MangaCategoryRepositoryImpl.kt | 21 +++ data/src/main/sqldelight/data/categories.sq | 35 +++- data/src/main/sqldelight/migrations/25.sqm | 1 + .../sqldelightanime/dataanime/categories.sq | 35 +++- .../main/sqldelightanime/migrations/125.sqm | 1 + .../interactor/CreateAnimeCategoryWithName.kt | 1 + .../interactor/GetVisibleAnimeCategories.kt | 25 +++ .../anime/interactor/HideAnimeCategory.kt | 33 ++++ .../repository/AnimeCategoryRepository.kt | 8 + .../interactor/CreateMangaCategoryWithName.kt | 1 + .../manga/interactor/GetMangaCategories.kt | 1 - .../interactor/GetVisibleMangaCategories.kt | 25 +++ .../manga/interactor/HideMangaCategory.kt | 33 ++++ .../repository/MangaCategoryRepository.kt | 8 + .../domain/category/model/Category.kt | 1 + .../domain/category/model/CategoryUpdate.kt | 1 + .../library/service/LibraryPreferences.kt | 158 +++++++++++++----- i18n/src/main/res/values/strings-aniyomi.xml | 6 + i18n/src/main/res/values/strings.xml | 20 +-- 34 files changed, 529 insertions(+), 99 deletions(-) create mode 100644 data/src/main/sqldelight/migrations/25.sqm create mode 100644 data/src/main/sqldelightanime/migrations/125.sqm create mode 100644 domain/src/main/java/tachiyomi/domain/category/anime/interactor/GetVisibleAnimeCategories.kt create mode 100644 domain/src/main/java/tachiyomi/domain/category/anime/interactor/HideAnimeCategory.kt create mode 100644 domain/src/main/java/tachiyomi/domain/category/manga/interactor/GetVisibleMangaCategories.kt create mode 100644 domain/src/main/java/tachiyomi/domain/category/manga/interactor/HideMangaCategory.kt diff --git a/app/src/main/java/eu/kanade/domain/DomainModule.kt b/app/src/main/java/eu/kanade/domain/DomainModule.kt index 03c28cd0ca..9aa9ce8673 100644 --- a/app/src/main/java/eu/kanade/domain/DomainModule.kt +++ b/app/src/main/java/eu/kanade/domain/DomainModule.kt @@ -49,6 +49,8 @@ import tachiyomi.data.updates.manga.MangaUpdatesRepositoryImpl import tachiyomi.domain.category.anime.interactor.CreateAnimeCategoryWithName import tachiyomi.domain.category.anime.interactor.DeleteAnimeCategory import tachiyomi.domain.category.anime.interactor.GetAnimeCategories +import tachiyomi.domain.category.anime.interactor.GetVisibleAnimeCategories +import tachiyomi.domain.category.anime.interactor.HideAnimeCategory import tachiyomi.domain.category.anime.interactor.RenameAnimeCategory import tachiyomi.domain.category.anime.interactor.ReorderAnimeCategory import tachiyomi.domain.category.anime.interactor.ResetAnimeCategoryFlags @@ -60,6 +62,8 @@ import tachiyomi.domain.category.anime.repository.AnimeCategoryRepository import tachiyomi.domain.category.manga.interactor.CreateMangaCategoryWithName import tachiyomi.domain.category.manga.interactor.DeleteMangaCategory import tachiyomi.domain.category.manga.interactor.GetMangaCategories +import tachiyomi.domain.category.manga.interactor.GetVisibleMangaCategories +import tachiyomi.domain.category.manga.interactor.HideMangaCategory import tachiyomi.domain.category.manga.interactor.RenameMangaCategory import tachiyomi.domain.category.manga.interactor.ReorderMangaCategory import tachiyomi.domain.category.manga.interactor.ResetMangaCategoryFlags @@ -142,6 +146,7 @@ class DomainModule : InjektModule { override fun InjektRegistrar.registerInjectables() { addSingletonFactory { AnimeCategoryRepositoryImpl(get()) } addFactory { GetAnimeCategories(get()) } + addFactory { GetVisibleAnimeCategories(get()) } addFactory { ResetAnimeCategoryFlags(get(), get()) } addFactory { SetDisplayModeForAnimeCategory(get(), get()) } addFactory { SetSortModeForAnimeCategory(get(), get()) } @@ -149,10 +154,12 @@ class DomainModule : InjektModule { addFactory { RenameAnimeCategory(get()) } addFactory { ReorderAnimeCategory(get()) } addFactory { UpdateAnimeCategory(get()) } + addFactory { HideAnimeCategory(get()) } addFactory { DeleteAnimeCategory(get()) } addSingletonFactory { MangaCategoryRepositoryImpl(get()) } addFactory { GetMangaCategories(get()) } + addFactory { GetVisibleMangaCategories(get()) } addFactory { ResetMangaCategoryFlags(get(), get()) } addFactory { SetDisplayModeForMangaCategory(get(), get()) } addFactory { SetSortModeForMangaCategory(get(), get()) } @@ -160,6 +167,7 @@ class DomainModule : InjektModule { addFactory { RenameMangaCategory(get()) } addFactory { ReorderMangaCategory(get()) } addFactory { UpdateMangaCategory(get()) } + addFactory { HideMangaCategory(get()) } addFactory { DeleteMangaCategory(get()) } addSingletonFactory { AnimeRepositoryImpl(get()) } diff --git a/app/src/main/java/eu/kanade/presentation/category/AnimeCategoryScreen.kt b/app/src/main/java/eu/kanade/presentation/category/AnimeCategoryScreen.kt index 4d3a31218d..c92e0ac57a 100644 --- a/app/src/main/java/eu/kanade/presentation/category/AnimeCategoryScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/category/AnimeCategoryScreen.kt @@ -23,6 +23,7 @@ fun AnimeCategoryScreen( contentPadding: PaddingValues, onClickCreate: () -> Unit, onClickRename: (Category) -> Unit, + onClickHide: (Category) -> Unit, onClickDelete: (Category) -> Unit, onClickMoveUp: (Category) -> Unit, onClickMoveDown: (Category) -> Unit, @@ -49,6 +50,7 @@ fun AnimeCategoryScreen( lazyListState = lazyListState, paddingValues = contentPadding + topSmallPaddingValues + PaddingValues(horizontal = MaterialTheme.padding.medium), onClickRename = onClickRename, + onClickHide = onClickHide, onClickDelete = onClickDelete, onMoveUp = onClickMoveUp, onMoveDown = onClickMoveDown, diff --git a/app/src/main/java/eu/kanade/presentation/category/MangaCategoryScreen.kt b/app/src/main/java/eu/kanade/presentation/category/MangaCategoryScreen.kt index 570fdc1639..3945d7198f 100644 --- a/app/src/main/java/eu/kanade/presentation/category/MangaCategoryScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/category/MangaCategoryScreen.kt @@ -23,6 +23,7 @@ fun MangaCategoryScreen( contentPadding: PaddingValues, onClickCreate: () -> Unit, onClickRename: (Category) -> Unit, + onClickHide: (Category) -> Unit, onClickDelete: (Category) -> Unit, onClickMoveUp: (Category) -> Unit, onClickMoveDown: (Category) -> Unit, @@ -49,6 +50,7 @@ fun MangaCategoryScreen( lazyListState = lazyListState, paddingValues = contentPadding + topSmallPaddingValues + PaddingValues(horizontal = MaterialTheme.padding.medium), onClickRename = onClickRename, + onClickHide = onClickHide, onClickDelete = onClickDelete, onMoveUp = onClickMoveUp, onMoveDown = onClickMoveDown, diff --git a/app/src/main/java/eu/kanade/presentation/category/components/CategoryContent.kt b/app/src/main/java/eu/kanade/presentation/category/components/CategoryContent.kt index b8a72c9ada..71fc2bc8d3 100644 --- a/app/src/main/java/eu/kanade/presentation/category/components/CategoryContent.kt +++ b/app/src/main/java/eu/kanade/presentation/category/components/CategoryContent.kt @@ -17,6 +17,7 @@ fun CategoryContent( lazyListState: LazyListState, paddingValues: PaddingValues, onClickRename: (Category) -> Unit, + onClickHide: (Category) -> Unit, onClickDelete: (Category) -> Unit, onMoveUp: (Category) -> Unit, onMoveDown: (Category) -> Unit, @@ -38,6 +39,7 @@ fun CategoryContent( onMoveUp = onMoveUp, onMoveDown = onMoveDown, onRename = { onClickRename(category) }, + onHide = { onClickHide(category) }, onDelete = { onClickDelete(category) }, ) } diff --git a/app/src/main/java/eu/kanade/presentation/category/components/CategoryListItem.kt b/app/src/main/java/eu/kanade/presentation/category/components/CategoryListItem.kt index 565ea7b355..bbacc91414 100644 --- a/app/src/main/java/eu/kanade/presentation/category/components/CategoryListItem.kt +++ b/app/src/main/java/eu/kanade/presentation/category/components/CategoryListItem.kt @@ -11,6 +11,8 @@ import androidx.compose.material.icons.outlined.ArrowDropUp import androidx.compose.material.icons.outlined.Delete import androidx.compose.material.icons.outlined.Edit import androidx.compose.material.icons.outlined.Label +import androidx.compose.material.icons.outlined.Visibility +import androidx.compose.material.icons.outlined.VisibilityOff import androidx.compose.material3.ElevatedCard import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -33,6 +35,7 @@ fun CategoryListItem( onMoveUp: (Category) -> Unit, onMoveDown: (Category) -> Unit, onRename: () -> Unit, + onHide: () -> Unit, onDelete: () -> Unit, ) { ElevatedCard( @@ -71,10 +74,29 @@ fun CategoryListItem( } Spacer(modifier = Modifier.weight(1f)) IconButton(onClick = onRename) { - Icon(imageVector = Icons.Outlined.Edit, contentDescription = stringResource(R.string.action_rename_category)) + Icon( + imageVector = Icons.Outlined.Edit, + contentDescription = stringResource(R.string.action_rename_category), + ) } + IconButton( + onClick = onHide, + content = { + Icon( + imageVector = if (category.hidden) { + Icons.Outlined.Visibility + } else { + Icons.Outlined.VisibilityOff + }, + contentDescription = stringResource(R.string.action_hide), + ) + }, + ) IconButton(onClick = onDelete) { - Icon(imageVector = Icons.Outlined.Delete, contentDescription = stringResource(R.string.action_delete)) + Icon( + imageVector = Icons.Outlined.Delete, + contentDescription = stringResource(R.string.action_delete), + ) } } } diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt index a80694da6c..3f62bfb523 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt @@ -54,13 +54,20 @@ object SettingsLibraryScreen : SearchableSettings { @Composable override fun getPreferences(): List { val getCategories = remember { Injekt.get() } - val allCategories by getCategories.subscribe().collectAsState(initial = runBlocking { getCategories.await() }) + val allCategories by getCategories.subscribe() + .collectAsState(initial = runBlocking { getCategories.await() }) val getAnimeCategories = remember { Injekt.get() } - val allAnimeCategories by getAnimeCategories.subscribe().collectAsState(initial = runBlocking { getAnimeCategories.await() }) + val allAnimeCategories by getAnimeCategories.subscribe() + .collectAsState(initial = runBlocking { getAnimeCategories.await() }) val libraryPreferences = remember { Injekt.get() } return listOf( - getCategoriesGroup(LocalNavigator.currentOrThrow, allCategories, allAnimeCategories, libraryPreferences), + getCategoriesGroup( + LocalNavigator.currentOrThrow, + allCategories, + allAnimeCategories, + libraryPreferences, + ), getGlobalUpdateGroup(allCategories, allAnimeCategories, libraryPreferences), getChapterSwipeActionsGroup(libraryPreferences), getEpisodeSwipeActionsGroup(libraryPreferences), @@ -82,7 +89,8 @@ object SettingsLibraryScreen : SearchableSettings { val defaultCategory by libraryPreferences.defaultMangaCategory().collectAsState() val selectedCategory = allCategories.find { it.id == defaultCategory.toLong() } val defaultAnimeCategory by libraryPreferences.defaultAnimeCategory().collectAsState() - val selectedAnimeCategory = allAnimeCategories.find { it.id == defaultAnimeCategory.toLong() } + val selectedAnimeCategory = + allAnimeCategories.find { it.id == defaultAnimeCategory.toLong() } // For default category val mangaIds = listOf(libraryPreferences.defaultMangaCategory().defaultValue()) + @@ -110,7 +118,8 @@ object SettingsLibraryScreen : SearchableSettings { Preference.PreferenceItem.ListPreference( pref = libraryPreferences.defaultAnimeCategory(), title = stringResource(R.string.default_anime_category), - subtitle = selectedAnimeCategory?.visualName ?: stringResource(R.string.default_category_summary), + subtitle = selectedAnimeCategory?.visualName + ?: stringResource(R.string.default_category_summary), entries = animeIds.zip(animeLabels).toMap(), ), Preference.PreferenceItem.TextPreference( @@ -125,7 +134,8 @@ object SettingsLibraryScreen : SearchableSettings { Preference.PreferenceItem.ListPreference( pref = libraryPreferences.defaultMangaCategory(), title = stringResource(R.string.default_manga_category), - subtitle = selectedCategory?.visualName ?: stringResource(R.string.default_category_summary), + subtitle = selectedCategory?.visualName + ?: stringResource(R.string.default_category_summary), entries = mangaIds.zip(mangaLabels).toMap(), ), Preference.PreferenceItem.SwitchPreference( @@ -140,6 +150,10 @@ object SettingsLibraryScreen : SearchableSettings { true }, ), + Preference.PreferenceItem.SwitchPreference( + pref = libraryPreferences.hideHiddenCategoriesSettings(), + title = stringResource(R.string.pref_category_hide_hidden), + ), ), ) } @@ -158,7 +172,8 @@ object SettingsLibraryScreen : SearchableSettings { val libraryUpdateMangaRestrictionPref = libraryPreferences.libraryUpdateItemRestriction() val animelibUpdateCategoriesPref = libraryPreferences.animeLibraryUpdateCategories() - val animelibUpdateCategoriesExcludePref = libraryPreferences.animeLibraryUpdateCategoriesExclude() + val animelibUpdateCategoriesExcludePref = + libraryPreferences.animeLibraryUpdateCategoriesExclude() val includedAnime by animelibUpdateCategoriesPref.collectAsState() val excludedAnime by animelibUpdateCategoriesExcludePref.collectAsState() @@ -174,14 +189,18 @@ object SettingsLibraryScreen : SearchableSettings { onDismissRequest = { showAnimeDialog = false }, onValueChanged = { newIncluded, newExcluded -> animelibUpdateCategoriesPref.set(newIncluded.map { it.id.toString() }.toSet()) - animelibUpdateCategoriesExcludePref.set(newExcluded.map { it.id.toString() }.toSet()) + animelibUpdateCategoriesExcludePref.set( + newExcluded.map { it.id.toString() } + .toSet(), + ) showAnimeDialog = false }, ) } val libraryUpdateCategoriesPref = libraryPreferences.mangaLibraryUpdateCategories() - val libraryUpdateCategoriesExcludePref = libraryPreferences.mangaLibraryUpdateCategoriesExclude() + val libraryUpdateCategoriesExcludePref = + libraryPreferences.mangaLibraryUpdateCategoriesExclude() val includedManga by libraryUpdateCategoriesPref.collectAsState() val excludedManga by libraryUpdateCategoriesExcludePref.collectAsState() @@ -197,7 +216,10 @@ object SettingsLibraryScreen : SearchableSettings { onDismissRequest = { showMangaDialog = false }, onValueChanged = { newIncluded, newExcluded -> libraryUpdateCategoriesPref.set(newIncluded.map { it.id.toString() }.toSet()) - libraryUpdateCategoriesExcludePref.set(newExcluded.map { it.id.toString() }.toSet()) + libraryUpdateCategoriesExcludePref.set( + newExcluded.map { it.id.toString() } + .toSet(), + ) showMangaDialog = false }, ) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupCategory.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupCategory.kt index 8d93a0b32d..7b143061da 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupCategory.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupCategory.kt @@ -18,6 +18,7 @@ class BackupCategory( name = this@BackupCategory.name, flags = this@BackupCategory.flags, order = this@BackupCategory.order, + hidden = false, ) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/category/anime/AnimeCategoryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/category/anime/AnimeCategoryScreenModel.kt index af20ad16da..0c2c3640ef 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/category/anime/AnimeCategoryScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/category/anime/AnimeCategoryScreenModel.kt @@ -13,18 +13,24 @@ import kotlinx.coroutines.launch import tachiyomi.domain.category.anime.interactor.CreateAnimeCategoryWithName import tachiyomi.domain.category.anime.interactor.DeleteAnimeCategory import tachiyomi.domain.category.anime.interactor.GetAnimeCategories +import tachiyomi.domain.category.anime.interactor.GetVisibleAnimeCategories +import tachiyomi.domain.category.anime.interactor.HideAnimeCategory import tachiyomi.domain.category.anime.interactor.RenameAnimeCategory import tachiyomi.domain.category.anime.interactor.ReorderAnimeCategory import tachiyomi.domain.category.model.Category +import tachiyomi.domain.library.service.LibraryPreferences import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get class AnimeCategoryScreenModel( - private val getCategories: GetAnimeCategories = Injekt.get(), + private val getAllCategories: GetAnimeCategories = Injekt.get(), + private val getVisibleCategories: GetVisibleAnimeCategories = Injekt.get(), private val createCategoryWithName: CreateAnimeCategoryWithName = Injekt.get(), + private val hideCategory: HideAnimeCategory = Injekt.get(), private val deleteCategory: DeleteAnimeCategory = Injekt.get(), private val reorderCategory: ReorderAnimeCategory = Injekt.get(), private val renameCategory: RenameAnimeCategory = Injekt.get(), + private val libraryPreferences: LibraryPreferences = Injekt.get(), ) : StateScreenModel(AnimeCategoryScreenState.Loading) { private val _events: Channel = Channel() @@ -32,21 +38,38 @@ class AnimeCategoryScreenModel( init { coroutineScope.launch { - getCategories.subscribe() - .collectLatest { categories -> - mutableState.update { - AnimeCategoryScreenState.Success( - categories = categories.filterNot(Category::isSystemCategory), - ) - } + val allCategories = if (libraryPreferences.hideHiddenCategoriesSettings().get()) { + getVisibleCategories.subscribe() + } else { + getAllCategories.subscribe() + } + + allCategories.collectLatest { categories -> + mutableState.update { + AnimeCategoryScreenState.Success( + categories = categories.filterNot(Category::isSystemCategory), + ) } + } } } fun createCategory(name: String) { coroutineScope.launch { when (createCategoryWithName.await(name)) { - is CreateAnimeCategoryWithName.Result.InternalError -> _events.send(AnimeCategoryEvent.InternalError) + is CreateAnimeCategoryWithName.Result.InternalError -> _events.send( + AnimeCategoryEvent.InternalError, + ) + + else -> {} + } + } + } + + fun hideCategory(category: Category) { + coroutineScope.launch { + when (hideCategory.await(category)) { + is HideAnimeCategory.Result.InternalError -> _events.send(AnimeCategoryEvent.InternalError) else -> {} } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/category/anime/AnimeCategoryTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/category/anime/AnimeCategoryTab.kt index 842049846b..03b8ef7120 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/category/anime/AnimeCategoryTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/category/anime/AnimeCategoryTab.kt @@ -36,6 +36,7 @@ fun Screen.animeCategoryTab(): TabContent { contentPadding = contentPadding, onClickCreate = { screenModel.showDialog(AnimeCategoryDialog.Create) }, onClickRename = { screenModel.showDialog(AnimeCategoryDialog.Rename(it)) }, + onClickHide = screenModel::hideCategory, onClickDelete = { screenModel.showDialog(AnimeCategoryDialog.Delete(it)) }, onClickMoveUp = screenModel::moveUp, onClickMoveDown = screenModel::moveDown, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/category/manga/MangaCategoryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/category/manga/MangaCategoryScreenModel.kt index 2f6602ee86..8991d53fbe 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/category/manga/MangaCategoryScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/category/manga/MangaCategoryScreenModel.kt @@ -13,18 +13,24 @@ import kotlinx.coroutines.launch import tachiyomi.domain.category.manga.interactor.CreateMangaCategoryWithName import tachiyomi.domain.category.manga.interactor.DeleteMangaCategory import tachiyomi.domain.category.manga.interactor.GetMangaCategories +import tachiyomi.domain.category.manga.interactor.GetVisibleMangaCategories +import tachiyomi.domain.category.manga.interactor.HideMangaCategory import tachiyomi.domain.category.manga.interactor.RenameMangaCategory import tachiyomi.domain.category.manga.interactor.ReorderMangaCategory import tachiyomi.domain.category.model.Category +import tachiyomi.domain.library.service.LibraryPreferences import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get class MangaCategoryScreenModel( - private val getCategories: GetMangaCategories = Injekt.get(), + private val getAllCategories: GetMangaCategories = Injekt.get(), + private val getVisibleCategories: GetVisibleMangaCategories = Injekt.get(), private val createCategoryWithName: CreateMangaCategoryWithName = Injekt.get(), + private val hideCategory: HideMangaCategory = Injekt.get(), private val deleteCategory: DeleteMangaCategory = Injekt.get(), private val reorderCategory: ReorderMangaCategory = Injekt.get(), private val renameCategory: RenameMangaCategory = Injekt.get(), + private val libraryPreferences: LibraryPreferences = Injekt.get(), ) : StateScreenModel(MangaCategoryScreenState.Loading) { private val _events: Channel = Channel() @@ -32,21 +38,38 @@ class MangaCategoryScreenModel( init { coroutineScope.launch { - getCategories.subscribe() - .collectLatest { categories -> - mutableState.update { - MangaCategoryScreenState.Success( - categories = categories.filterNot(Category::isSystemCategory), - ) - } + val allCategories = if (libraryPreferences.hideHiddenCategoriesSettings().get()) { + getVisibleCategories.subscribe() + } else { + getAllCategories.subscribe() + } + + allCategories.collectLatest { categories -> + mutableState.update { + MangaCategoryScreenState.Success( + categories = categories.filterNot(Category::isSystemCategory), + ) } + } } } fun createCategory(name: String) { coroutineScope.launch { when (createCategoryWithName.await(name)) { - is CreateMangaCategoryWithName.Result.InternalError -> _events.send(MangaCategoryEvent.InternalError) + is CreateMangaCategoryWithName.Result.InternalError -> _events.send( + MangaCategoryEvent.InternalError, + ) + + else -> {} + } + } + } + + fun hideCategory(category: Category) { + coroutineScope.launch { + when (hideCategory.await(category)) { + is HideMangaCategory.Result.InternalError -> _events.send(MangaCategoryEvent.InternalError) else -> {} } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/category/manga/MangaCategoryTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/category/manga/MangaCategoryTab.kt index bc039dab69..1f88ebdc2d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/category/manga/MangaCategoryTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/category/manga/MangaCategoryTab.kt @@ -37,6 +37,7 @@ fun Screen.mangaCategoryTab(): TabContent { contentPadding = contentPadding, onClickCreate = { screenModel.showDialog(MangaCategoryDialog.Create) }, onClickRename = { screenModel.showDialog(MangaCategoryDialog.Rename(it)) }, + onClickHide = screenModel::hideCategory, onClickDelete = { screenModel.showDialog(MangaCategoryDialog.Delete(it)) }, onClickMoveUp = screenModel::moveUp, onClickMoveDown = screenModel::moveDown, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/anime/AnimeLibraryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/anime/AnimeLibraryScreenModel.kt index 59a31471ce..aa15792dc8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/anime/AnimeLibraryScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/anime/AnimeLibraryScreenModel.kt @@ -44,7 +44,7 @@ import tachiyomi.core.preference.CheckboxState import tachiyomi.core.util.lang.launchIO import tachiyomi.core.util.lang.launchNonCancellable import tachiyomi.core.util.lang.withIOContext -import tachiyomi.domain.category.anime.interactor.GetAnimeCategories +import tachiyomi.domain.category.anime.interactor.GetVisibleAnimeCategories import tachiyomi.domain.category.anime.interactor.SetAnimeCategories import tachiyomi.domain.category.model.Category import tachiyomi.domain.entries.TriStateFilter @@ -75,7 +75,7 @@ typealias AnimeLibraryMap = Map> class AnimeLibraryScreenModel( private val getLibraryAnime: GetLibraryAnime = Injekt.get(), - private val getCategories: GetAnimeCategories = Injekt.get(), + private val getCategories: GetVisibleAnimeCategories = Injekt.get(), private val getTracksPerAnime: GetTracksPerAnime = Injekt.get(), private val getNextEpisodes: GetNextEpisodes = Injekt.get(), private val getEpisodesByAnimeId: GetEpisodeByAnimeId = Injekt.get(), diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibraryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibraryScreenModel.kt index a606ea06ee..b0d3f57a44 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibraryScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibraryScreenModel.kt @@ -44,7 +44,7 @@ import tachiyomi.core.preference.CheckboxState import tachiyomi.core.util.lang.launchIO import tachiyomi.core.util.lang.launchNonCancellable import tachiyomi.core.util.lang.withIOContext -import tachiyomi.domain.category.manga.interactor.GetMangaCategories +import tachiyomi.domain.category.manga.interactor.GetVisibleMangaCategories import tachiyomi.domain.category.manga.interactor.SetMangaCategories import tachiyomi.domain.category.model.Category import tachiyomi.domain.entries.TriStateFilter @@ -75,7 +75,7 @@ typealias MangaLibraryMap = Map> class MangaLibraryScreenModel( private val getLibraryManga: GetLibraryManga = Injekt.get(), - private val getCategories: GetMangaCategories = Injekt.get(), + private val getCategories: GetVisibleMangaCategories = Injekt.get(), private val getTracksPerManga: GetTracksPerManga = Injekt.get(), private val getNextChapters: GetNextChapters = Injekt.get(), private val getChaptersByMangaId: GetChapterByMangaId = Injekt.get(), diff --git a/data/src/main/java/tachiyomi/data/category/CategoryMapper.kt b/data/src/main/java/tachiyomi/data/category/CategoryMapper.kt index 37f18ae808..2c815e4a09 100644 --- a/data/src/main/java/tachiyomi/data/category/CategoryMapper.kt +++ b/data/src/main/java/tachiyomi/data/category/CategoryMapper.kt @@ -2,11 +2,13 @@ package tachiyomi.data.category import tachiyomi.domain.category.model.Category -val categoryMapper: (Long, String, Long, Long) -> Category = { id, name, order, flags -> - Category( - id = id, - name = name, - order = order, - flags = flags, - ) -} +val categoryMapper: (Long, String, Long, Long, Long) -> Category = + { id, name, order, flags, hidden -> + Category( + id = id, + name = name, + order = order, + flags = flags, + hidden = hidden == 1L, + ) + } diff --git a/data/src/main/java/tachiyomi/data/category/anime/AnimeCategoryRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/category/anime/AnimeCategoryRepositoryImpl.kt index 0c1e97f248..9362fc63fe 100644 --- a/data/src/main/java/tachiyomi/data/category/anime/AnimeCategoryRepositoryImpl.kt +++ b/data/src/main/java/tachiyomi/data/category/anime/AnimeCategoryRepositoryImpl.kt @@ -20,22 +20,42 @@ class AnimeCategoryRepositoryImpl( return handler.awaitList { categoriesQueries.getCategories(categoryMapper) } } + override suspend fun getAllVisibleAnimeCategories(): List { + return handler.awaitList { categoriesQueries.getVisibleCategories(categoryMapper) } + } + override fun getAllAnimeCategoriesAsFlow(): Flow> { return handler.subscribeToList { categoriesQueries.getCategories(categoryMapper) } } + override fun getAllVisibleAnimeCategoriesAsFlow(): Flow> { + return handler.subscribeToList { categoriesQueries.getVisibleCategories(categoryMapper) } + } + override suspend fun getCategoriesByAnimeId(animeId: Long): List { return handler.awaitList { categoriesQueries.getCategoriesByAnimeId(animeId, categoryMapper) } } + override suspend fun getVisibleCategoriesByAnimeId(animeId: Long): List { + return handler.awaitList { + categoriesQueries.getVisibleCategoriesByAnimeId(animeId, categoryMapper) + } + } + override fun getCategoriesByAnimeIdAsFlow(animeId: Long): Flow> { return handler.subscribeToList { categoriesQueries.getCategoriesByAnimeId(animeId, categoryMapper) } } + override fun getVisibleCategoriesByAnimeIdAsFlow(animeId: Long): Flow> { + return handler.subscribeToList { + categoriesQueries.getVisibleCategoriesByAnimeId(animeId, categoryMapper) + } + } + override suspend fun insertAnimeCategory(category: Category) { handler.await { categoriesQueries.insert( @@ -65,6 +85,7 @@ class AnimeCategoryRepositoryImpl( name = update.name, order = update.order, flags = update.flags, + hidden = if (update.hidden == true) 1L else 0L, categoryId = update.id, ) } diff --git a/data/src/main/java/tachiyomi/data/category/manga/MangaCategoryRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/category/manga/MangaCategoryRepositoryImpl.kt index 116dd956f2..27cbf7ba59 100644 --- a/data/src/main/java/tachiyomi/data/category/manga/MangaCategoryRepositoryImpl.kt +++ b/data/src/main/java/tachiyomi/data/category/manga/MangaCategoryRepositoryImpl.kt @@ -20,22 +20,42 @@ class MangaCategoryRepositoryImpl( return handler.awaitList { categoriesQueries.getCategories(categoryMapper) } } + override suspend fun getAllVisibleMangaCategories(): List { + return handler.awaitList { categoriesQueries.getVisibleCategories(categoryMapper) } + } + override fun getAllMangaCategoriesAsFlow(): Flow> { return handler.subscribeToList { categoriesQueries.getCategories(categoryMapper) } } + override fun getAllVisibleMangaCategoriesAsFlow(): Flow> { + return handler.subscribeToList { categoriesQueries.getVisibleCategories(categoryMapper) } + } + override suspend fun getCategoriesByMangaId(mangaId: Long): List { return handler.awaitList { categoriesQueries.getCategoriesByMangaId(mangaId, categoryMapper) } } + override suspend fun getVisibleCategoriesByMangaId(mangaId: Long): List { + return handler.awaitList { + categoriesQueries.getVisibleCategoriesByMangaId(mangaId, categoryMapper) + } + } + override fun getCategoriesByMangaIdAsFlow(mangaId: Long): Flow> { return handler.subscribeToList { categoriesQueries.getCategoriesByMangaId(mangaId, categoryMapper) } } + override fun getVisibleCategoriesByMangaIdAsFlow(mangaId: Long): Flow> { + return handler.subscribeToList { + categoriesQueries.getVisibleCategoriesByMangaId(mangaId, categoryMapper) + } + } + override suspend fun insertMangaCategory(category: Category) { handler.await { categoriesQueries.insert( @@ -65,6 +85,7 @@ class MangaCategoryRepositoryImpl( name = update.name, order = update.order, flags = update.flags, + hidden = if (update.hidden == true) 1L else 0L, categoryId = update.id, ) } diff --git a/data/src/main/sqldelight/data/categories.sq b/data/src/main/sqldelight/data/categories.sq index a25e6ca3ba..1f55cc4e55 100644 --- a/data/src/main/sqldelight/data/categories.sq +++ b/data/src/main/sqldelight/data/categories.sq @@ -2,7 +2,8 @@ CREATE TABLE categories( _id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL, sort INTEGER NOT NULL, - flags INTEGER NOT NULL + flags INTEGER NOT NULL, + hidden INTEGER NOT NULL DEFAULT 0 ); -- Insert system category @@ -27,21 +28,46 @@ SELECT _id AS id, name, sort AS `order`, -flags +flags, +hidden FROM categories ORDER BY sort; +getVisibleCategories: +SELECT +_id AS id, +name, +sort AS `order`, +flags, +hidden +FROM categories +WHERE hidden = 0 +ORDER BY sort; + getCategoriesByMangaId: SELECT C._id AS id, C.name, C.sort AS `order`, -C.flags +C.flags, +C.hidden FROM categories C JOIN mangas_categories MC ON C._id = MC.category_id WHERE MC.manga_id = :mangaId; +getVisibleCategoriesByMangaId: +SELECT +C._id AS id, +C.name, +C.sort AS `order`, +C.flags, +C.hidden +FROM categories C +JOIN mangas_categories MC +ON C._id = MC.category_id +WHERE MC.manga_id = :mangaId AND C.hidden = 0; + insert: INSERT INTO categories(name, sort, flags) VALUES (:name, :order, :flags); @@ -54,7 +80,8 @@ update: UPDATE categories SET name = coalesce(:name, name), sort = coalesce(:order, sort), - flags = coalesce(:flags, flags) + flags = coalesce(:flags, flags), + hidden = coalesce(:hidden, hidden) WHERE _id = :categoryId; updateAllFlags: diff --git a/data/src/main/sqldelight/migrations/25.sqm b/data/src/main/sqldelight/migrations/25.sqm new file mode 100644 index 0000000000..86c2ecf1b8 --- /dev/null +++ b/data/src/main/sqldelight/migrations/25.sqm @@ -0,0 +1 @@ +ALTER TABLE categories ADD COLUMN hidden INTEGER DEFAULT 0 NOT NULL; \ No newline at end of file diff --git a/data/src/main/sqldelightanime/dataanime/categories.sq b/data/src/main/sqldelightanime/dataanime/categories.sq index e9094a3962..09dbf3887e 100644 --- a/data/src/main/sqldelightanime/dataanime/categories.sq +++ b/data/src/main/sqldelightanime/dataanime/categories.sq @@ -2,7 +2,8 @@ CREATE TABLE categories( _id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL, sort INTEGER NOT NULL, - flags INTEGER NOT NULL + flags INTEGER NOT NULL, + hidden INTEGER NOT NULL DEFAULT 0 ); -- Insert system category @@ -27,21 +28,46 @@ SELECT _id AS id, name, sort AS `order`, -flags +flags, +hidden FROM categories ORDER BY sort; +getVisibleCategories: +SELECT +_id AS id, +name, +sort AS `order`, +flags, +hidden +FROM categories +WHERE hidden = 0 +ORDER BY sort; + getCategoriesByAnimeId: SELECT C._id AS id, C.name, C.sort AS `order`, -C.flags +C.flags, +C.hidden FROM categories C JOIN animes_categories AC ON C._id = AC.category_id WHERE AC.anime_id = :animeId; +getVisibleCategoriesByAnimeId: +SELECT +C._id AS id, +C.name, +C.sort AS `order`, +C.flags, +C.hidden +FROM categories C +JOIN animes_categories AC +ON C._id = AC.category_id +WHERE AC.anime_id = :animeId AND C.hidden = 0; + insert: INSERT INTO categories(name, sort, flags) VALUES (:name, :order, :flags); @@ -54,7 +80,8 @@ update: UPDATE categories SET name = coalesce(:name, name), sort = coalesce(:order, sort), - flags = coalesce(:flags, flags) + flags = coalesce(:flags, flags), + hidden = coalesce(:hidden, hidden) WHERE _id = :categoryId; updateAllFlags: diff --git a/data/src/main/sqldelightanime/migrations/125.sqm b/data/src/main/sqldelightanime/migrations/125.sqm new file mode 100644 index 0000000000..86c2ecf1b8 --- /dev/null +++ b/data/src/main/sqldelightanime/migrations/125.sqm @@ -0,0 +1 @@ +ALTER TABLE categories ADD COLUMN hidden INTEGER DEFAULT 0 NOT NULL; \ No newline at end of file diff --git a/domain/src/main/java/tachiyomi/domain/category/anime/interactor/CreateAnimeCategoryWithName.kt b/domain/src/main/java/tachiyomi/domain/category/anime/interactor/CreateAnimeCategoryWithName.kt index 35f619c6d4..d705b5a74a 100644 --- a/domain/src/main/java/tachiyomi/domain/category/anime/interactor/CreateAnimeCategoryWithName.kt +++ b/domain/src/main/java/tachiyomi/domain/category/anime/interactor/CreateAnimeCategoryWithName.kt @@ -28,6 +28,7 @@ class CreateAnimeCategoryWithName( name = name, order = nextOrder, flags = initialFlags, + hidden = false, ) try { diff --git a/domain/src/main/java/tachiyomi/domain/category/anime/interactor/GetVisibleAnimeCategories.kt b/domain/src/main/java/tachiyomi/domain/category/anime/interactor/GetVisibleAnimeCategories.kt new file mode 100644 index 0000000000..2b30a49adf --- /dev/null +++ b/domain/src/main/java/tachiyomi/domain/category/anime/interactor/GetVisibleAnimeCategories.kt @@ -0,0 +1,25 @@ +package tachiyomi.domain.category.anime.interactor + +import kotlinx.coroutines.flow.Flow +import tachiyomi.domain.category.anime.repository.AnimeCategoryRepository +import tachiyomi.domain.category.model.Category + +class GetVisibleAnimeCategories( + private val categoryRepository: AnimeCategoryRepository, +) { + fun subscribe(): Flow> { + return categoryRepository.getAllVisibleAnimeCategoriesAsFlow() + } + + fun subscribe(animeId: Long): Flow> { + return categoryRepository.getVisibleCategoriesByAnimeIdAsFlow(animeId) + } + + suspend fun await(): List { + return categoryRepository.getAllVisibleAnimeCategories() + } + + suspend fun await(animeId: Long): List { + return categoryRepository.getVisibleCategoriesByAnimeId(animeId) + } +} diff --git a/domain/src/main/java/tachiyomi/domain/category/anime/interactor/HideAnimeCategory.kt b/domain/src/main/java/tachiyomi/domain/category/anime/interactor/HideAnimeCategory.kt new file mode 100644 index 0000000000..7d4eaf3fbb --- /dev/null +++ b/domain/src/main/java/tachiyomi/domain/category/anime/interactor/HideAnimeCategory.kt @@ -0,0 +1,33 @@ +package tachiyomi.domain.category.anime.interactor + +import logcat.LogPriority +import tachiyomi.core.util.lang.withNonCancellableContext +import tachiyomi.core.util.system.logcat +import tachiyomi.domain.category.anime.repository.AnimeCategoryRepository +import tachiyomi.domain.category.model.Category +import tachiyomi.domain.category.model.CategoryUpdate + +class HideAnimeCategory( + private val categoryRepository: AnimeCategoryRepository, +) { + + suspend fun await(category: Category) = withNonCancellableContext { + val update = CategoryUpdate( + id = category.id, + hidden = !category.hidden, + ) + + try { + categoryRepository.updatePartialAnimeCategory(update) + RenameAnimeCategory.Result.Success + } catch (e: Exception) { + logcat(LogPriority.ERROR, e) + Result.InternalError(e) + } + } + + sealed class Result { + object Success : Result() + data class InternalError(val error: Throwable) : Result() + } +} diff --git a/domain/src/main/java/tachiyomi/domain/category/anime/repository/AnimeCategoryRepository.kt b/domain/src/main/java/tachiyomi/domain/category/anime/repository/AnimeCategoryRepository.kt index 193d3f983b..5f2ecf02a9 100644 --- a/domain/src/main/java/tachiyomi/domain/category/anime/repository/AnimeCategoryRepository.kt +++ b/domain/src/main/java/tachiyomi/domain/category/anime/repository/AnimeCategoryRepository.kt @@ -10,12 +10,20 @@ interface AnimeCategoryRepository { suspend fun getAllAnimeCategories(): List + suspend fun getAllVisibleAnimeCategories(): List + fun getAllAnimeCategoriesAsFlow(): Flow> + fun getAllVisibleAnimeCategoriesAsFlow(): Flow> + suspend fun getCategoriesByAnimeId(animeId: Long): List + suspend fun getVisibleCategoriesByAnimeId(animeId: Long): List + fun getCategoriesByAnimeIdAsFlow(animeId: Long): Flow> + fun getVisibleCategoriesByAnimeIdAsFlow(animeId: Long): Flow> + suspend fun insertAnimeCategory(category: Category) suspend fun updatePartialAnimeCategory(update: CategoryUpdate) diff --git a/domain/src/main/java/tachiyomi/domain/category/manga/interactor/CreateMangaCategoryWithName.kt b/domain/src/main/java/tachiyomi/domain/category/manga/interactor/CreateMangaCategoryWithName.kt index 2caaab2a6b..c3aedebbce 100644 --- a/domain/src/main/java/tachiyomi/domain/category/manga/interactor/CreateMangaCategoryWithName.kt +++ b/domain/src/main/java/tachiyomi/domain/category/manga/interactor/CreateMangaCategoryWithName.kt @@ -28,6 +28,7 @@ class CreateMangaCategoryWithName( name = name, order = nextOrder, flags = initialFlags, + hidden = false, ) try { diff --git a/domain/src/main/java/tachiyomi/domain/category/manga/interactor/GetMangaCategories.kt b/domain/src/main/java/tachiyomi/domain/category/manga/interactor/GetMangaCategories.kt index d47acadb0c..6956ffc09a 100644 --- a/domain/src/main/java/tachiyomi/domain/category/manga/interactor/GetMangaCategories.kt +++ b/domain/src/main/java/tachiyomi/domain/category/manga/interactor/GetMangaCategories.kt @@ -7,7 +7,6 @@ import tachiyomi.domain.category.model.Category class GetMangaCategories( private val categoryRepository: MangaCategoryRepository, ) { - fun subscribe(): Flow> { return categoryRepository.getAllMangaCategoriesAsFlow() } diff --git a/domain/src/main/java/tachiyomi/domain/category/manga/interactor/GetVisibleMangaCategories.kt b/domain/src/main/java/tachiyomi/domain/category/manga/interactor/GetVisibleMangaCategories.kt new file mode 100644 index 0000000000..b5c7a31f42 --- /dev/null +++ b/domain/src/main/java/tachiyomi/domain/category/manga/interactor/GetVisibleMangaCategories.kt @@ -0,0 +1,25 @@ +package tachiyomi.domain.category.manga.interactor + +import kotlinx.coroutines.flow.Flow +import tachiyomi.domain.category.manga.repository.MangaCategoryRepository +import tachiyomi.domain.category.model.Category + +class GetVisibleMangaCategories( + private val categoryRepository: MangaCategoryRepository, +) { + fun subscribe(): Flow> { + return categoryRepository.getAllVisibleMangaCategoriesAsFlow() + } + + fun subscribe(mangaId: Long): Flow> { + return categoryRepository.getVisibleCategoriesByMangaIdAsFlow(mangaId) + } + + suspend fun await(): List { + return categoryRepository.getAllVisibleMangaCategories() + } + + suspend fun await(mangaId: Long): List { + return categoryRepository.getVisibleCategoriesByMangaId(mangaId) + } +} diff --git a/domain/src/main/java/tachiyomi/domain/category/manga/interactor/HideMangaCategory.kt b/domain/src/main/java/tachiyomi/domain/category/manga/interactor/HideMangaCategory.kt new file mode 100644 index 0000000000..11f26c3ed2 --- /dev/null +++ b/domain/src/main/java/tachiyomi/domain/category/manga/interactor/HideMangaCategory.kt @@ -0,0 +1,33 @@ +package tachiyomi.domain.category.manga.interactor + +import logcat.LogPriority +import tachiyomi.core.util.lang.withNonCancellableContext +import tachiyomi.core.util.system.logcat +import tachiyomi.domain.category.manga.repository.MangaCategoryRepository +import tachiyomi.domain.category.model.Category +import tachiyomi.domain.category.model.CategoryUpdate + +class HideMangaCategory( + private val categoryRepository: MangaCategoryRepository, +) { + + suspend fun await(category: Category) = withNonCancellableContext { + val update = CategoryUpdate( + id = category.id, + hidden = !category.hidden, + ) + + try { + categoryRepository.updatePartialMangaCategory(update) + Result.Success + } catch (e: Exception) { + logcat(LogPriority.ERROR, e) + Result.InternalError(e) + } + } + + sealed class Result { + object Success : Result() + data class InternalError(val error: Throwable) : Result() + } +} diff --git a/domain/src/main/java/tachiyomi/domain/category/manga/repository/MangaCategoryRepository.kt b/domain/src/main/java/tachiyomi/domain/category/manga/repository/MangaCategoryRepository.kt index ea396b0d5f..1199b3bf30 100644 --- a/domain/src/main/java/tachiyomi/domain/category/manga/repository/MangaCategoryRepository.kt +++ b/domain/src/main/java/tachiyomi/domain/category/manga/repository/MangaCategoryRepository.kt @@ -10,12 +10,20 @@ interface MangaCategoryRepository { suspend fun getAllMangaCategories(): List + suspend fun getAllVisibleMangaCategories(): List + fun getAllMangaCategoriesAsFlow(): Flow> + fun getAllVisibleMangaCategoriesAsFlow(): Flow> + suspend fun getCategoriesByMangaId(mangaId: Long): List + suspend fun getVisibleCategoriesByMangaId(mangaId: Long): List + fun getCategoriesByMangaIdAsFlow(mangaId: Long): Flow> + fun getVisibleCategoriesByMangaIdAsFlow(mangaId: Long): Flow> + suspend fun insertMangaCategory(category: Category) suspend fun updatePartialMangaCategory(update: CategoryUpdate) diff --git a/domain/src/main/java/tachiyomi/domain/category/model/Category.kt b/domain/src/main/java/tachiyomi/domain/category/model/Category.kt index ea901ce80f..46e2af9482 100644 --- a/domain/src/main/java/tachiyomi/domain/category/model/Category.kt +++ b/domain/src/main/java/tachiyomi/domain/category/model/Category.kt @@ -7,6 +7,7 @@ data class Category( val name: String, val order: Long, val flags: Long, + val hidden: Boolean, ) : Serializable { val isSystemCategory: Boolean = id == UNCATEGORIZED_ID diff --git a/domain/src/main/java/tachiyomi/domain/category/model/CategoryUpdate.kt b/domain/src/main/java/tachiyomi/domain/category/model/CategoryUpdate.kt index d3ee8baa97..66842d8e93 100644 --- a/domain/src/main/java/tachiyomi/domain/category/model/CategoryUpdate.kt +++ b/domain/src/main/java/tachiyomi/domain/category/model/CategoryUpdate.kt @@ -5,4 +5,5 @@ data class CategoryUpdate( val name: String? = null, val order: Long? = null, val flags: Long? = null, + val hidden: Boolean? = null, ) 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 f1124a64e7..37459d2138 100644 --- a/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt +++ b/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt @@ -17,13 +17,29 @@ class LibraryPreferences( fun bottomNavStyle() = preferenceStore.getInt("bottom_nav_style", 0) - fun isDefaultHomeTabLibraryManga() = preferenceStore.getBoolean("default_home_tab_library", false) - - fun libraryDisplayMode() = preferenceStore.getObject("pref_display_mode_library", LibraryDisplayMode.default, LibraryDisplayMode.Serializer::serialize, LibraryDisplayMode.Serializer::deserialize) + fun isDefaultHomeTabLibraryManga() = + preferenceStore.getBoolean("default_home_tab_library", false) + + fun libraryDisplayMode() = preferenceStore.getObject( + "pref_display_mode_library", + LibraryDisplayMode.default, + LibraryDisplayMode.Serializer::serialize, + LibraryDisplayMode.Serializer::deserialize, + ) - fun libraryMangaSortingMode() = preferenceStore.getObject("library_sorting_mode", MangaLibrarySort.default, MangaLibrarySort.Serializer::serialize, MangaLibrarySort.Serializer::deserialize) + fun libraryMangaSortingMode() = preferenceStore.getObject( + "library_sorting_mode", + MangaLibrarySort.default, + MangaLibrarySort.Serializer::serialize, + MangaLibrarySort.Serializer::deserialize, + ) - fun libraryAnimeSortingMode() = preferenceStore.getObject("animelib_sorting_mode", AnimeLibrarySort.default, AnimeLibrarySort.Serializer::serialize, AnimeLibrarySort.Serializer::deserialize) + fun libraryAnimeSortingMode() = preferenceStore.getObject( + "animelib_sorting_mode", + AnimeLibrarySort.default, + AnimeLibrarySort.Serializer::serialize, + AnimeLibrarySort.Serializer::deserialize, + ) fun libraryUpdateInterval() = preferenceStore.getInt("pref_library_update_interval_key", 0) @@ -35,6 +51,7 @@ class LibraryPreferences( DEVICE_ONLY_ON_WIFI, ), ) + fun libraryUpdateItemRestriction() = preferenceStore.getStringSet( "library_update_manga_restriction", setOf( @@ -48,7 +65,8 @@ class LibraryPreferences( fun autoUpdateTrackers() = preferenceStore.getBoolean("auto_update_trackers", false) - fun showContinueViewingButton() = preferenceStore.getBoolean("display_continue_reading_button", false) + fun showContinueViewingButton() = + preferenceStore.getBoolean("display_continue_reading_button", false) // Common Category @@ -58,6 +76,8 @@ class LibraryPreferences( fun categorizedDisplaySettings() = preferenceStore.getBoolean("categorized_display", false) + fun hideHiddenCategoriesSettings() = preferenceStore.getBoolean("hidden_categories", false) + // Common badges fun downloadBadge() = preferenceStore.getBoolean("display_download_badge", false) @@ -82,23 +102,41 @@ class LibraryPreferences( // Mixture Filter - fun filterDownloadedAnime() = preferenceStore.getEnum("pref_filter_animelib_downloaded_v2", TriStateFilter.DISABLED) - fun filterDownloadedManga() = preferenceStore.getEnum("pref_filter_library_downloaded_v2", TriStateFilter.DISABLED) + fun filterDownloadedAnime() = + preferenceStore.getEnum("pref_filter_animelib_downloaded_v2", TriStateFilter.DISABLED) + + fun filterDownloadedManga() = + preferenceStore.getEnum("pref_filter_library_downloaded_v2", TriStateFilter.DISABLED) - fun filterUnseen() = preferenceStore.getEnum("pref_filter_animelib_unread_v2", TriStateFilter.DISABLED) - fun filterUnread() = preferenceStore.getEnum("pref_filter_library_unread_v2", TriStateFilter.DISABLED) + fun filterUnseen() = + preferenceStore.getEnum("pref_filter_animelib_unread_v2", TriStateFilter.DISABLED) - fun filterStartedAnime() = preferenceStore.getEnum("pref_filter_animelib_started_v2", TriStateFilter.DISABLED) - fun filterStartedManga() = preferenceStore.getEnum("pref_filter_library_started_v2", TriStateFilter.DISABLED) + fun filterUnread() = + preferenceStore.getEnum("pref_filter_library_unread_v2", TriStateFilter.DISABLED) - fun filterBookmarkedAnime() = preferenceStore.getEnum("pref_filter_animelib_bookmarked_v2", TriStateFilter.DISABLED) - fun filterBookmarkedManga() = preferenceStore.getEnum("pref_filter_library_bookmarked_v2", TriStateFilter.DISABLED) + fun filterStartedAnime() = + preferenceStore.getEnum("pref_filter_animelib_started_v2", TriStateFilter.DISABLED) - fun filterCompletedAnime() = preferenceStore.getEnum("pref_filter_animelib_completed_v2", TriStateFilter.DISABLED) - fun filterCompletedManga() = preferenceStore.getEnum("pref_filter_library_completed_v2", TriStateFilter.DISABLED) + fun filterStartedManga() = + preferenceStore.getEnum("pref_filter_library_started_v2", TriStateFilter.DISABLED) - fun filterTrackedAnime(id: Int) = preferenceStore.getEnum("pref_filter_animelib_tracked_${id}_v2", TriStateFilter.DISABLED) - fun filterTrackedManga(id: Int) = preferenceStore.getEnum("pref_filter_library_tracked_${id}_v2", TriStateFilter.DISABLED) + fun filterBookmarkedAnime() = + preferenceStore.getEnum("pref_filter_animelib_bookmarked_v2", TriStateFilter.DISABLED) + + fun filterBookmarkedManga() = + preferenceStore.getEnum("pref_filter_library_bookmarked_v2", TriStateFilter.DISABLED) + + fun filterCompletedAnime() = + preferenceStore.getEnum("pref_filter_animelib_completed_v2", TriStateFilter.DISABLED) + + fun filterCompletedManga() = + preferenceStore.getEnum("pref_filter_library_completed_v2", TriStateFilter.DISABLED) + + fun filterTrackedAnime(id: Int) = + preferenceStore.getEnum("pref_filter_animelib_tracked_${id}_v2", TriStateFilter.DISABLED) + + fun filterTrackedManga(id: Int) = + preferenceStore.getEnum("pref_filter_library_tracked_${id}_v2", TriStateFilter.DISABLED) // Mixture Update Count @@ -113,32 +151,68 @@ class LibraryPreferences( fun lastUsedAnimeCategory() = preferenceStore.getInt("last_used_anime_category", 0) fun lastUsedMangaCategory() = preferenceStore.getInt("last_used_category", 0) - fun animeLibraryUpdateCategories() = preferenceStore.getStringSet("animelib_update_categories", emptySet()) - fun mangaLibraryUpdateCategories() = preferenceStore.getStringSet("library_update_categories", emptySet()) + fun animeLibraryUpdateCategories() = + preferenceStore.getStringSet("animelib_update_categories", emptySet()) + + fun mangaLibraryUpdateCategories() = + preferenceStore.getStringSet("library_update_categories", emptySet()) + + fun animeLibraryUpdateCategoriesExclude() = + preferenceStore.getStringSet("animelib_update_categories_exclude", emptySet()) - fun animeLibraryUpdateCategoriesExclude() = preferenceStore.getStringSet("animelib_update_categories_exclude", emptySet()) - fun mangaLibraryUpdateCategoriesExclude() = preferenceStore.getStringSet("library_update_categories_exclude", emptySet()) + fun mangaLibraryUpdateCategoriesExclude() = + preferenceStore.getStringSet("library_update_categories_exclude", emptySet()) // Mixture Item - fun filterEpisodeBySeen() = preferenceStore.getLong("default_episode_filter_by_seen", Anime.SHOW_ALL) - fun filterChapterByRead() = preferenceStore.getLong("default_chapter_filter_by_read", Manga.SHOW_ALL) + fun filterEpisodeBySeen() = + preferenceStore.getLong("default_episode_filter_by_seen", Anime.SHOW_ALL) - fun filterEpisodeByDownloaded() = preferenceStore.getLong("default_episode_filter_by_downloaded", Anime.SHOW_ALL) - fun filterChapterByDownloaded() = preferenceStore.getLong("default_chapter_filter_by_downloaded", Manga.SHOW_ALL) + fun filterChapterByRead() = + preferenceStore.getLong("default_chapter_filter_by_read", Manga.SHOW_ALL) - fun filterEpisodeByBookmarked() = preferenceStore.getLong("default_episode_filter_by_bookmarked", Anime.SHOW_ALL) - fun filterChapterByBookmarked() = preferenceStore.getLong("default_chapter_filter_by_bookmarked", Manga.SHOW_ALL) + fun filterEpisodeByDownloaded() = + preferenceStore.getLong("default_episode_filter_by_downloaded", Anime.SHOW_ALL) + + fun filterChapterByDownloaded() = + preferenceStore.getLong("default_chapter_filter_by_downloaded", Manga.SHOW_ALL) + + fun filterEpisodeByBookmarked() = + preferenceStore.getLong("default_episode_filter_by_bookmarked", Anime.SHOW_ALL) + + fun filterChapterByBookmarked() = + preferenceStore.getLong("default_chapter_filter_by_bookmarked", Manga.SHOW_ALL) // and upload date - fun sortEpisodeBySourceOrNumber() = preferenceStore.getLong("default_episode_sort_by_source_or_number", Anime.EPISODE_SORTING_SOURCE) - fun sortChapterBySourceOrNumber() = preferenceStore.getLong("default_chapter_sort_by_source_or_number", Manga.CHAPTER_SORTING_SOURCE) + fun sortEpisodeBySourceOrNumber() = preferenceStore.getLong( + "default_episode_sort_by_source_or_number", + Anime.EPISODE_SORTING_SOURCE, + ) + + fun sortChapterBySourceOrNumber() = preferenceStore.getLong( + "default_chapter_sort_by_source_or_number", + Manga.CHAPTER_SORTING_SOURCE, + ) + + fun displayEpisodeByNameOrNumber() = preferenceStore.getLong( + "default_chapter_display_by_name_or_number", + Anime.EPISODE_DISPLAY_NAME, + ) - fun displayEpisodeByNameOrNumber() = preferenceStore.getLong("default_chapter_display_by_name_or_number", Anime.EPISODE_DISPLAY_NAME) - fun displayChapterByNameOrNumber() = preferenceStore.getLong("default_chapter_display_by_name_or_number", Manga.CHAPTER_DISPLAY_NAME) + fun displayChapterByNameOrNumber() = preferenceStore.getLong( + "default_chapter_display_by_name_or_number", + Manga.CHAPTER_DISPLAY_NAME, + ) - fun sortEpisodeByAscendingOrDescending() = preferenceStore.getLong("default_chapter_sort_by_ascending_or_descending", Anime.EPISODE_SORT_DESC) - fun sortChapterByAscendingOrDescending() = preferenceStore.getLong("default_chapter_sort_by_ascending_or_descending", Manga.CHAPTER_SORT_DESC) + fun sortEpisodeByAscendingOrDescending() = preferenceStore.getLong( + "default_chapter_sort_by_ascending_or_descending", + Anime.EPISODE_SORT_DESC, + ) + + fun sortChapterByAscendingOrDescending() = preferenceStore.getLong( + "default_chapter_sort_by_ascending_or_descending", + Manga.CHAPTER_SORT_DESC, + ) fun setEpisodeSettingsDefault(anime: Anime) { filterEpisodeBySeen().set(anime.unseenFilterRaw) @@ -160,13 +234,21 @@ class LibraryPreferences( // region Swipe Actions - fun swipeEpisodeEndAction() = preferenceStore.getEnum("pref_episode_swipe_start_action", EpisodeSwipeAction.ToggleBookmark) + fun swipeEpisodeEndAction() = preferenceStore.getEnum( + "pref_episode_swipe_start_action", + EpisodeSwipeAction.ToggleBookmark, + ) - fun swipeEpisodeStartAction() = preferenceStore.getEnum("pref_episode_swipe_end_action", EpisodeSwipeAction.ToggleSeen) + fun swipeEpisodeStartAction() = + preferenceStore.getEnum("pref_episode_swipe_end_action", EpisodeSwipeAction.ToggleSeen) - fun swipeChapterEndAction() = preferenceStore.getEnum("pref_chapter_swipe_start_action", ChapterSwipeAction.ToggleBookmark) + fun swipeChapterEndAction() = preferenceStore.getEnum( + "pref_chapter_swipe_start_action", + ChapterSwipeAction.ToggleBookmark, + ) - fun swipeChapterStartAction() = preferenceStore.getEnum("pref_chapter_swipe_end_action", ChapterSwipeAction.ToggleRead) + fun swipeChapterStartAction() = + preferenceStore.getEnum("pref_chapter_swipe_end_action", ChapterSwipeAction.ToggleRead) // endregion diff --git a/i18n/src/main/res/values/strings-aniyomi.xml b/i18n/src/main/res/values/strings-aniyomi.xml index 4ed8e3e455..cdd789d730 100644 --- a/i18n/src/main/res/values/strings-aniyomi.xml +++ b/i18n/src/main/res/values/strings-aniyomi.xml @@ -1,5 +1,8 @@ + + Hide + Manga Categories Categories Anime Categories @@ -308,6 +311,9 @@ Episode swipe Swipe to right action Swipe to left action + + Hide hidden categories from categories screen + Exclude from data saver Stop excluding from data saver diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml index 0580af943e..a2fc61785b 100644 --- a/i18n/src/main/res/values/strings.xml +++ b/i18n/src/main/res/values/strings.xml @@ -152,7 +152,7 @@ App not available - + General Appearance Library @@ -173,7 +173,7 @@ App lock, secure screen Dump crash logs, battery optimizations - + Theme Dark mode Follow system @@ -229,7 +229,7 @@ %1$d days ago - + Display Grid size %d per row @@ -276,7 +276,7 @@ Swipe to right action Swipe to left action - + Multi Updates pending Update @@ -309,7 +309,7 @@ Shizuku is not running Install and start Shizuku to use Shizuku as extension installer. - + Fullscreen Show tap zones overlay Briefly show when reader is opened @@ -423,7 +423,7 @@ Low Lowest - + Download location Excluded categories Custom location @@ -458,11 +458,11 @@ Services that provide enhanced features for specific sources. Entries are automatically tracked when added to your library. Track - + Only search pinned sources in global search Hide entries already in library - + Create backup Can be used to restore current library Restore backup @@ -496,7 +496,7 @@ Canceled restore You should keep copies of backups in other places as well. - + Network Clear cookies DNS over HTTPS (DoH) @@ -537,7 +537,7 @@ Print verbose logs to system log (reduces app performance) Debug info - + Website Version What\'s new