Allow/Skip one by one
This commit is contained in:
parent
83910501b7
commit
a617528fda
8 changed files with 254 additions and 58 deletions
|
|
@ -1,13 +1,16 @@
|
||||||
package eu.kanade.presentation.manga
|
package eu.kanade.presentation.manga
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.FlowColumn
|
||||||
import androidx.compose.foundation.layout.FlowRow
|
import androidx.compose.foundation.layout.FlowRow
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.material.padding
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
|
|
@ -62,53 +65,91 @@ fun DuplicateMangaDialog(
|
||||||
@Composable
|
@Composable
|
||||||
fun AllowDuplicateDialog(
|
fun AllowDuplicateDialog(
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
onAllowDuplicate: () -> Unit,
|
onAllowAllDuplicate: () -> Unit,
|
||||||
onSkipDuplicate: () -> Unit,
|
onSkipAllDuplicate: () -> Unit,
|
||||||
onOpenManga: () -> Unit = { },
|
onOpenManga: () -> Unit = {},
|
||||||
|
onAllowDuplicate: () -> Unit = {},
|
||||||
|
onSkipDuplicate: () -> Unit = {},
|
||||||
|
duplicatedName: String = "",
|
||||||
) {
|
) {
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
dismissButton = {
|
|
||||||
TextButton(onClick = onDismissRequest) {
|
|
||||||
Text(text = stringResource(MR.strings.action_cancel))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
title = {
|
title = {
|
||||||
Text(text = stringResource(MR.strings.are_you_sure))
|
Text(text = duplicatedName)
|
||||||
},
|
},
|
||||||
text = {
|
text = {
|
||||||
Text(text = stringResource(MR.strings.confirm_add_duplicate_manga))
|
Text(text = stringResource(MR.strings.confirm_add_duplicate_manga))
|
||||||
},
|
},
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
FlowRow(
|
FlowRow(
|
||||||
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.extraSmall),
|
Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.SpaceAround,
|
||||||
) {
|
) {
|
||||||
TextButton(
|
FlowColumn {
|
||||||
onClick = {
|
TextButton(
|
||||||
onDismissRequest()
|
onClick = {
|
||||||
onOpenManga()
|
onDismissRequest()
|
||||||
},
|
onAllowDuplicate()
|
||||||
) {
|
},
|
||||||
Text(text = stringResource(MR.strings.action_show_manga))
|
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(
|
FlowColumn {
|
||||||
onClick = {
|
TextButton(
|
||||||
onDismissRequest()
|
onClick = {
|
||||||
onSkipDuplicate()
|
onDismissRequest()
|
||||||
},
|
onSkipDuplicate()
|
||||||
) {
|
},
|
||||||
Text(text = stringResource(MR.strings.action_skip_duplicate_manga))
|
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(
|
FlowColumn {
|
||||||
onClick = {
|
TextButton(
|
||||||
onDismissRequest()
|
onClick = {
|
||||||
onAllowDuplicate()
|
onDismissRequest()
|
||||||
},
|
onOpenManga()
|
||||||
) {
|
},
|
||||||
Text(text = stringResource(MR.strings.action_allow_duplicate_manga))
|
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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.produceState
|
import androidx.compose.runtime.produceState
|
||||||
import androidx.compose.ui.util.fastAny
|
import androidx.compose.ui.util.fastAny
|
||||||
import androidx.compose.ui.util.fastForEach
|
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.StateScreenModel
|
||||||
import cafe.adriel.voyager.core.model.screenModelScope
|
import cafe.adriel.voyager.core.model.screenModelScope
|
||||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
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.interactor.SetMangaCategories
|
||||||
import tachiyomi.domain.category.model.Category
|
import tachiyomi.domain.category.model.Category
|
||||||
import tachiyomi.domain.library.service.LibraryPreferences
|
import tachiyomi.domain.library.service.LibraryPreferences
|
||||||
|
import tachiyomi.domain.manga.interactor.GetDuplicateLibraryManga
|
||||||
import tachiyomi.domain.manga.interactor.GetManga
|
import tachiyomi.domain.manga.interactor.GetManga
|
||||||
import tachiyomi.domain.manga.interactor.NetworkToLocalManga
|
import tachiyomi.domain.manga.interactor.NetworkToLocalManga
|
||||||
import tachiyomi.domain.source.interactor.CountFeedSavedSearchGlobal
|
import tachiyomi.domain.source.interactor.CountFeedSavedSearchGlobal
|
||||||
|
|
@ -78,6 +80,7 @@ open class FeedScreenModel(
|
||||||
private val getCategories: GetCategories = Injekt.get(),
|
private val getCategories: GetCategories = Injekt.get(),
|
||||||
private val setMangaCategories: SetMangaCategories = Injekt.get(),
|
private val setMangaCategories: SetMangaCategories = Injekt.get(),
|
||||||
private val libraryPreferences: LibraryPreferences = Injekt.get(),
|
private val libraryPreferences: LibraryPreferences = Injekt.get(),
|
||||||
|
private val getDuplicateLibraryManga: GetDuplicateLibraryManga = Injekt.get(),
|
||||||
// KMK <--
|
// KMK <--
|
||||||
) : StateScreenModel<FeedScreenState>(FeedScreenState()) {
|
) : StateScreenModel<FeedScreenState>(FeedScreenState()) {
|
||||||
|
|
||||||
|
|
@ -337,9 +340,19 @@ open class FeedScreenModel(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addFavorite() {
|
fun addFavorite(startIdx: Int = 0) {
|
||||||
screenModelScope.launch {
|
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 categories = getCategories()
|
||||||
val defaultCategoryId = libraryPreferences.defaultCategory().get()
|
val defaultCategoryId = libraryPreferences.defaultCategory().get()
|
||||||
val defaultCategory = categories.find { it.id == defaultCategoryId.toLong() }
|
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.
|
* Bulk update categories of manga using old and new common categories.
|
||||||
*
|
*
|
||||||
|
|
@ -463,6 +502,7 @@ open class FeedScreenModel(
|
||||||
val mangas: List<DomainManga>,
|
val mangas: List<DomainManga>,
|
||||||
val initialSelection: ImmutableList<CheckboxState<Category>>,
|
val initialSelection: ImmutableList<CheckboxState<Category>>,
|
||||||
) : Dialog()
|
) : Dialog()
|
||||||
|
data class AllowDuplicate(val duplicatedManga: Pair<Int, DomainManga>) : Dialog()
|
||||||
// KMK <--
|
// KMK <--
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import eu.kanade.presentation.browse.FeedScreen
|
||||||
import eu.kanade.presentation.category.components.ChangeCategoryDialog
|
import eu.kanade.presentation.category.components.ChangeCategoryDialog
|
||||||
import eu.kanade.presentation.components.AppBar
|
import eu.kanade.presentation.components.AppBar
|
||||||
import eu.kanade.presentation.components.TabContent
|
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.browse.source.browse.BrowseSourceScreen
|
||||||
import eu.kanade.tachiyomi.ui.category.CategoryScreen
|
import eu.kanade.tachiyomi.ui.category.CategoryScreen
|
||||||
import eu.kanade.tachiyomi.ui.home.HomeScreen
|
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 <--
|
// KMK <--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -399,12 +399,23 @@ data class BrowseSourceScreen(
|
||||||
is BrowseSourceScreenModel.Dialog.AllowDuplicate -> {
|
is BrowseSourceScreenModel.Dialog.AllowDuplicate -> {
|
||||||
AllowDuplicateDialog(
|
AllowDuplicateDialog(
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
onAllowDuplicate = {
|
onAllowAllDuplicate = {
|
||||||
screenModel.addFavoriteDuplicate()
|
screenModel.addFavoriteDuplicate()
|
||||||
},
|
},
|
||||||
onSkipDuplicate = {
|
onSkipAllDuplicate = {
|
||||||
screenModel.addFavoriteDuplicate(skipDuplicate = true)
|
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 <--
|
// KMK <--
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.util.fastAny
|
import androidx.compose.ui.util.fastAny
|
||||||
import androidx.compose.ui.util.fastForEach
|
import androidx.compose.ui.util.fastForEach
|
||||||
|
import androidx.compose.ui.util.fastForEachIndexed
|
||||||
import androidx.paging.Pager
|
import androidx.paging.Pager
|
||||||
import androidx.paging.PagingConfig
|
import androidx.paging.PagingConfig
|
||||||
import androidx.paging.cachedIn
|
import androidx.paging.cachedIn
|
||||||
|
|
@ -475,18 +476,19 @@ open class BrowseSourceScreenModel(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addFavorite() {
|
fun addFavorite(startIdx: Int = 0) {
|
||||||
screenModelScope.launch {
|
screenModelScope.launch {
|
||||||
if (hasDuplicateLibraryMangas(state.value.selection))
|
val mangaWithDup = getDuplicateLibraryManga(startIdx)
|
||||||
setDialog(Dialog.AllowDuplicate)
|
if (mangaWithDup != null)
|
||||||
|
setDialog(Dialog.AllowDuplicate(mangaWithDup))
|
||||||
else
|
else
|
||||||
addFavoriteDuplicate()
|
addFavoriteDuplicate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addFavoriteDuplicate(skipDuplicate: Boolean = false) {
|
fun addFavoriteDuplicate(skipAllDuplicates: Boolean = false) {
|
||||||
screenModelScope.launch {
|
screenModelScope.launch {
|
||||||
val mangaList = if (skipDuplicate) getNotDuplicateLibraryMangas() else state.value.selection
|
val mangaList = if (skipAllDuplicates) getNotDuplicateLibraryMangas() else state.value.selection
|
||||||
val categories = getCategories()
|
val categories = getCategories()
|
||||||
val defaultCategoryId = libraryPreferences.defaultCategory().get()
|
val defaultCategoryId = libraryPreferences.defaultCategory().get()
|
||||||
val defaultCategory = categories.find { it.id == defaultCategoryId.toLong() }
|
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.
|
* 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) }
|
val common = mangaCategories.reduce { set1, set2 -> set1.intersect(set2) }
|
||||||
return mangaCategories.flatten().distinct().subtract(common)
|
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 <--
|
// KMK <--
|
||||||
|
|
||||||
sealed interface Dialog {
|
sealed interface Dialog {
|
||||||
|
|
@ -616,7 +632,7 @@ open class BrowseSourceScreenModel(
|
||||||
val mangas: List<Manga>,
|
val mangas: List<Manga>,
|
||||||
val initialSelection: ImmutableList<CheckboxState<Category>>,
|
val initialSelection: ImmutableList<CheckboxState<Category>>,
|
||||||
) : Dialog
|
) : Dialog
|
||||||
data object AllowDuplicate : Dialog
|
data class AllowDuplicate(val duplicatedManga: Pair<Int, Manga>) : Dialog
|
||||||
// KMK <--
|
// KMK <--
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import eu.kanade.presentation.browse.SourceFeedScreen
|
||||||
import eu.kanade.presentation.browse.components.SourceFeedAddDialog
|
import eu.kanade.presentation.browse.components.SourceFeedAddDialog
|
||||||
import eu.kanade.presentation.browse.components.SourceFeedDeleteDialog
|
import eu.kanade.presentation.browse.components.SourceFeedDeleteDialog
|
||||||
import eu.kanade.presentation.category.components.ChangeCategoryDialog
|
import eu.kanade.presentation.category.components.ChangeCategoryDialog
|
||||||
|
import eu.kanade.presentation.manga.AllowDuplicateDialog
|
||||||
import eu.kanade.presentation.util.Screen
|
import eu.kanade.presentation.util.Screen
|
||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreen
|
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 <--
|
// KMK <--
|
||||||
SourceFeedScreenModel.Dialog.Filter -> {
|
SourceFeedScreenModel.Dialog.Filter -> {
|
||||||
SourceFilterDialog(
|
SourceFilterDialog(
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.produceState
|
import androidx.compose.runtime.produceState
|
||||||
import androidx.compose.ui.util.fastAny
|
import androidx.compose.ui.util.fastAny
|
||||||
import androidx.compose.ui.util.fastForEach
|
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.StateScreenModel
|
||||||
import cafe.adriel.voyager.core.model.screenModelScope
|
import cafe.adriel.voyager.core.model.screenModelScope
|
||||||
import dev.icerock.moko.resources.StringResource
|
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.interactor.SetMangaCategories
|
||||||
import tachiyomi.domain.category.model.Category
|
import tachiyomi.domain.category.model.Category
|
||||||
import tachiyomi.domain.library.service.LibraryPreferences
|
import tachiyomi.domain.library.service.LibraryPreferences
|
||||||
|
import tachiyomi.domain.manga.interactor.GetDuplicateLibraryManga
|
||||||
import tachiyomi.domain.manga.interactor.GetManga
|
import tachiyomi.domain.manga.interactor.GetManga
|
||||||
import tachiyomi.domain.manga.interactor.NetworkToLocalManga
|
import tachiyomi.domain.manga.interactor.NetworkToLocalManga
|
||||||
import tachiyomi.domain.source.interactor.CountFeedSavedSearchBySourceId
|
import tachiyomi.domain.source.interactor.CountFeedSavedSearchBySourceId
|
||||||
|
|
@ -84,6 +86,7 @@ open class SourceFeedScreenModel(
|
||||||
private val getCategories: GetCategories = Injekt.get(),
|
private val getCategories: GetCategories = Injekt.get(),
|
||||||
private val setMangaCategories: SetMangaCategories = Injekt.get(),
|
private val setMangaCategories: SetMangaCategories = Injekt.get(),
|
||||||
private val libraryPreferences: LibraryPreferences = Injekt.get(),
|
private val libraryPreferences: LibraryPreferences = Injekt.get(),
|
||||||
|
private val getDuplicateLibraryManga: GetDuplicateLibraryManga = Injekt.get(),
|
||||||
// KMK <--
|
// KMK <--
|
||||||
) : StateScreenModel<SourceFeedState>(SourceFeedState()) {
|
) : StateScreenModel<SourceFeedState>(SourceFeedState()) {
|
||||||
|
|
||||||
|
|
@ -341,9 +344,19 @@ open class SourceFeedScreenModel(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addFavorite() {
|
fun addFavorite(startIdx: Int = 0) {
|
||||||
screenModelScope.launch {
|
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 categories = getCategories()
|
||||||
val defaultCategoryId = libraryPreferences.defaultCategory().get()
|
val defaultCategoryId = libraryPreferences.defaultCategory().get()
|
||||||
val defaultCategory = categories.find { it.id == defaultCategoryId.toLong() }
|
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.
|
* Bulk update categories of manga using old and new common categories.
|
||||||
*
|
*
|
||||||
|
|
@ -467,6 +506,7 @@ open class SourceFeedScreenModel(
|
||||||
val mangas: List<DomainManga>,
|
val mangas: List<DomainManga>,
|
||||||
val initialSelection: ImmutableList<CheckboxState<Category>>,
|
val initialSelection: ImmutableList<CheckboxState<Category>>,
|
||||||
) : Dialog()
|
) : Dialog()
|
||||||
|
data class AllowDuplicate(val duplicatedManga: Pair<Int, DomainManga>) : Dialog()
|
||||||
// KMK <--
|
// KMK <--
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -678,6 +678,10 @@
|
||||||
<string name="remove_from_library">Remove from library</string>
|
<string name="remove_from_library">Remove from library</string>
|
||||||
<string name="unknown_title">Unknown title</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="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_added_library">Added to library</string>
|
||||||
<string name="manga_removed_library">Removed from library</string>
|
<string name="manga_removed_library">Removed from library</string>
|
||||||
<string name="manga_info_expand">More</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="source_not_installed">Source not installed: %1$s</string>
|
||||||
<string name="snack_add_to_library">Add to library?</string>
|
<string name="snack_add_to_library">Add to library?</string>
|
||||||
<string name="description_placeholder">No description</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 -->
|
<!-- Manga chapters -->
|
||||||
<string name="display_mode_chapter">Chapter %1$s</string>
|
<string name="display_mode_chapter">Chapter %1$s</string>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue