From fa7b18590e703d87f655cd37688c944ec40c64f0 Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Wed, 9 Apr 2025 11:42:43 +0700 Subject: [PATCH] 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 --- .../presentation/browse/SourceFeedScreen.kt | 2 +- .../components/BrowseSourceSimpleToolbar.kt | 1 - .../browse/components/BrowseSourceToolbar.kt | 1 - .../browse/components/BulkFavoriteDialogs.kt | 225 ++++++++++++++++++ .../browse/components/GlobalSearchToolbar.kt | 1 - .../manga/DuplicateMangaDialog.kt | 9 +- .../ui/browse/BulkFavoriteScreenModel.kt | 224 +++-------------- .../tachiyomi/ui/browse/feed/FeedTab.kt | 4 +- .../browse/migration/search/MigrateDialog.kt | 5 +- .../migration/search/MigrateSearchScreen.kt | 6 +- .../migration/search/SourceSearchScreen.kt | 7 +- .../source/browse/BrowseSourceScreen.kt | 7 +- .../ui/browse/source/feed/SourceFeedScreen.kt | 2 +- .../source/globalsearch/GlobalSearchScreen.kt | 2 +- .../kanade/tachiyomi/ui/history/HistoryTab.kt | 2 +- .../kanade/tachiyomi/ui/manga/MangaScreen.kt | 4 +- .../exh/md/follows/MangaDexFollowsScreen.kt | 7 +- .../java/exh/recs/BrowseRecommendsScreen.kt | 2 +- .../main/java/exh/recs/RecommendsScreen.kt | 2 +- 19 files changed, 282 insertions(+), 231 deletions(-) create mode 100644 app/src/main/java/eu/kanade/presentation/browse/components/BulkFavoriteDialogs.kt diff --git a/app/src/main/java/eu/kanade/presentation/browse/SourceFeedScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/SourceFeedScreen.kt index f9f69722c..c14523b76 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/SourceFeedScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/SourceFeedScreen.kt @@ -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 diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceSimpleToolbar.kt b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceSimpleToolbar.kt index 3ca1aa983..7bcbee496 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceSimpleToolbar.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceSimpleToolbar.kt @@ -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 diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt index d05383529..6519891cb 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt @@ -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 diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/BulkFavoriteDialogs.kt b/app/src/main/java/eu/kanade/presentation/browse/components/BulkFavoriteDialogs.kt new file mode 100644 index 000000000..dd26b0344 --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/browse/components/BulkFavoriteDialogs.kt @@ -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, List) -> 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, +) diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchToolbar.kt b/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchToolbar.kt index 071e7a4a1..cb4304833 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchToolbar.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchToolbar.kt @@ -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 diff --git a/app/src/main/java/eu/kanade/presentation/manga/DuplicateMangaDialog.kt b/app/src/main/java/eu/kanade/presentation/manga/DuplicateMangaDialog.kt index a9ce2b3a2..6a07b8bc8 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/DuplicateMangaDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/DuplicateMangaDialog.kt @@ -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( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/BulkFavoriteScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/BulkFavoriteScreenModel.kt index b35f94ffe..f6481dc4f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/BulkFavoriteScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/BulkFavoriteScreenModel.kt @@ -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, addCategories: List, removeCategories: List) { + internal fun setMangasCategories(mangaList: List, addCategories: List, removeCategories: List) { screenModelScope.launchNonCancellable { startRunning() mangaList.fastForEach { manga -> @@ -246,7 +234,7 @@ class BulkFavoriteScreenModel( } stopRunning() } - toggleSelectionMode() + toggleSelectionMode(newMode = false) } private fun moveMangaToCategoriesAndAddToLibrary(manga: Manga, categories: List) { @@ -293,7 +281,7 @@ class BulkFavoriteScreenModel( * * @return List of categories, not including the default category */ - suspend fun getCategories(): List { + private suspend fun getCategories(): List { 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) { + private fun moveMangaToCategories(manga: Manga, categoryIds: List) { 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) : Dialog data class BulkAllowDuplicate(val manga: Manga, val duplicates: List, val currentIdx: Int) : Dialog data class RemoveManga(val manga: Manga) : Dialog - data class ChangeMangaCategory( - val manga: Manga, - val initialSelection: ImmutableList>, - ) : Dialog data class ChangeMangasCategory( val mangas: List, val initialSelection: ImmutableList>, @@ -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().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, -) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/feed/FeedTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/feed/FeedTab.kt index 280ead41f..8924c4345 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/feed/FeedTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/feed/FeedTab.kt @@ -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 diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt index 4fa6d677a..7198ffbb6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt @@ -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)) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreen.kt index d720e100a..2024aea6d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreen.kt @@ -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) : 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, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt index b09e7782b..722d971a1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt @@ -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, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt index e77bccff0..43a6a2b1e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt @@ -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, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedScreen.kt index fb3fd96d8..6c3eac64c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedScreen.kt @@ -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 diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreen.kt index 2db30432e..b7d073347 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreen.kt @@ -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 diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryTab.kt index 3ad45df57..af402c7f9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryTab.kt @@ -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 -> {} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt index 6f75d3cc1..9d347fbe5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt @@ -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( diff --git a/app/src/main/java/exh/md/follows/MangaDexFollowsScreen.kt b/app/src/main/java/exh/md/follows/MangaDexFollowsScreen.kt index 3b62f4411..9c0d9e5ca 100644 --- a/app/src/main/java/exh/md/follows/MangaDexFollowsScreen.kt +++ b/app/src/main/java/exh/md/follows/MangaDexFollowsScreen.kt @@ -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, diff --git a/app/src/main/java/exh/recs/BrowseRecommendsScreen.kt b/app/src/main/java/exh/recs/BrowseRecommendsScreen.kt index b43f9dc1f..12166260e 100644 --- a/app/src/main/java/exh/recs/BrowseRecommendsScreen.kt +++ b/app/src/main/java/exh/recs/BrowseRecommendsScreen.kt @@ -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 diff --git a/app/src/main/java/exh/recs/RecommendsScreen.kt b/app/src/main/java/exh/recs/RecommendsScreen.kt index 61d116dd2..caa60822e 100644 --- a/app/src/main/java/exh/recs/RecommendsScreen.kt +++ b/app/src/main/java/exh/recs/RecommendsScreen.kt @@ -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