Allow migrate mangas when using Bulk-favorite (#824)

* Combine ChangeMangaCategoryDialog

* Allow migrating a single manga when using Bulk-favorite

* Allow migrating mangas one by one when using Bulk-favorite

* set visibility

* Fix duplicate toggleSelection after migration

* After migration, continue adding favorites if there are more to bulk-favorite or push new Manga screen if it's a single manga being added by long-click

* fix usage of ChangeMangaCategory

* improve toggleSelection code

* refactoring ChangeMangasCategory

* switch LocalNavigator.currentOrThrow to current

* split composable to `presentation`

* hoisting the state to a common parent composable

* Fix migration dialog dismissed when clicking on Show entry

* Don't push new Manga screen after migration of duplicated entries everywhere
This commit is contained in:
Cuong-Tran 2025-04-09 11:42:43 +07:00 committed by GitHub
parent 8d94c09a43
commit fa7b18590e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 282 additions and 231 deletions

View file

@ -17,13 +17,13 @@ import eu.kanade.presentation.browse.components.GlobalSearchCardRow
import eu.kanade.presentation.browse.components.GlobalSearchErrorResultItem
import eu.kanade.presentation.browse.components.GlobalSearchLoadingResultItem
import eu.kanade.presentation.browse.components.GlobalSearchResultItem
import eu.kanade.presentation.browse.components.bulkSelectionButton
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.AppBarActions
import eu.kanade.presentation.components.AppBarTitle
import eu.kanade.presentation.components.BulkSelectionToolbar
import eu.kanade.presentation.components.SearchToolbar
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
import eu.kanade.tachiyomi.ui.browse.bulkSelectionButton
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import tachiyomi.domain.manga.model.Manga

View file

@ -14,7 +14,6 @@ import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.AppBarActions
import eu.kanade.presentation.components.DropdownMenu
import eu.kanade.presentation.components.RadioMenuItem
import eu.kanade.tachiyomi.ui.browse.bulkSelectionButton
import kotlinx.collections.immutable.persistentListOf
import tachiyomi.domain.library.model.LibraryDisplayMode
import tachiyomi.i18n.MR

View file

@ -20,7 +20,6 @@ import eu.kanade.presentation.components.RadioMenuItem
import eu.kanade.presentation.components.SearchToolbar
import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.ui.browse.bulkSelectionButton
import exh.source.anyIs
import kotlinx.collections.immutable.persistentListOf
import tachiyomi.domain.library.model.LibraryDisplayMode

View file

@ -0,0 +1,225 @@
package eu.kanade.presentation.browse.components
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Checklist
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.Navigator
import eu.kanade.presentation.category.components.ChangeCategoryDialog
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.manga.DuplicateMangaDialog
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel.Dialog
import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateDialog
import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateDialogScreenModel
import eu.kanade.tachiyomi.ui.category.CategoryScreen
import eu.kanade.tachiyomi.ui.manga.MangaScreen
import tachiyomi.domain.manga.model.Manga
import tachiyomi.i18n.kmk.KMR
import tachiyomi.presentation.core.i18n.stringResource
/**
* Compose to shows the bulk favorite dialogs.
*
* @param bulkFavoriteScreenModel the screen model.
* @param dialog the dialog to show.
*/
@Composable
fun BulkFavoriteDialogs(
bulkFavoriteScreenModel: BulkFavoriteScreenModel,
dialog: Dialog?,
) {
val navigator = LocalNavigator.current
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
when (dialog) {
/* Bulk-favorite actions */
is Dialog.ChangeMangasCategory ->
ChangeMangasCategoryDialog(
dialog = dialog,
navigator = navigator,
onDismiss = bulkFavoriteScreenModel::dismissDialog,
onConfirm = { include, exclude ->
bulkFavoriteScreenModel.setMangasCategories(dialog.mangas, include, exclude)
},
)
is Dialog.BulkAllowDuplicate ->
BulkAllowDuplicateDialog(
dialog = dialog,
navigator = navigator,
onDismiss = bulkFavoriteScreenModel::dismissDialog,
stopRunning = bulkFavoriteScreenModel::stopRunning,
addFavorite = bulkFavoriteScreenModel::addFavorite,
showMigrateDialog = bulkFavoriteScreenModel::showMigrateDialog,
addFavoriteDuplicate = bulkFavoriteScreenModel::addFavoriteDuplicate,
removeDuplicateSelectedManga = bulkFavoriteScreenModel::removeDuplicateSelectedManga,
)
/* Single-favorite actions for screens originally don't have it */
is Dialog.AddDuplicateManga ->
AddDuplicateMangaDialog(
dialog = dialog,
navigator = navigator,
state = bulkFavoriteState,
onDismiss = bulkFavoriteScreenModel::dismissDialog,
stopRunning = bulkFavoriteScreenModel::stopRunning,
toggleSelectionMode = bulkFavoriteScreenModel::toggleSelectionMode,
addFavorite = bulkFavoriteScreenModel::addFavorite,
showMigrateDialog = bulkFavoriteScreenModel::showMigrateDialog,
)
is Dialog.RemoveManga ->
RemoveMangaDialog(
dialog = dialog,
onDismiss = bulkFavoriteScreenModel::dismissDialog,
changeMangaFavorite = bulkFavoriteScreenModel::changeMangaFavorite,
)
is Dialog.Migrate ->
ShowMigrateDialog(
dialog = dialog,
navigator = navigator,
state = bulkFavoriteState,
onDismiss = bulkFavoriteScreenModel::dismissDialog,
stopRunning = bulkFavoriteScreenModel::stopRunning,
toggleSelection = bulkFavoriteScreenModel::toggleSelection,
addFavorite = bulkFavoriteScreenModel::addFavorite,
)
else -> {}
}
}
@Composable
private fun ShowMigrateDialog(
dialog: Dialog.Migrate,
navigator: Navigator?,
state: BulkFavoriteScreenModel.State,
onDismiss: () -> Unit,
stopRunning: () -> Unit,
toggleSelection: (Manga, toSelectedState: Boolean) -> Unit,
addFavorite: () -> Unit,
) {
stopRunning()
MigrateDialog(
oldManga = dialog.oldManga,
newManga = dialog.newManga,
screenModel = MigrateDialogScreenModel(),
onDismissRequest = onDismiss,
onClickTitle = { navigator?.push(MangaScreen(dialog.oldManga.id)) },
onPopScreen = {
toggleSelection(dialog.newManga, false)
onDismiss()
// `selection.size` is at current value before calling above `toggleSelection`
if (state.selection.size > 1) {
// Continue adding favorites
addFavorite()
}
},
)
}
/**
* Shows dialog to add a single manga to library when there are duplicates.
*/
@Composable
private fun AddDuplicateMangaDialog(
dialog: Dialog.AddDuplicateManga,
navigator: Navigator?,
state: BulkFavoriteScreenModel.State,
onDismiss: () -> Unit,
stopRunning: () -> Unit,
toggleSelectionMode: () -> Unit,
addFavorite: (Manga) -> Unit,
showMigrateDialog: (manga: Manga, duplicate: Manga) -> Unit,
) {
stopRunning()
DuplicateMangaDialog(
duplicates = dialog.duplicates,
onDismissRequest = onDismiss,
onConfirm = {
if (state.selectionMode) {
toggleSelectionMode()
}
addFavorite(dialog.manga)
},
onOpenManga = { navigator?.push(MangaScreen(it.id)) },
onMigrate = { showMigrateDialog(dialog.manga, it) },
)
}
@Composable
private fun RemoveMangaDialog(
dialog: Dialog.RemoveManga,
onDismiss: () -> Unit,
changeMangaFavorite: (Manga) -> Unit,
) {
RemoveMangaDialog(
onDismissRequest = onDismiss,
onConfirm = { changeMangaFavorite(dialog.manga) },
mangaToRemove = dialog.manga,
)
}
@Composable
private fun ChangeMangasCategoryDialog(
dialog: Dialog.ChangeMangasCategory,
navigator: Navigator?,
onDismiss: () -> Unit,
onConfirm: (List<Long>, List<Long>) -> Unit,
) {
ChangeCategoryDialog(
initialSelection = dialog.initialSelection,
onDismissRequest = onDismiss,
onEditCategories = { navigator?.push(CategoryScreen()) },
onConfirm = onConfirm,
)
}
/**
* Shows dialog to bulk allow/skip or migrate multiple manga to library when there are duplicates.
*/
@Composable
private fun BulkAllowDuplicateDialog(
dialog: Dialog.BulkAllowDuplicate,
navigator: Navigator?,
onDismiss: () -> Unit,
stopRunning: () -> Unit,
addFavorite: (startIdx: Int) -> Unit,
showMigrateDialog: (manga: Manga, duplicate: Manga) -> Unit,
addFavoriteDuplicate: (skipAllDuplicates: Boolean) -> Unit,
removeDuplicateSelectedManga: (index: Int) -> Unit,
) {
DuplicateMangaDialog(
duplicates = dialog.duplicates,
onDismissRequest = onDismiss,
onConfirm = { addFavorite(dialog.currentIdx + 1) },
onOpenManga = { navigator?.push(MangaScreen(it.id)) },
onMigrate = { showMigrateDialog(dialog.manga, it) },
bulkFavoriteManga = dialog.manga,
onAllowAllDuplicate = { addFavoriteDuplicate(false) },
onSkipAllDuplicate = { addFavoriteDuplicate(true) },
onSkipDuplicate = {
removeDuplicateSelectedManga(dialog.currentIdx)
addFavorite(dialog.currentIdx)
},
stopRunning = stopRunning,
)
}
@Composable
fun bulkSelectionButton(
isRunning: Boolean,
toggleSelectionMode: () -> Unit,
) = AppBar.Action(
title = stringResource(KMR.strings.action_bulk_select),
icon = Icons.Outlined.Checklist,
iconTint = MaterialTheme.colorScheme.primary.takeIf { isRunning },
onClick = toggleSelectionMode,
)

View file

@ -28,7 +28,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import eu.kanade.presentation.components.AppBarActions
import eu.kanade.presentation.components.SearchToolbar
import eu.kanade.tachiyomi.ui.browse.bulkSelectionButton
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SourceFilter
import kotlinx.collections.immutable.persistentListOf
import tachiyomi.i18n.MR

View file

@ -147,12 +147,7 @@ fun DuplicateMangaDialog(
DuplicateMangaListItem(
manga = it,
getSource = { sourceManager.getOrStub(it.source) },
onMigrate = {
// KMK -->
if (bulkFavoriteManga == null) onDismissRequest()
// KMK <--
onMigrate(it)
},
onMigrate = { onMigrate(it) },
onOpenManga = { onOpenManga(it) },
)
@ -184,7 +179,7 @@ fun DuplicateMangaDialog(
},
Modifier.align(Alignment.CenterHorizontally),
) {
Text(text = stringResource(KMR.strings.action_allow_duplicate_manga))
Text(text = stringResource(MR.strings.action_add_anyway))
}
TextButton(

View file

@ -1,12 +1,6 @@
package eu.kanade.tachiyomi.ui.browse
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Checklist
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.hapticfeedback.HapticFeedback
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.util.fastAny
@ -15,20 +9,11 @@ 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 cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.track.interactor.AddTracks
import eu.kanade.presentation.browse.components.RemoveMangaDialog
import eu.kanade.presentation.category.components.ChangeCategoryDialog
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.BulkSelectionToolbar
import eu.kanade.presentation.manga.DuplicateMangaDialog
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel.Dialog
import eu.kanade.tachiyomi.ui.browse.migration.advanced.design.PreMigrationScreen
import eu.kanade.tachiyomi.ui.category.CategoryScreen
import eu.kanade.tachiyomi.ui.manga.MangaScreen
import eu.kanade.tachiyomi.util.removeCovers
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.PersistentList
@ -43,7 +28,6 @@ import tachiyomi.core.common.preference.CheckboxState
import tachiyomi.core.common.preference.mapAsCheckboxState
import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.core.common.util.lang.launchNonCancellable
import tachiyomi.domain.UnsortedPreferences
import tachiyomi.domain.category.interactor.GetCategories
import tachiyomi.domain.category.interactor.SetMangaCategories
import tachiyomi.domain.category.model.Category
@ -53,8 +37,6 @@ import tachiyomi.domain.manga.interactor.GetDuplicateLibraryManga
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.toMangaUpdate
import tachiyomi.domain.source.service.SourceManager
import tachiyomi.i18n.kmk.KMR
import tachiyomi.presentation.core.i18n.stringResource
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.time.Instant
@ -76,11 +58,11 @@ class BulkFavoriteScreenModel(
toggleSelectionMode()
}
fun toggleSelectionMode() {
fun toggleSelectionMode(newMode: Boolean? = null) {
if (state.value.selectionMode) {
clearSelection()
}
mutableState.update { it.copy(selectionMode = !it.selectionMode) }
mutableState.update { it.copy(selectionMode = newMode ?: !it.selectionMode) }
}
private fun clearSelection() {
@ -97,10 +79,13 @@ class BulkFavoriteScreenModel(
fun toggleSelection(manga: Manga, toSelectedState: Boolean? = null) {
mutableState.update { state ->
val newSelection = state.selection.mutate { list ->
if (toSelectedState != true && list.fastAny { it.id == manga.id }) {
list.removeAll { it.id == manga.id }
} else if (toSelectedState != false && list.none { it.id == manga.id }) {
val isSelected = list.fastAny { it.id == manga.id }
val shouldSelect = toSelectedState ?: !isSelected
// Both condition to avoid adding duplicate entries
if (shouldSelect && !isSelected) {
list.add(manga)
} else if (!shouldSelect && isSelected) {
list.removeAll { it.id == manga.id }
}
}
state.copy(
@ -151,8 +136,11 @@ class BulkFavoriteScreenModel(
/**
* Add mangas to library if there is default category or no category exists.
* If not, it shows the categories list.
*
* @param skipAllDuplicates if true, skip all duplicates and add all selected mangas to library.
* if false, allow all duplicates and add all selected mangas to library
*/
fun addFavoriteDuplicate(skipAllDuplicates: Boolean = false) {
internal fun addFavoriteDuplicate(skipAllDuplicates: Boolean = false) {
screenModelScope.launch {
val mangaList = if (skipAllDuplicates) getNotDuplicateLibraryMangas() else state.value.selection
if (mangaList.isEmpty()) {
@ -216,7 +204,7 @@ class BulkFavoriteScreenModel(
return null
}
fun removeDuplicateSelectedManga(index: Int) {
internal fun removeDuplicateSelectedManga(index: Int) {
mutableState.update { state ->
val newSelection = state.selection.mutate { list ->
list.removeAt(index)
@ -232,7 +220,7 @@ class BulkFavoriteScreenModel(
* @param addCategories the categories to add for all mangas.
* @param removeCategories the categories to remove in all mangas.
*/
fun setMangasCategories(mangaList: List<Manga>, addCategories: List<Long>, removeCategories: List<Long>) {
internal fun setMangasCategories(mangaList: List<Manga>, addCategories: List<Long>, removeCategories: List<Long>) {
screenModelScope.launchNonCancellable {
startRunning()
mangaList.fastForEach { manga ->
@ -246,7 +234,7 @@ class BulkFavoriteScreenModel(
}
stopRunning()
}
toggleSelectionMode()
toggleSelectionMode(newMode = false)
}
private fun moveMangaToCategoriesAndAddToLibrary(manga: Manga, categories: List<Long>) {
@ -293,7 +281,7 @@ class BulkFavoriteScreenModel(
*
* @return List of categories, not including the default category
*/
suspend fun getCategories(): List<Category> {
private suspend fun getCategories(): List<Category> {
return getCategories.subscribe()
.firstOrNull()
?.filterNot { it.isSystemCategory }
@ -304,7 +292,7 @@ class BulkFavoriteScreenModel(
moveMangaToCategories(manga, categories.filter { it.id != 0L }.map { it.id })
}
fun moveMangaToCategories(manga: Manga, categoryIds: List<Long>) {
private fun moveMangaToCategories(manga: Manga, categoryIds: List<Long>) {
screenModelScope.launchIO {
setMangaCategories.await(
mangaId = manga.id,
@ -318,7 +306,7 @@ class BulkFavoriteScreenModel(
*
* @param manga the manga to update.
*/
fun changeMangaFavorite(manga: Manga) {
internal fun changeMangaFavorite(manga: Manga) {
val source = sourceManager.getOrStub(manga.source)
screenModelScope.launch {
@ -341,7 +329,7 @@ class BulkFavoriteScreenModel(
}
}
fun addFavorite(manga: Manga) {
internal fun addFavorite(manga: Manga) {
screenModelScope.launch {
val categories = getCategories()
val defaultCategoryId = libraryPreferences.defaultCategory().get()
@ -364,8 +352,8 @@ class BulkFavoriteScreenModel(
else -> {
val preselectedIds = getCategories.await(manga.id).map { it.id }
setDialog(
Dialog.ChangeMangaCategory(
manga,
Dialog.ChangeMangasCategory(
listOf(manga),
categories.mapAsCheckboxState { it.id in preselectedIds }.toImmutableList(),
),
)
@ -388,13 +376,17 @@ class BulkFavoriteScreenModel(
}
}
internal fun showMigrateDialog(manga: Manga, duplicate: Manga) {
setDialog(Dialog.Migrate(newManga = manga, oldManga = duplicate))
}
private fun setDialog(dialog: Dialog?) {
mutableState.update {
it.copy(dialog = dialog)
}
}
fun dismissDialog() {
internal fun dismissDialog() {
mutableState.update {
it.copy(dialog = null)
}
@ -406,20 +398,17 @@ class BulkFavoriteScreenModel(
}
}
fun stopRunning() {
internal fun stopRunning() {
mutableState.update {
it.copy(isRunning = false)
}
}
interface Dialog {
sealed interface Dialog {
data class Migrate(val newManga: Manga, val oldManga: Manga) : Dialog
data class AddDuplicateManga(val manga: Manga, val duplicates: List<Manga>) : Dialog
data class BulkAllowDuplicate(val manga: Manga, val duplicates: List<Manga>, val currentIdx: Int) : Dialog
data class RemoveManga(val manga: Manga) : Dialog
data class ChangeMangaCategory(
val manga: Manga,
val initialSelection: ImmutableList<CheckboxState.State<Category>>,
) : Dialog
data class ChangeMangasCategory(
val mangas: List<Manga>,
val initialSelection: ImmutableList<CheckboxState<Category>>,
@ -434,158 +423,3 @@ class BulkFavoriteScreenModel(
val isRunning: Boolean = false,
)
}
/**
* Compose to shows the bulk favorite dialogs.
*
* @param bulkFavoriteScreenModel the screen model.
* @param dialog the dialog to show.
*/
@Composable
fun BulkFavoriteDialogs(
bulkFavoriteScreenModel: BulkFavoriteScreenModel,
dialog: Dialog?,
) {
when (dialog) {
/* Bulk-favorite actions */
is Dialog.ChangeMangasCategory ->
ChangeMangasCategoryDialog(bulkFavoriteScreenModel)
is Dialog.BulkAllowDuplicate ->
BulkAllowDuplicateDialog(bulkFavoriteScreenModel)
/* Single-favorite actions for screens originally don't have it */
is Dialog.AddDuplicateManga ->
AddDuplicateMangaDialog(bulkFavoriteScreenModel)
is Dialog.RemoveManga ->
RemoveMangaDialog(bulkFavoriteScreenModel)
is Dialog.ChangeMangaCategory ->
ChangeMangaCategoryDialog(bulkFavoriteScreenModel)
}
}
/**
* Shows dialog to add a single manga to library when there are duplicates.
*
* @param bulkFavoriteScreenModel the screen model.
*/
@Composable
fun AddDuplicateMangaDialog(bulkFavoriteScreenModel: BulkFavoriteScreenModel) {
val navigator = LocalNavigator.currentOrThrow
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
val dialog = bulkFavoriteState.dialog as Dialog.AddDuplicateManga
bulkFavoriteScreenModel.stopRunning()
DuplicateMangaDialog(
duplicates = dialog.duplicates,
onDismissRequest = bulkFavoriteScreenModel::dismissDialog,
onConfirm = {
if (bulkFavoriteState.selectionMode) {
bulkFavoriteScreenModel.toggleSelectionMode()
}
bulkFavoriteScreenModel.addFavorite(dialog.manga)
},
onOpenManga = { navigator.push(MangaScreen(it.id)) },
onMigrate = {
PreMigrationScreen.navigateToMigration(
Injekt.get<UnsortedPreferences>().skipPreMigration().get(),
navigator,
it.id,
dialog.manga.id,
)
},
)
}
@Composable
fun RemoveMangaDialog(bulkFavoriteScreenModel: BulkFavoriteScreenModel) {
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
val dialog = bulkFavoriteState.dialog as Dialog.RemoveManga
RemoveMangaDialog(
onDismissRequest = bulkFavoriteScreenModel::dismissDialog,
onConfirm = {
bulkFavoriteScreenModel.changeMangaFavorite(dialog.manga)
},
mangaToRemove = dialog.manga,
)
}
@Composable
fun ChangeMangaCategoryDialog(bulkFavoriteScreenModel: BulkFavoriteScreenModel) {
val navigator = LocalNavigator.currentOrThrow
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
val dialog = bulkFavoriteState.dialog as Dialog.ChangeMangaCategory
ChangeCategoryDialog(
initialSelection = dialog.initialSelection,
onDismissRequest = bulkFavoriteScreenModel::dismissDialog,
onEditCategories = { navigator.push(CategoryScreen()) },
onConfirm = { include, _ ->
bulkFavoriteScreenModel.changeMangaFavorite(dialog.manga)
bulkFavoriteScreenModel.moveMangaToCategories(dialog.manga, include)
},
)
}
@Composable
fun ChangeMangasCategoryDialog(bulkFavoriteScreenModel: BulkFavoriteScreenModel) {
val navigator = LocalNavigator.currentOrThrow
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
val dialog = bulkFavoriteState.dialog as Dialog.ChangeMangasCategory
ChangeCategoryDialog(
initialSelection = dialog.initialSelection,
onDismissRequest = bulkFavoriteScreenModel::dismissDialog,
onEditCategories = { navigator.push(CategoryScreen()) },
onConfirm = { include, exclude ->
bulkFavoriteScreenModel.setMangasCategories(dialog.mangas, include, exclude)
},
)
}
/**
* Shows dialog to bulk allow/skip or migrate multiple manga to library when there are duplicates.
*
* @param bulkFavoriteScreenModel the screen model.
*/
@Composable
fun BulkAllowDuplicateDialog(bulkFavoriteScreenModel: BulkFavoriteScreenModel) {
val navigator = LocalNavigator.currentOrThrow
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
val dialog = bulkFavoriteState.dialog as Dialog.BulkAllowDuplicate
DuplicateMangaDialog(
duplicates = dialog.duplicates,
onDismissRequest = bulkFavoriteScreenModel::dismissDialog,
onConfirm = {
bulkFavoriteScreenModel.addFavorite(startIdx = dialog.currentIdx + 1)
},
onOpenManga = { navigator.push(MangaScreen(it.id)) },
onMigrate = { navigator.push(MangaScreen(it.id)) },
bulkFavoriteManga = dialog.manga,
onAllowAllDuplicate = bulkFavoriteScreenModel::addFavoriteDuplicate,
onSkipAllDuplicate = {
bulkFavoriteScreenModel.addFavoriteDuplicate(skipAllDuplicates = true)
},
onSkipDuplicate = {
bulkFavoriteScreenModel.removeDuplicateSelectedManga(index = dialog.currentIdx)
bulkFavoriteScreenModel.addFavorite(startIdx = dialog.currentIdx)
},
stopRunning = bulkFavoriteScreenModel::stopRunning,
)
}
@Composable
fun bulkSelectionButton(
isRunning: Boolean,
toggleSelectionMode: () -> Unit,
) = AppBar.Action(
title = stringResource(KMR.strings.action_bulk_select),
icon = Icons.Outlined.Checklist,
iconTint = MaterialTheme.colorScheme.primary.takeIf { isRunning },
onClick = toggleSelectionMode,
)

View file

@ -22,13 +22,13 @@ import eu.kanade.presentation.browse.FeedAddDialog
import eu.kanade.presentation.browse.FeedAddSearchDialog
import eu.kanade.presentation.browse.FeedOrderScreen
import eu.kanade.presentation.browse.FeedScreen
import eu.kanade.presentation.browse.components.BulkFavoriteDialogs
import eu.kanade.presentation.browse.components.FeedActionsDialog
import eu.kanade.presentation.browse.components.SourceFeedDeleteDialog
import eu.kanade.presentation.browse.components.bulkSelectionButton
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.TabContent
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteDialogs
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
import eu.kanade.tachiyomi.ui.browse.bulkSelectionButton
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreen
import eu.kanade.tachiyomi.ui.home.HomeScreen
import eu.kanade.tachiyomi.ui.manga.MangaScreen

View file

@ -102,10 +102,7 @@ internal fun MigrateDialog(
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.extraSmall),
) {
TextButton(
onClick = {
onDismissRequest()
onClickTitle()
},
onClick = { onClickTitle() },
) {
Text(text = stringResource(MR.strings.action_show_manga))
}

View file

@ -8,12 +8,15 @@ import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.presentation.browse.MigrateSearchScreen
import eu.kanade.presentation.browse.components.BulkFavoriteDialogs
import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteDialogs
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigrationListScreen
import eu.kanade.tachiyomi.ui.manga.MangaScreen
/**
* Manual search [validSources] for manga to migrate to.
*/
class MigrateSearchScreen(private val mangaId: Long, private val validSources: List<Long>) : Screen() {
@Composable
@ -73,6 +76,7 @@ class MigrateSearchScreen(private val mangaId: Long, private val validSources: L
)
// KMK -->
// Bulk-favorite actions only
BulkFavoriteDialogs(
bulkFavoriteScreenModel = bulkFavoriteScreenModel,
dialog = bulkFavoriteState.dialog,

View file

@ -16,14 +16,14 @@ import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.core.util.ifSourcesLoaded
import eu.kanade.presentation.browse.BrowseSourceContent
import eu.kanade.presentation.browse.components.BrowseSourceFloatingActionButton
import eu.kanade.presentation.browse.components.BulkFavoriteDialogs
import eu.kanade.presentation.browse.components.bulkSelectionButton
import eu.kanade.presentation.components.AppBarActions
import eu.kanade.presentation.components.BulkSelectionToolbar
import eu.kanade.presentation.components.SearchToolbar
import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteDialogs
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
import eu.kanade.tachiyomi.ui.browse.bulkSelectionButton
import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigrationListScreen
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel
import eu.kanade.tachiyomi.ui.browse.source.browse.SourceFilterDialog
@ -41,7 +41,7 @@ import tachiyomi.presentation.core.screens.LoadingScreen
import tachiyomi.source.local.LocalSource
/**
* Opened when click on a source in [MigrateSearchScreen]
* Opened when click on a source in [MigrateSearchScreen] while doing manual search for migration
*/
data class SourceSearchScreen(
private val oldManga: Manga,
@ -212,6 +212,7 @@ data class SourceSearchScreen(
}
// KMK -->
// Bulk-favorite actions only
BulkFavoriteDialogs(
bulkFavoriteScreenModel = bulkFavoriteScreenModel,
dialog = bulkFavoriteState.dialog,

View file

@ -40,6 +40,7 @@ import eu.kanade.core.util.ifSourcesLoaded
import eu.kanade.presentation.browse.BrowseSourceContent
import eu.kanade.presentation.browse.MissingSourceScreen
import eu.kanade.presentation.browse.components.BrowseSourceToolbar
import eu.kanade.presentation.browse.components.BulkFavoriteDialogs
import eu.kanade.presentation.browse.components.RemoveMangaDialog
import eu.kanade.presentation.browse.components.SavedSearchCreateDialog
import eu.kanade.presentation.browse.components.SavedSearchDeleteDialog
@ -52,7 +53,6 @@ import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteDialogs
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
import eu.kanade.tachiyomi.ui.browse.extension.details.SourcePreferencesScreen
import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateDialog
@ -463,9 +463,7 @@ data class BrowseSourceScreen(
screenModel = MigrateDialogScreenModel(),
onDismissRequest = onDismissRequest,
onClickTitle = { navigator.push(MangaScreen(dialog.oldManga.id)) },
onPopScreen = {
onDismissRequest()
},
onPopScreen = { onDismissRequest() },
)
}
is BrowseSourceScreenModel.Dialog.RemoveManga -> {
@ -504,6 +502,7 @@ data class BrowseSourceScreen(
}
// KMK -->
// Bulk-favorite actions only
BulkFavoriteDialogs(
bulkFavoriteScreenModel = bulkFavoriteScreenModel,
dialog = bulkFavoriteState.dialog,

View file

@ -19,6 +19,7 @@ import eu.kanade.presentation.browse.MissingSourceScreen
import eu.kanade.presentation.browse.SourceFeedOrderScreen
import eu.kanade.presentation.browse.SourceFeedScreen
import eu.kanade.presentation.browse.SourceFeedUI
import eu.kanade.presentation.browse.components.BulkFavoriteDialogs
import eu.kanade.presentation.browse.components.FeedActionsDialog
import eu.kanade.presentation.browse.components.SourceFeedAddDialog
import eu.kanade.presentation.browse.components.SourceFeedDeleteDialog
@ -27,7 +28,6 @@ import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteDialogs
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
import eu.kanade.tachiyomi.ui.browse.extension.details.SourcePreferencesScreen
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreen

View file

@ -14,8 +14,8 @@ import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.core.util.ifSourcesLoaded
import eu.kanade.presentation.browse.GlobalSearchScreen
import eu.kanade.presentation.browse.components.BulkFavoriteDialogs
import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteDialogs
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreen
import eu.kanade.tachiyomi.ui.manga.MangaScreen

View file

@ -141,7 +141,7 @@ data object HistoryTab : Tab {
screenModel = MigrateDialogScreenModel(),
onDismissRequest = onDismissRequest,
onClickTitle = { navigator.push(MangaScreen(dialog.oldManga.id)) },
onPopScreen = { navigator.replace(MangaScreen(dialog.newManga.id)) },
onPopScreen = { onDismissRequest() },
)
}
null -> {}

View file

@ -44,6 +44,7 @@ import dev.chrisbanes.haze.hazeEffect
import eu.kanade.core.util.ifSourcesLoaded
import eu.kanade.domain.manga.model.hasCustomCover
import eu.kanade.domain.manga.model.toSManga
import eu.kanade.presentation.browse.components.BulkFavoriteDialogs
import eu.kanade.presentation.category.components.ChangeCategoryDialog
import eu.kanade.presentation.components.NavigatorAdaptiveSheet
import eu.kanade.presentation.manga.ChapterSettingsDialog
@ -63,7 +64,6 @@ import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.isLocalOrStub
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteDialogs
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionsScreen
import eu.kanade.tachiyomi.ui.browse.extension.details.SourcePreferencesScreen
@ -470,7 +470,7 @@ class MangaScreen(
screenModel = MigrateDialogScreenModel(),
onDismissRequest = onDismissRequest,
onClickTitle = { navigator.push(MangaScreen(dialog.oldManga.id)) },
onPopScreen = { navigator.replace(MangaScreen(dialog.newManga.id)) },
onPopScreen = { onDismissRequest() },
)
}
MangaScreenModel.Dialog.SettingsSheet -> ChapterSettingsDialog(

View file

@ -17,12 +17,12 @@ import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.core.util.ifSourcesLoaded
import eu.kanade.presentation.browse.BrowseSourceContent
import eu.kanade.presentation.browse.components.BrowseSourceSimpleToolbar
import eu.kanade.presentation.browse.components.BulkFavoriteDialogs
import eu.kanade.presentation.browse.components.RemoveMangaDialog
import eu.kanade.presentation.category.components.ChangeCategoryDialog
import eu.kanade.presentation.components.BulkSelectionToolbar
import eu.kanade.presentation.manga.DuplicateMangaDialog
import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteDialogs
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateDialog
import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateDialogScreenModel
@ -175,9 +175,7 @@ class MangaDexFollowsScreen(private val sourceId: Long) : Screen() {
screenModel = MigrateDialogScreenModel(),
onDismissRequest = onDismissRequest,
onClickTitle = { navigator.push(MangaScreen(dialog.oldManga.id)) },
onPopScreen = {
onDismissRequest()
},
onPopScreen = { onDismissRequest() },
)
}
// KMK <--
@ -207,6 +205,7 @@ class MangaDexFollowsScreen(private val sourceId: Long) : Screen() {
}
// KMK -->
// Bulk-favorite actions only
BulkFavoriteDialogs(
bulkFavoriteScreenModel = bulkFavoriteScreenModel,
dialog = bulkFavoriteState.dialog,

View file

@ -16,9 +16,9 @@ import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.core.util.ifSourcesLoaded
import eu.kanade.presentation.browse.BrowseSourceContent
import eu.kanade.presentation.browse.components.BrowseSourceSimpleToolbar
import eu.kanade.presentation.browse.components.BulkFavoriteDialogs
import eu.kanade.presentation.components.BulkSelectionToolbar
import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteDialogs
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
import eu.kanade.tachiyomi.ui.browse.source.SourcesScreen
import eu.kanade.tachiyomi.ui.manga.MangaScreen

View file

@ -10,8 +10,8 @@ import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.core.util.ifSourcesLoaded
import eu.kanade.presentation.browse.components.BulkFavoriteDialogs
import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteDialogs
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
import eu.kanade.tachiyomi.ui.browse.source.SourcesScreen
import eu.kanade.tachiyomi.ui.manga.MangaScreen