feat(category): Add ability to hide categories from library

This commit is contained in:
Jeffery Orazulike 2023-09-15 19:24:08 +01:00 committed by GitHub
parent 20bf2a95a4
commit b536adb8ec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 528 additions and 98 deletions

View file

@ -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<AnimeCategoryRepository> { 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<MangaCategoryRepository> { 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<AnimeRepository> { AnimeRepositoryImpl(get()) }

View file

@ -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,

View file

@ -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,

View file

@ -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) },
)
}

View file

@ -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),
)
}
}
}

View file

@ -54,13 +54,20 @@ object SettingsLibraryScreen : SearchableSettings {
@Composable
override fun getPreferences(): List<Preference> {
val getCategories = remember { Injekt.get<GetMangaCategories>() }
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<GetAnimeCategories>() }
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<LibraryPreferences>() }
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
},
)

View file

@ -18,6 +18,7 @@ class BackupCategory(
name = this@BackupCategory.name,
flags = this@BackupCategory.flags,
order = this@BackupCategory.order,
hidden = false,
)
}
}

View file

@ -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>(AnimeCategoryScreenState.Loading) {
private val _events: Channel<AnimeCategoryEvent> = 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 -> {}
}
}

View file

@ -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,

View file

@ -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>(MangaCategoryScreenState.Loading) {
private val _events: Channel<MangaCategoryEvent> = 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 -> {}
}
}

View file

@ -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,

View file

@ -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<Category, List<AnimeLibraryItem>>
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(),

View file

@ -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<Category, List<MangaLibraryItem>>
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(),

View file

@ -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,
)
}

View file

@ -20,22 +20,42 @@ class AnimeCategoryRepositoryImpl(
return handler.awaitList { categoriesQueries.getCategories(categoryMapper) }
}
override suspend fun getAllVisibleAnimeCategories(): List<Category> {
return handler.awaitList { categoriesQueries.getVisibleCategories(categoryMapper) }
}
override fun getAllAnimeCategoriesAsFlow(): Flow<List<Category>> {
return handler.subscribeToList { categoriesQueries.getCategories(categoryMapper) }
}
override fun getAllVisibleAnimeCategoriesAsFlow(): Flow<List<Category>> {
return handler.subscribeToList { categoriesQueries.getVisibleCategories(categoryMapper) }
}
override suspend fun getCategoriesByAnimeId(animeId: Long): List<Category> {
return handler.awaitList {
categoriesQueries.getCategoriesByAnimeId(animeId, categoryMapper)
}
}
override suspend fun getVisibleCategoriesByAnimeId(animeId: Long): List<Category> {
return handler.awaitList {
categoriesQueries.getVisibleCategoriesByAnimeId(animeId, categoryMapper)
}
}
override fun getCategoriesByAnimeIdAsFlow(animeId: Long): Flow<List<Category>> {
return handler.subscribeToList {
categoriesQueries.getCategoriesByAnimeId(animeId, categoryMapper)
}
}
override fun getVisibleCategoriesByAnimeIdAsFlow(animeId: Long): Flow<List<Category>> {
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,
)
}

View file

@ -20,22 +20,42 @@ class MangaCategoryRepositoryImpl(
return handler.awaitList { categoriesQueries.getCategories(categoryMapper) }
}
override suspend fun getAllVisibleMangaCategories(): List<Category> {
return handler.awaitList { categoriesQueries.getVisibleCategories(categoryMapper) }
}
override fun getAllMangaCategoriesAsFlow(): Flow<List<Category>> {
return handler.subscribeToList { categoriesQueries.getCategories(categoryMapper) }
}
override fun getAllVisibleMangaCategoriesAsFlow(): Flow<List<Category>> {
return handler.subscribeToList { categoriesQueries.getVisibleCategories(categoryMapper) }
}
override suspend fun getCategoriesByMangaId(mangaId: Long): List<Category> {
return handler.awaitList {
categoriesQueries.getCategoriesByMangaId(mangaId, categoryMapper)
}
}
override suspend fun getVisibleCategoriesByMangaId(mangaId: Long): List<Category> {
return handler.awaitList {
categoriesQueries.getVisibleCategoriesByMangaId(mangaId, categoryMapper)
}
}
override fun getCategoriesByMangaIdAsFlow(mangaId: Long): Flow<List<Category>> {
return handler.subscribeToList {
categoriesQueries.getCategoriesByMangaId(mangaId, categoryMapper)
}
}
override fun getVisibleCategoriesByMangaIdAsFlow(mangaId: Long): Flow<List<Category>> {
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,
)
}

View file

@ -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:

View file

@ -0,0 +1 @@
ALTER TABLE categories ADD COLUMN hidden INTEGER DEFAULT 0 NOT NULL;

View file

@ -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:

View file

@ -0,0 +1 @@
ALTER TABLE categories ADD COLUMN hidden INTEGER DEFAULT 0 NOT NULL;

View file

@ -28,6 +28,7 @@ class CreateAnimeCategoryWithName(
name = name,
order = nextOrder,
flags = initialFlags,
hidden = false,
)
try {

View file

@ -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<List<Category>> {
return categoryRepository.getAllVisibleAnimeCategoriesAsFlow()
}
fun subscribe(animeId: Long): Flow<List<Category>> {
return categoryRepository.getVisibleCategoriesByAnimeIdAsFlow(animeId)
}
suspend fun await(): List<Category> {
return categoryRepository.getAllVisibleAnimeCategories()
}
suspend fun await(animeId: Long): List<Category> {
return categoryRepository.getVisibleCategoriesByAnimeId(animeId)
}
}

View file

@ -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()
}
}

View file

@ -10,12 +10,20 @@ interface AnimeCategoryRepository {
suspend fun getAllAnimeCategories(): List<Category>
suspend fun getAllVisibleAnimeCategories(): List<Category>
fun getAllAnimeCategoriesAsFlow(): Flow<List<Category>>
fun getAllVisibleAnimeCategoriesAsFlow(): Flow<List<Category>>
suspend fun getCategoriesByAnimeId(animeId: Long): List<Category>
suspend fun getVisibleCategoriesByAnimeId(animeId: Long): List<Category>
fun getCategoriesByAnimeIdAsFlow(animeId: Long): Flow<List<Category>>
fun getVisibleCategoriesByAnimeIdAsFlow(animeId: Long): Flow<List<Category>>
suspend fun insertAnimeCategory(category: Category)
suspend fun updatePartialAnimeCategory(update: CategoryUpdate)

View file

@ -28,6 +28,7 @@ class CreateMangaCategoryWithName(
name = name,
order = nextOrder,
flags = initialFlags,
hidden = false,
)
try {

View file

@ -7,7 +7,6 @@ import tachiyomi.domain.category.model.Category
class GetMangaCategories(
private val categoryRepository: MangaCategoryRepository,
) {
fun subscribe(): Flow<List<Category>> {
return categoryRepository.getAllMangaCategoriesAsFlow()
}

View file

@ -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<List<Category>> {
return categoryRepository.getAllVisibleMangaCategoriesAsFlow()
}
fun subscribe(mangaId: Long): Flow<List<Category>> {
return categoryRepository.getVisibleCategoriesByMangaIdAsFlow(mangaId)
}
suspend fun await(): List<Category> {
return categoryRepository.getAllVisibleMangaCategories()
}
suspend fun await(mangaId: Long): List<Category> {
return categoryRepository.getVisibleCategoriesByMangaId(mangaId)
}
}

View file

@ -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()
}
}

View file

@ -10,12 +10,20 @@ interface MangaCategoryRepository {
suspend fun getAllMangaCategories(): List<Category>
suspend fun getAllVisibleMangaCategories(): List<Category>
fun getAllMangaCategoriesAsFlow(): Flow<List<Category>>
fun getAllVisibleMangaCategoriesAsFlow(): Flow<List<Category>>
suspend fun getCategoriesByMangaId(mangaId: Long): List<Category>
suspend fun getVisibleCategoriesByMangaId(mangaId: Long): List<Category>
fun getCategoriesByMangaIdAsFlow(mangaId: Long): Flow<List<Category>>
fun getVisibleCategoriesByMangaIdAsFlow(mangaId: Long): Flow<List<Category>>
suspend fun insertMangaCategory(category: Category)
suspend fun updatePartialMangaCategory(update: CategoryUpdate)

View file

@ -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

View file

@ -5,4 +5,5 @@ data class CategoryUpdate(
val name: String? = null,
val order: Long? = null,
val flags: Long? = null,
val hidden: Boolean? = null,
)

View file

@ -17,13 +17,29 @@ class LibraryPreferences(
fun bottomNavStyle() = preferenceStore.getInt("bottom_nav_style", 0)
fun isDefaultHomeTabLibraryManga() = preferenceStore.getBoolean("default_home_tab_library", false)
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 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 filterUnseen() = preferenceStore.getEnum("pref_filter_animelib_unread_v2", TriStateFilter.DISABLED)
fun filterUnread() = preferenceStore.getEnum("pref_filter_library_unread_v2", TriStateFilter.DISABLED)
fun filterDownloadedManga() =
preferenceStore.getEnum("pref_filter_library_downloaded_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 filterUnseen() =
preferenceStore.getEnum("pref_filter_animelib_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 filterUnread() =
preferenceStore.getEnum("pref_filter_library_unread_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 filterStartedAnime() =
preferenceStore.getEnum("pref_filter_animelib_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 filterStartedManga() =
preferenceStore.getEnum("pref_filter_library_started_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 animeLibraryUpdateCategoriesExclude() = preferenceStore.getStringSet("animelib_update_categories_exclude", emptySet())
fun mangaLibraryUpdateCategoriesExclude() = preferenceStore.getStringSet("library_update_categories_exclude", emptySet())
fun mangaLibraryUpdateCategories() =
preferenceStore.getStringSet("library_update_categories", emptySet())
fun animeLibraryUpdateCategoriesExclude() =
preferenceStore.getStringSet("animelib_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 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 sortChapterBySourceOrNumber() = preferenceStore.getLong(
"default_chapter_sort_by_source_or_number",
Manga.CHAPTER_SORTING_SOURCE,
)
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 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 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

View file

@ -1,5 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Actions -->
<string name="action_hide">Hide</string>
<string name="manga_categories">Manga Categories</string>
<string name="general_categories">Categories</string>
<string name="anime_categories">Anime Categories</string>
@ -308,6 +311,9 @@
<string name="pref_episode_swipe">Episode swipe</string>
<string name="pref_episode_swipe_end">Swipe to right action</string>
<string name="pref_episode_swipe_start">Swipe to left action</string>
<string name="pref_category_hide_hidden">Hide hidden categories from categories screen</string>
<!-- TachiyomiSY -->
<string name="data_saver_exclude">Exclude from data saver</string>
<string name="data_saver_stop_exclude">Stop excluding from data saver</string>

View file

@ -152,7 +152,7 @@
<string name="app_not_available">App not available</string>
<!-- Preferences -->
<!-- Subsections -->
<!-- Subsections -->
<string name="pref_category_general">General</string>
<string name="pref_category_appearance">Appearance</string>
<string name="pref_category_library">Library</string>
@ -173,7 +173,7 @@
<string name="pref_security_summary">App lock, secure screen</string>
<string name="pref_advanced_summary">Dump crash logs, battery optimizations</string>
<!-- General section -->
<!-- General section -->
<string name="pref_category_theme">Theme</string>
<string name="pref_theme_mode">Dark mode</string>
<string name="theme_system">Follow system</string>
@ -229,7 +229,7 @@
<item quantity="other">%1$d days ago</item>
</plurals>
<!-- Library section -->
<!-- Library section -->
<string name="pref_category_display">Display</string>
<string name="pref_library_columns">Grid size</string>
<string name="pref_library_columns_per_row">%d per row</string>
@ -276,7 +276,7 @@
<string name="pref_chapter_swipe_end">Swipe to right action</string>
<string name="pref_chapter_swipe_start">Swipe to left action</string>
<!-- Extension section -->
<!-- Extension section -->
<string name="multi_lang">Multi</string>
<string name="ext_updates_pending">Updates pending</string>
<string name="ext_update">Update</string>
@ -309,7 +309,7 @@
<string name="ext_installer_shizuku_stopped">Shizuku is not running</string>
<string name="ext_installer_shizuku_unavailable_dialog">Install and start Shizuku to use Shizuku as extension installer.</string>
<!-- Reader section -->
<!-- Reader section -->
<string name="pref_fullscreen">Fullscreen</string>
<string name="pref_show_navigation_mode">Show tap zones overlay</string>
<string name="pref_show_navigation_mode_summary">Briefly show when reader is opened</string>
@ -423,7 +423,7 @@
<string name="pref_low">Low</string>
<string name="pref_lowest">Lowest</string>
<!-- Downloads section -->
<!-- Downloads section -->
<string name="pref_download_directory">Download location</string>
<string name="pref_remove_exclude_categories">Excluded categories</string>
<string name="custom_dir">Custom location</string>
@ -458,11 +458,11 @@
<string name="enhanced_tracking_info">Services that provide enhanced features for specific sources. Entries are automatically tracked when added to your library.</string>
<string name="action_track">Track</string>
<!-- Browse section -->
<!-- Browse section -->
<string name="pref_search_pinned_sources_only">Only search pinned sources in global search</string>
<string name="pref_hide_in_library_items">Hide entries already in library</string>
<!-- Backup section -->
<!-- Backup section -->
<string name="pref_create_backup">Create backup</string>
<string name="pref_create_backup_summ">Can be used to restore current library</string>
<string name="pref_restore_backup">Restore backup</string>
@ -496,7 +496,7 @@
<string name="restoring_backup_canceled">Canceled restore</string>
<string name="backup_info">You should keep copies of backups in other places as well.</string>
<!-- Advanced section -->
<!-- Advanced section -->
<string name="label_network">Network</string>
<string name="pref_clear_cookies">Clear cookies</string>
<string name="pref_dns_over_https">DNS over HTTPS (DoH)</string>
@ -537,7 +537,7 @@
<string name="pref_verbose_logging_summary">Print verbose logs to system log (reduces app performance)</string>
<string name="pref_debug_info">Debug info</string>
<!-- About section -->
<!-- About section -->
<string name="website">Website</string>
<string name="version">Version</string>
<string name="whats_new">What\'s new</string>