Allow/Skip one by one

This commit is contained in:
Cuong M. Tran 2024-03-06 18:27:54 +07:00 committed by Cuong Tran
parent 83910501b7
commit a617528fda
No known key found for this signature in database
GPG key ID: 733AA7624B9315C2
8 changed files with 254 additions and 58 deletions

View file

@ -1,13 +1,16 @@
package eu.kanade.presentation.manga
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.FlowColumn
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.padding
@ -62,53 +65,91 @@ fun DuplicateMangaDialog(
@Composable
fun AllowDuplicateDialog(
onDismissRequest: () -> Unit,
onAllowDuplicate: () -> Unit,
onSkipDuplicate: () -> Unit,
onOpenManga: () -> Unit = { },
onAllowAllDuplicate: () -> Unit,
onSkipAllDuplicate: () -> Unit,
onOpenManga: () -> Unit = {},
onAllowDuplicate: () -> Unit = {},
onSkipDuplicate: () -> Unit = {},
duplicatedName: String = "",
) {
AlertDialog(
onDismissRequest = onDismissRequest,
dismissButton = {
TextButton(onClick = onDismissRequest) {
Text(text = stringResource(MR.strings.action_cancel))
}
},
title = {
Text(text = stringResource(MR.strings.are_you_sure))
Text(text = duplicatedName)
},
text = {
Text(text = stringResource(MR.strings.confirm_add_duplicate_manga))
},
confirmButton = {
FlowRow(
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.extraSmall),
Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceAround,
) {
TextButton(
onClick = {
onDismissRequest()
onOpenManga()
},
) {
Text(text = stringResource(MR.strings.action_show_manga))
FlowColumn {
TextButton(
onClick = {
onDismissRequest()
onAllowDuplicate()
},
Modifier.align(Alignment.CenterHorizontally),
) {
Text(text = stringResource(MR.strings.action_allow_duplicate_manga))
}
TextButton(
onClick = {
onDismissRequest()
onAllowAllDuplicate()
},
Modifier.align(Alignment.CenterHorizontally),
) {
Text(text = stringResource(MR.strings.action_allow_all_duplicate_manga))
}
}
TextButton(
onClick = {
onDismissRequest()
onSkipDuplicate()
},
) {
Text(text = stringResource(MR.strings.action_skip_duplicate_manga))
FlowColumn {
TextButton(
onClick = {
onDismissRequest()
onSkipDuplicate()
},
Modifier.align(Alignment.CenterHorizontally),
) {
Text(text = stringResource(MR.strings.action_skip_duplicate_manga))
}
TextButton(
onClick = {
onDismissRequest()
onSkipAllDuplicate()
},
Modifier.align(Alignment.CenterHorizontally),
) {
Text(text = stringResource(MR.strings.action_skip_all_duplicate_manga))
}
}
TextButton(
onClick = {
onDismissRequest()
onAllowDuplicate()
},
) {
Text(text = stringResource(MR.strings.action_allow_duplicate_manga))
FlowColumn {
TextButton(
onClick = {
onDismissRequest()
onOpenManga()
},
Modifier.align(Alignment.CenterHorizontally),
) {
Text(text = stringResource(MR.strings.action_show_manga))
}
TextButton(
onClick = {
onDismissRequest()
},
Modifier.align(Alignment.CenterHorizontally),
) {
Text(text = stringResource(MR.strings.action_cancel))
}
}
}
},
)

View file

@ -5,6 +5,7 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.produceState
import androidx.compose.ui.util.fastAny
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastForEachIndexed
import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
import eu.kanade.domain.manga.interactor.UpdateManga
@ -42,6 +43,7 @@ import tachiyomi.domain.category.interactor.GetCategories
import tachiyomi.domain.category.interactor.SetMangaCategories
import tachiyomi.domain.category.model.Category
import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.manga.interactor.GetDuplicateLibraryManga
import tachiyomi.domain.manga.interactor.GetManga
import tachiyomi.domain.manga.interactor.NetworkToLocalManga
import tachiyomi.domain.source.interactor.CountFeedSavedSearchGlobal
@ -78,6 +80,7 @@ open class FeedScreenModel(
private val getCategories: GetCategories = Injekt.get(),
private val setMangaCategories: SetMangaCategories = Injekt.get(),
private val libraryPreferences: LibraryPreferences = Injekt.get(),
private val getDuplicateLibraryManga: GetDuplicateLibraryManga = Injekt.get(),
// KMK <--
) : StateScreenModel<FeedScreenState>(FeedScreenState()) {
@ -337,9 +340,19 @@ open class FeedScreenModel(
}
}
fun addFavorite() {
fun addFavorite(startIdx: Int = 0) {
screenModelScope.launch {
val mangaList = state.value.selection
val mangaWithDup = getDuplicateLibraryManga(startIdx)
if (mangaWithDup != null)
setDialog(Dialog.AllowDuplicate(mangaWithDup))
else
addFavoriteDuplicate()
}
}
fun addFavoriteDuplicate(skipAllDuplicates: Boolean = false) {
screenModelScope.launch {
val mangaList = if (skipAllDuplicates) getNotDuplicateLibraryMangas() else state.value.selection
val categories = getCategories()
val defaultCategoryId = libraryPreferences.defaultCategory().get()
val defaultCategory = categories.find { it.id == defaultCategoryId.toLong() }
@ -376,6 +389,32 @@ open class FeedScreenModel(
}
}
private suspend fun getNotDuplicateLibraryMangas(): List<DomainManga> {
return state.value.selection.filterNot { manga ->
getDuplicateLibraryManga.await(manga).isNotEmpty()
}
}
private suspend fun getDuplicateLibraryManga(startIdx: Int = 0): Pair<Int, DomainManga>? {
val mangas = state.value.selection
mangas.fastForEachIndexed { index, manga ->
if (index < startIdx) return@fastForEachIndexed
val dup = getDuplicateLibraryManga.await(manga)
if (dup.isEmpty()) return@fastForEachIndexed
return Pair(index, dup.first())
}
return null
}
fun removeDuplicateSelectedManga(index: Int) {
mutableState.update { state ->
val newSelection = state.selection.mutate { list ->
list.removeAt(index)
}
state.copy(selection = newSelection)
}
}
/**
* Bulk update categories of manga using old and new common categories.
*
@ -463,6 +502,7 @@ open class FeedScreenModel(
val mangas: List<DomainManga>,
val initialSelection: ImmutableList<CheckboxState<Category>>,
) : Dialog()
data class AllowDuplicate(val duplicatedManga: Pair<Int, DomainManga>) : Dialog()
// KMK <--
}

View file

@ -23,6 +23,7 @@ import eu.kanade.presentation.browse.FeedScreen
import eu.kanade.presentation.category.components.ChangeCategoryDialog
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.TabContent
import eu.kanade.presentation.manga.AllowDuplicateDialog
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreen
import eu.kanade.tachiyomi.ui.category.CategoryScreen
import eu.kanade.tachiyomi.ui.home.HomeScreen
@ -204,6 +205,28 @@ fun Screen.feedTab(
},
)
}
is FeedScreenModel.Dialog.AllowDuplicate -> {
AllowDuplicateDialog(
onDismissRequest = onDismissRequest,
onAllowAllDuplicate = {
screenModel.addFavoriteDuplicate()
},
onSkipAllDuplicate = {
screenModel.addFavoriteDuplicate(skipAllDuplicates = true)
},
onOpenManga = {
navigator.push(MangaScreen(dialog.duplicatedManga.second.id))
},
onAllowDuplicate = {
screenModel.addFavorite(startIdx = dialog.duplicatedManga.first + 1)
},
onSkipDuplicate = {
screenModel.removeDuplicateSelectedManga(index = dialog.duplicatedManga.first)
screenModel.addFavorite(startIdx = dialog.duplicatedManga.first)
},
duplicatedName = dialog.duplicatedManga.second.title,
)
}
// KMK <--
}
}

View file

@ -399,12 +399,23 @@ data class BrowseSourceScreen(
is BrowseSourceScreenModel.Dialog.AllowDuplicate -> {
AllowDuplicateDialog(
onDismissRequest = onDismissRequest,
onAllowDuplicate = {
onAllowAllDuplicate = {
screenModel.addFavoriteDuplicate()
},
onSkipDuplicate = {
screenModel.addFavoriteDuplicate(skipDuplicate = true)
onSkipAllDuplicate = {
screenModel.addFavoriteDuplicate(skipAllDuplicates = true)
},
onOpenManga = {
navigator.push(MangaScreen(dialog.duplicatedManga.second.id))
},
onAllowDuplicate = {
screenModel.addFavorite(startIdx = dialog.duplicatedManga.first + 1)
},
onSkipDuplicate = {
screenModel.removeDuplicateSelectedManga(index = dialog.duplicatedManga.first)
screenModel.addFavorite(startIdx = dialog.duplicatedManga.first)
},
duplicatedName = dialog.duplicatedManga.second.title,
)
}
// KMK <--

View file

@ -8,6 +8,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastAny
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastForEachIndexed
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.cachedIn
@ -475,18 +476,19 @@ open class BrowseSourceScreenModel(
}
}
fun addFavorite() {
fun addFavorite(startIdx: Int = 0) {
screenModelScope.launch {
if (hasDuplicateLibraryMangas(state.value.selection))
setDialog(Dialog.AllowDuplicate)
val mangaWithDup = getDuplicateLibraryManga(startIdx)
if (mangaWithDup != null)
setDialog(Dialog.AllowDuplicate(mangaWithDup))
else
addFavoriteDuplicate()
}
}
fun addFavoriteDuplicate(skipDuplicate: Boolean = false) {
fun addFavoriteDuplicate(skipAllDuplicates: Boolean = false) {
screenModelScope.launch {
val mangaList = if (skipDuplicate) getNotDuplicateLibraryMangas() else state.value.selection
val mangaList = if (skipAllDuplicates) getNotDuplicateLibraryMangas() else state.value.selection
val categories = getCategories()
val defaultCategoryId = libraryPreferences.defaultCategory().get()
val defaultCategory = categories.find { it.id == defaultCategoryId.toLong() }
@ -523,6 +525,32 @@ open class BrowseSourceScreenModel(
}
}
private suspend fun getNotDuplicateLibraryMangas(): List<Manga> {
return state.value.selection.filterNot { manga ->
getDuplicateLibraryManga.await(manga).isNotEmpty()
}
}
private suspend fun getDuplicateLibraryManga(startIdx: Int = 0): Pair<Int, Manga>? {
val mangas = state.value.selection
mangas.fastForEachIndexed { index, manga ->
if (index < startIdx) return@fastForEachIndexed
val dup = getDuplicateLibraryManga.await(manga)
if (dup.isEmpty()) return@fastForEachIndexed
return Pair(index, dup.first())
}
return null
}
fun removeDuplicateSelectedManga(index: Int) {
mutableState.update { state ->
val newSelection = state.selection.mutate { list ->
list.removeAt(index)
}
state.copy(selection = newSelection)
}
}
/**
* Bulk update categories of manga using old and new common categories.
*
@ -583,18 +611,6 @@ open class BrowseSourceScreenModel(
val common = mangaCategories.reduce { set1, set2 -> set1.intersect(set2) }
return mangaCategories.flatten().distinct().subtract(common)
}
private suspend fun getNotDuplicateLibraryMangas(): List<Manga> {
return state.value.selection.filterNot { manga ->
getDuplicateLibraryManga.await(manga).isNotEmpty()
}
}
private suspend fun hasDuplicateLibraryMangas(mangas: List<Manga>): Boolean {
return mangas.fastAny { manga ->
getDuplicateLibraryManga.await(manga).isNotEmpty()
}
}
// KMK <--
sealed interface Dialog {
@ -616,7 +632,7 @@ open class BrowseSourceScreenModel(
val mangas: List<Manga>,
val initialSelection: ImmutableList<CheckboxState<Category>>,
) : Dialog
data object AllowDuplicate : Dialog
data class AllowDuplicate(val duplicatedManga: Pair<Int, Manga>) : Dialog
// KMK <--
}

View file

@ -15,6 +15,7 @@ import eu.kanade.presentation.browse.SourceFeedScreen
import eu.kanade.presentation.browse.components.SourceFeedAddDialog
import eu.kanade.presentation.browse.components.SourceFeedDeleteDialog
import eu.kanade.presentation.category.components.ChangeCategoryDialog
import eu.kanade.presentation.manga.AllowDuplicateDialog
import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreen
@ -109,6 +110,28 @@ class SourceFeedScreen(val sourceId: Long) : Screen() {
},
)
}
is SourceFeedScreenModel.Dialog.AllowDuplicate -> {
AllowDuplicateDialog(
onDismissRequest = onDismissRequest,
onAllowAllDuplicate = {
screenModel.addFavoriteDuplicate()
},
onSkipAllDuplicate = {
screenModel.addFavoriteDuplicate(skipAllDuplicates = true)
},
onOpenManga = {
navigator.push(MangaScreen(dialog.duplicatedManga.second.id))
},
onAllowDuplicate = {
screenModel.addFavorite(startIdx = dialog.duplicatedManga.first + 1)
},
onSkipDuplicate = {
screenModel.removeDuplicateSelectedManga(index = dialog.duplicatedManga.first)
screenModel.addFavorite(startIdx = dialog.duplicatedManga.first)
},
duplicatedName = dialog.duplicatedManga.second.title,
)
}
// KMK <--
SourceFeedScreenModel.Dialog.Filter -> {
SourceFilterDialog(

View file

@ -7,6 +7,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState
import androidx.compose.ui.util.fastAny
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastForEachIndexed
import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
import dev.icerock.moko.resources.StringResource
@ -49,6 +50,7 @@ import tachiyomi.domain.category.interactor.GetCategories
import tachiyomi.domain.category.interactor.SetMangaCategories
import tachiyomi.domain.category.model.Category
import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.manga.interactor.GetDuplicateLibraryManga
import tachiyomi.domain.manga.interactor.GetManga
import tachiyomi.domain.manga.interactor.NetworkToLocalManga
import tachiyomi.domain.source.interactor.CountFeedSavedSearchBySourceId
@ -84,6 +86,7 @@ open class SourceFeedScreenModel(
private val getCategories: GetCategories = Injekt.get(),
private val setMangaCategories: SetMangaCategories = Injekt.get(),
private val libraryPreferences: LibraryPreferences = Injekt.get(),
private val getDuplicateLibraryManga: GetDuplicateLibraryManga = Injekt.get(),
// KMK <--
) : StateScreenModel<SourceFeedState>(SourceFeedState()) {
@ -341,9 +344,19 @@ open class SourceFeedScreenModel(
}
}
fun addFavorite() {
fun addFavorite(startIdx: Int = 0) {
screenModelScope.launch {
val mangaList = state.value.selection
val mangaWithDup = getDuplicateLibraryManga(startIdx)
if (mangaWithDup != null)
setDialog(Dialog.AllowDuplicate(mangaWithDup))
else
addFavoriteDuplicate()
}
}
fun addFavoriteDuplicate(skipAllDuplicates: Boolean = false) {
screenModelScope.launch {
val mangaList = if (skipAllDuplicates) getNotDuplicateLibraryMangas() else state.value.selection
val categories = getCategories()
val defaultCategoryId = libraryPreferences.defaultCategory().get()
val defaultCategory = categories.find { it.id == defaultCategoryId.toLong() }
@ -380,6 +393,32 @@ open class SourceFeedScreenModel(
}
}
private suspend fun getNotDuplicateLibraryMangas(): List<DomainManga> {
return state.value.selection.filterNot { manga ->
getDuplicateLibraryManga.await(manga).isNotEmpty()
}
}
private suspend fun getDuplicateLibraryManga(startIdx: Int = 0): Pair<Int, DomainManga>? {
val mangas = state.value.selection
mangas.fastForEachIndexed { index, manga ->
if (index < startIdx) return@fastForEachIndexed
val dup = getDuplicateLibraryManga.await(manga)
if (dup.isEmpty()) return@fastForEachIndexed
return Pair(index, dup.first())
}
return null
}
fun removeDuplicateSelectedManga(index: Int) {
mutableState.update { state ->
val newSelection = state.selection.mutate { list ->
list.removeAt(index)
}
state.copy(selection = newSelection)
}
}
/**
* Bulk update categories of manga using old and new common categories.
*
@ -467,6 +506,7 @@ open class SourceFeedScreenModel(
val mangas: List<DomainManga>,
val initialSelection: ImmutableList<CheckboxState<Category>>,
) : Dialog()
data class AllowDuplicate(val duplicatedManga: Pair<Int, DomainManga>) : Dialog()
// KMK <--
}

View file

@ -678,6 +678,10 @@
<string name="remove_from_library">Remove from library</string>
<string name="unknown_title">Unknown title</string>
<string name="confirm_add_duplicate_manga">You have an entry in your library with the same name.\n\nDo you still wish to continue?</string>
<string name="action_allow_duplicate_manga">Allow it</string>
<string name="action_skip_duplicate_manga">Skip it</string>
<string name="action_allow_all_duplicate_manga">Allow all</string>
<string name="action_skip_all_duplicate_manga">Skip all</string>
<string name="manga_added_library">Added to library</string>
<string name="manga_removed_library">Removed from library</string>
<string name="manga_info_expand">More</string>
@ -689,8 +693,6 @@
<string name="source_not_installed">Source not installed: %1$s</string>
<string name="snack_add_to_library">Add to library?</string>
<string name="description_placeholder">No description</string>
<string name="action_allow_duplicate_manga">Allow duplicate</string>
<string name="action_skip_duplicate_manga">Skip duplicate</string>
<!-- Manga chapters -->
<string name="display_mode_chapter">Chapter %1$s</string>