diff --git a/app/src/main/java/eu/kanade/presentation/browse/FeedScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/FeedScreen.kt index 0abad3e11..54e1977b1 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/FeedScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/FeedScreen.kt @@ -71,6 +71,7 @@ fun FeedScreen( onClickManga: (Manga) -> Unit, // KMK --> onLongClickManga: (Manga) -> Unit, + selection: List, // KMK <-- onRefresh: () -> Unit, getMangaState: @Composable (Manga, CatalogueSource?) -> State, @@ -126,7 +127,7 @@ fun FeedScreen( onClickManga = onClickManga, // KMK --> onLongClickManga = onLongClickManga, - selection = state.selection, + selection = selection, // KMK <-- ) } @@ -239,7 +240,7 @@ fun RadioSelector( options: ImmutableList, selected: Int?, optionStrings: ImmutableList = remember { options.map { it.toString() }.toImmutableList() }, - onSelectOption: (Int) -> Unit /* KMK --> */ = {} /* KMK <-- */, + onSelectOption: (Int) -> Unit/* KMK --> */ = {},/* KMK <-- */ ) { Column(Modifier.verticalScroll(rememberScrollState())) { optionStrings.forEachIndexed { index, option -> diff --git a/app/src/main/java/eu/kanade/presentation/browse/GlobalSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/GlobalSearchScreen.kt index 2faa80563..bee20e7d2 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/GlobalSearchScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/GlobalSearchScreen.kt @@ -4,6 +4,8 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable import androidx.compose.runtime.State +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import eu.kanade.domain.source.model.installedExtension import eu.kanade.presentation.browse.components.GlobalSearchCardRow import eu.kanade.presentation.browse.components.GlobalSearchErrorResultItem @@ -35,19 +37,21 @@ fun GlobalSearchScreen( onClickItem: (Manga) -> Unit, onLongClickItem: (Manga) -> Unit, // KMK --> - bulkFavoriteState: BulkFavoriteScreenModel.State, - toggleSelectionMode: () -> Unit, - addFavorite: () -> Unit, + bulkFavoriteScreenModel: BulkFavoriteScreenModel, // KMK <-- ) { + // KMK --> + val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState() + // KMK <-- + Scaffold( topBar = { scrollBehavior -> // KMK --> if (bulkFavoriteState.selectionMode) SelectionToolbar( selectedCount = bulkFavoriteState.selection.size, - onClickClearSelection = toggleSelectionMode, - onChangeCategoryClicked = addFavorite, + onClickClearSelection = bulkFavoriteScreenModel::toggleSelectionMode, + onChangeCategoryClicked = bulkFavoriteScreenModel::addFavorite, ) else // KMK <-- @@ -64,7 +68,7 @@ fun GlobalSearchScreen( onToggleResults = onToggleResults, scrollBehavior = scrollBehavior, // KMK --> - toggleBulkSelectionMode = toggleSelectionMode, + toggleSelectionMode = bulkFavoriteScreenModel::toggleSelectionMode, // KMK <-- ) }, diff --git a/app/src/main/java/eu/kanade/presentation/browse/MigrateSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/MigrateSearchScreen.kt index 2823b5c9c..4714b86b6 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/MigrateSearchScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/MigrateSearchScreen.kt @@ -2,6 +2,8 @@ package eu.kanade.presentation.browse import androidx.compose.runtime.Composable import androidx.compose.runtime.State +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import eu.kanade.presentation.browse.components.GlobalSearchToolbar import eu.kanade.presentation.components.SelectionToolbar import eu.kanade.tachiyomi.source.CatalogueSource @@ -25,19 +27,21 @@ fun MigrateSearchScreen( onClickItem: (Manga) -> Unit, onLongClickItem: (Manga) -> Unit, // KMK --> - bulkFavoriteState: BulkFavoriteScreenModel.State, - toggleSelectionMode: () -> Unit, - addFavorite: () -> Unit, + bulkFavoriteScreenModel: BulkFavoriteScreenModel, // KMK <-- ) { + // KMK --> + val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState() + // KMK <-- + Scaffold( topBar = { scrollBehavior -> // KMK --> if (bulkFavoriteState.selectionMode) SelectionToolbar( selectedCount = bulkFavoriteState.selection.size, - onClickClearSelection = toggleSelectionMode, - onChangeCategoryClicked = addFavorite, + onClickClearSelection = bulkFavoriteScreenModel::toggleSelectionMode, + onChangeCategoryClicked = bulkFavoriteScreenModel::addFavorite, ) else // KMK <-- @@ -54,7 +58,7 @@ fun MigrateSearchScreen( onToggleResults = onToggleResults, scrollBehavior = scrollBehavior, // KMK --> - toggleBulkSelectionMode = toggleSelectionMode + toggleSelectionMode = bulkFavoriteScreenModel::toggleSelectionMode // KMK <-- ) }, 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 3ed1449ce..caf5c1597 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/SourceFeedScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/SourceFeedScreen.kt @@ -3,10 +3,14 @@ package eu.kanade.presentation.browse import androidx.compose.animation.Crossfade import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Checklist import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.State +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import eu.kanade.presentation.browse.components.BrowseSourceFloatingActionButton import eu.kanade.presentation.browse.components.GlobalSearchCardRow @@ -14,11 +18,12 @@ 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.SourceSettingsButton +import eu.kanade.presentation.components.AppBar +import eu.kanade.presentation.components.AppBarActions import eu.kanade.presentation.components.AppBarTitle import eu.kanade.presentation.components.SearchToolbar import eu.kanade.presentation.components.SelectionToolbar -import eu.kanade.tachiyomi.ui.browse.source.feed.SourceFeedScreenModel -import eu.kanade.tachiyomi.ui.browse.source.feed.SourceFeedState +import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import tachiyomi.domain.manga.model.Manga @@ -104,18 +109,21 @@ fun SourceFeedScreen( // KMK --> sourceId: Long, onLongClickManga: (Manga) -> Unit, - screenModel: SourceFeedScreenModel, - screenState: SourceFeedState, + bulkFavoriteScreenModel: BulkFavoriteScreenModel, // KMK <-- ) { + // KMK --> + val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState() + // KMK <-- + Scaffold( topBar = { scrollBehavior -> // KMK --> - if (screenState.selectionMode) + if (bulkFavoriteState.selectionMode) SelectionToolbar( - selectedCount = screenState.selection.size, - onClickClearSelection = screenModel::clearSelection, - onChangeCategoryClicked = screenModel::addFavorite, + selectedCount = bulkFavoriteState.selection.size, + onClickClearSelection = bulkFavoriteScreenModel::toggleSelectionMode, + onChangeCategoryClicked = bulkFavoriteScreenModel::addFavorite, ) else // KMK <-- @@ -127,6 +135,7 @@ fun SourceFeedScreen( onClickSearch = onClickSearch, // KMK --> sourceId = sourceId, + toggleSelectionMode = bulkFavoriteScreenModel::toggleSelectionMode, // KMK <-- ) }, @@ -152,7 +161,7 @@ fun SourceFeedScreen( onClickManga = onClickManga, // KMK --> onLongClickManga = onLongClickManga, - screenState = screenState, + selection = bulkFavoriteState.selection, // KMK <-- ) } @@ -173,7 +182,7 @@ fun SourceFeedList( onClickManga: (Manga) -> Unit, // KMK --> onLongClickManga: (Manga) -> Unit, - screenState: SourceFeedState, + selection: List, // KMK <-- ) { ScrollbarLazyColumn( @@ -208,7 +217,7 @@ fun SourceFeedList( onClickManga = onClickManga, // KMK --> onLongClickManga = onLongClickManga, - selection = screenState.selection, + selection = selection, // KMK <-- ) } @@ -257,6 +266,7 @@ fun SourceFeedToolbar( onClickSearch: (String) -> Unit, // KMK --> sourceId: Long, + toggleSelectionMode: () -> Unit, // KMK <-- ) { SearchToolbar( @@ -269,6 +279,19 @@ fun SourceFeedToolbar( placeholderText = stringResource(MR.strings.action_search_hint), // KMK --> actions = { + AppBarActions( + actions = persistentListOf().builder() + .apply { + add( + AppBar.Action( + title = stringResource(MR.strings.action_bulk_select), + icon = Icons.Outlined.Checklist, + onClick = toggleSelectionMode, + ), + ) + } + .build(), + ) persistentListOf( SourceSettingsButton(sourceId), ) 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 5981ae015..b158f1847 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 @@ -42,7 +42,7 @@ fun BrowseSourceToolbar( onSearch: (String) -> Unit, scrollBehavior: TopAppBarScrollBehavior? = null, // KMK --> - toggleBulkSelectionMode: () -> Unit, + toggleSelectionMode: () -> Unit, // KMK <-- ) { // Avoid capturing unstable source in actions lambda @@ -81,7 +81,7 @@ fun BrowseSourceToolbar( AppBar.Action( title = stringResource(MR.strings.action_bulk_select), icon = Icons.Outlined.Checklist, - onClick = toggleBulkSelectionMode, + onClick = toggleSelectionMode, ), ) // KMK <-- 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 3dbc87fce..4b8980f0a 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 @@ -50,7 +50,7 @@ fun GlobalSearchToolbar( onToggleResults: () -> Unit, scrollBehavior: TopAppBarScrollBehavior, // KMK --> - toggleBulkSelectionMode: () -> Unit, + toggleSelectionMode: () -> Unit, // KMK <-- ) { Column(modifier = Modifier.background(MaterialTheme.colorScheme.surface)) { @@ -71,7 +71,7 @@ fun GlobalSearchToolbar( AppBar.Action( title = stringResource(MR.strings.action_bulk_select), icon = Icons.Outlined.Checklist, - onClick = toggleBulkSelectionMode, + onClick = toggleSelectionMode, ), ) } diff --git a/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt b/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt index bda9e86c2..7e44c2d35 100644 --- a/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt @@ -24,7 +24,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.zIndex import dev.icerock.moko.resources.StringResource -import eu.kanade.tachiyomi.ui.browse.feed.FeedScreenModel +import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.launch @@ -40,7 +40,7 @@ fun TabbedScreen( searchQuery: String? = null, onChangeSearchQuery: (String?) -> Unit = {}, // KMK --> - screenModel: FeedScreenModel, + bulkFavoriteScreenModel: BulkFavoriteScreenModel, // KMK <-- ) { val scope = rememberCoroutineScope() @@ -48,7 +48,7 @@ fun TabbedScreen( val snackbarHostState = remember { SnackbarHostState() } // KMK --> - val screenState by screenModel.state.collectAsState() + val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState() // KMK <-- LaunchedEffect(startIndex) { @@ -62,11 +62,11 @@ fun TabbedScreen( val tab = tabs[state.currentPage] val searchEnabled = tab.searchEnabled // KMK --> - if (screenState.selectionMode) + if (bulkFavoriteState.selectionMode) SelectionToolbar( - selectedCount = screenState.selection.size, - onClickClearSelection = screenModel::clearSelection, - onChangeCategoryClicked = screenModel::addFavorite, + selectedCount = bulkFavoriteState.selection.size, + onClickClearSelection = bulkFavoriteScreenModel::toggleSelectionMode, + onChangeCategoryClicked = bulkFavoriteScreenModel::addFavorite, ) else // KMK <-- diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowseTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowseTab.kt index 0ee52b0c2..0302a2d4d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowseTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowseTab.kt @@ -21,7 +21,6 @@ import eu.kanade.presentation.util.Tab import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.browse.extension.ExtensionsScreenModel import eu.kanade.tachiyomi.ui.browse.extension.extensionsTab -import eu.kanade.tachiyomi.ui.browse.feed.FeedScreenModel import eu.kanade.tachiyomi.ui.browse.feed.feedTab import eu.kanade.tachiyomi.ui.browse.migration.sources.migrateSourceTab import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchScreen @@ -67,7 +66,7 @@ data class BrowseTab( val extensionsState by extensionsScreenModel.state.collectAsState() // KMK --> - val feedScreenModel = rememberScreenModel { FeedScreenModel() } + val bulkFavoriteScreenModel = rememberScreenModel { BulkFavoriteScreenModel() } // KMK <-- TabbedScreen( @@ -81,7 +80,7 @@ data class BrowseTab( ) } else if (feedTabInFront) { persistentListOf( - feedTab(/* KMK --> */feedScreenModel/* KMK <-- */), + feedTab(/* KMK --> */bulkFavoriteScreenModel/* KMK <-- */), sourcesTab(), extensionsTab(extensionsScreenModel), migrateSourceTab(), @@ -89,7 +88,7 @@ data class BrowseTab( } else { persistentListOf( sourcesTab(), - feedTab(/* KMK --> */feedScreenModel/* KMK <-- */), + feedTab(/* KMK --> */bulkFavoriteScreenModel/* KMK <-- */), extensionsTab(extensionsScreenModel), migrateSourceTab(), ) @@ -99,7 +98,7 @@ data class BrowseTab( searchQuery = extensionsState.searchQuery, onChangeSearchQuery = extensionsScreenModel::search, // KMK --> - screenModel = feedScreenModel, + bulkFavoriteScreenModel = bulkFavoriteScreenModel, // KMK <-- ) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/feed/FeedScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/feed/FeedScreenModel.kt index 9bd8dfc4f..8197def21 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/feed/FeedScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/feed/FeedScreenModel.kt @@ -4,8 +4,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.runtime.produceState import androidx.compose.ui.util.fastAny -import androidx.compose.ui.util.fastForEach -import androidx.compose.ui.util.fastForEachIndexed import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.screenModelScope import eu.kanade.domain.manga.interactor.UpdateManga @@ -16,8 +14,6 @@ import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.util.system.LocaleHelper import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.PersistentList -import kotlinx.collections.immutable.mutate import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.asCoroutineDispatcher @@ -27,7 +23,6 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.receiveAsFlow @@ -35,15 +30,9 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.serialization.json.Json -import tachiyomi.core.common.preference.CheckboxState import tachiyomi.core.common.util.lang.launchIO import tachiyomi.core.common.util.lang.launchNonCancellable import tachiyomi.core.common.util.lang.withIOContext -import tachiyomi.domain.category.interactor.GetCategories -import tachiyomi.domain.category.interactor.SetMangaCategories -import tachiyomi.domain.category.model.Category -import tachiyomi.domain.library.service.LibraryPreferences -import tachiyomi.domain.manga.interactor.GetDuplicateLibraryManga import tachiyomi.domain.manga.interactor.GetManga import tachiyomi.domain.manga.interactor.NetworkToLocalManga import tachiyomi.domain.source.interactor.CountFeedSavedSearchGlobal @@ -76,12 +65,6 @@ open class FeedScreenModel( private val getSavedSearchBySourceId: GetSavedSearchBySourceId = Injekt.get(), private val insertFeedSavedSearch: InsertFeedSavedSearch = Injekt.get(), private val deleteFeedSavedSearchById: DeleteFeedSavedSearchById = Injekt.get(), - // KMK --> - private val getCategories: GetCategories = Injekt.get(), - private val setMangaCategories: SetMangaCategories = Injekt.get(), - private val libraryPreferences: LibraryPreferences = Injekt.get(), - private val getDuplicateLibraryManga: GetDuplicateLibraryManga = Injekt.get(), - // KMK <-- ) : StateScreenModel(FeedScreenState()) { private val _events = Channel(Int.MAX_VALUE) @@ -322,188 +305,10 @@ open class FeedScreenModel( mutableState.update { it.copy(dialog = null) } } - // KMK --> - fun clearSelection() { - mutableState.update { it.copy(selection = persistentListOf()) } - } - - fun toggleSelection(manga: DomainManga) { - mutableState.update { state -> - val newSelection = state.selection.mutate { list -> - if (list.fastAny { it.id == manga.id }) { - list.removeAll { it.id == manga.id } - } else { - list.add(manga) - } - } - state.copy(selection = newSelection) - } - } - - fun addFavorite(startIdx: Int = 0) { - screenModelScope.launch { - val mangaWithDup = getDuplicateLibraryManga(startIdx) - if (mangaWithDup != null) - setDialog(Dialog.AllowDuplicate(mangaWithDup)) - else - addFavoriteDuplicate() - } - } - - fun addFavoriteDuplicate(skipAllDuplicates: Boolean = false) { - screenModelScope.launch { - val mangaList = if (skipAllDuplicates) getNotDuplicateLibraryMangas() else state.value.selection - val categories = getCategories() - val defaultCategoryId = libraryPreferences.defaultCategory().get() - val defaultCategory = categories.find { it.id == defaultCategoryId.toLong() } - - when { - // Default category set - defaultCategory != null -> { - setMangaCategories(mangaList, listOf(defaultCategory.id), emptyList()) - } - - // Automatic 'Default' or no categories - defaultCategoryId == 0 || categories.isEmpty() -> { - // Automatic 'Default' or no categories - setMangaCategories(mangaList, emptyList(), emptyList()) - } - - else -> { - // Get indexes of the common categories to preselect. - val common = getCommonCategories(mangaList) - // Get indexes of the mix categories to preselect. - val mix = getMixCategories(mangaList) - val preselected = categories - .map { - when (it) { - in common -> CheckboxState.State.Checked(it) - in mix -> CheckboxState.TriState.Exclude(it) - else -> CheckboxState.State.None(it) - } - } - .toImmutableList() - setDialog(Dialog.ChangeMangasCategory(mangaList, preselected)) - } - } - } - } - - private suspend fun getNotDuplicateLibraryMangas(): List { - return state.value.selection.filterNot { manga -> - getDuplicateLibraryManga.await(manga).isNotEmpty() - } - } - - private suspend fun getDuplicateLibraryManga(startIdx: Int = 0): Pair? { - 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. - * - * @param mangaList the list of manga to move. - * @param addCategories the categories to add for all mangas. - * @param removeCategories the categories to remove in all mangas. - */ - fun setMangaCategories(mangaList: List, addCategories: List, removeCategories: List) { - screenModelScope.launchNonCancellable { - mangaList.fastForEach { manga -> - val categoryIds = getCategories.await(manga.id) - .map { it.id } - .subtract(removeCategories.toSet()) - .plus(addCategories) - .toList() - - moveMangaToCategoriesAndAddToLibrary(manga, categoryIds) - } - } - clearSelection() - } - - private fun moveMangaToCategoriesAndAddToLibrary(manga: DomainManga, categories: List) { - moveMangaToCategory(manga.id, categories) - if (manga.favorite) return - - screenModelScope.launchIO { - updateManga.awaitUpdateFavorite(manga.id, true) - } - } - - private fun moveMangaToCategory(mangaId: Long, categoryIds: List) { - screenModelScope.launchIO { - setMangaCategories.await(mangaId, categoryIds) - } - } - - /** - * Get user categories. - * - * @return List of categories, not including the default category - */ - suspend fun getCategories(): List { - return getCategories.subscribe() - .firstOrNull() - ?.filterNot { it.isSystemCategory } - .orEmpty() - } - - /** - * Returns the common categories for the given list of manga. - * - * @param mangas the list of manga. - */ - private suspend fun getCommonCategories(mangas: List): Collection { - if (mangas.isEmpty()) return emptyList() - return mangas - .map { getCategories.await(it.id).toSet() } - .reduce { set1, set2 -> set1.intersect(set2) } - } - - /** - * Returns the mix (non-common) categories for the given list of manga. - * - * @param mangas the list of manga. - */ - private suspend fun getMixCategories(mangas: List): Collection { - if (mangas.isEmpty()) return emptyList() - val mangaCategories = mangas.map { getCategories.await(it.id).toSet() } - val common = mangaCategories.reduce { set1, set2 -> set1.intersect(set2) } - return mangaCategories.flatten().distinct().subtract(common) - } - - private fun setDialog(dialog: Dialog?) { - mutableState.update { it.copy(dialog = dialog) } - } - // KMK <-- - sealed class Dialog { data class AddFeed(val options: ImmutableList) : Dialog() data class AddFeedSearch(val source: CatalogueSource, val options: ImmutableList) : Dialog() data class DeleteFeed(val feed: FeedSavedSearch) : Dialog() - // KMK --> - data class ChangeMangasCategory( - val mangas: List, - val initialSelection: ImmutableList>, - ) : Dialog() - data class AllowDuplicate(val duplicatedManga: Pair) : Dialog() - // KMK <-- } sealed class Event { @@ -515,9 +320,6 @@ open class FeedScreenModel( data class FeedScreenState( val dialog: FeedScreenModel.Dialog? = null, val items: List? = null, - // KMK --> - val selection: PersistentList = persistentListOf(), - // KMK <-- ) { val isLoading get() = items == null @@ -527,10 +329,6 @@ data class FeedScreenState( val isLoadingItems get() = items?.fastAny { it.results == null } != false - - // KMK --> - val selectionMode = selection.isNotEmpty() - // KMK <-- } const val MAX_FEED_ITEMS = 20 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 71617924e..4e0ef4778 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 @@ -2,16 +2,17 @@ package eu.kanade.tachiyomi.ui.browse.feed import androidx.activity.compose.BackHandler import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Bookmark import androidx.compose.material.icons.outlined.Add -import androidx.compose.material.icons.outlined.Bookmark +import androidx.compose.material.icons.outlined.Checklist import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalHapticFeedback +import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.core.stack.StackEvent import cafe.adriel.voyager.navigator.LocalNavigator @@ -20,10 +21,13 @@ import eu.kanade.presentation.browse.FeedAddDialog import eu.kanade.presentation.browse.FeedAddSearchDialog import eu.kanade.presentation.browse.FeedDeleteConfirmDialog import eu.kanade.presentation.browse.FeedScreen +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.TabContent import eu.kanade.presentation.manga.AllowDuplicateDialog +import eu.kanade.presentation.manga.DuplicateMangaDialog +import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreen import eu.kanade.tachiyomi.ui.category.CategoryScreen import eu.kanade.tachiyomi.ui.home.HomeScreen @@ -31,6 +35,7 @@ import eu.kanade.tachiyomi.ui.manga.MangaScreen import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch +import tachiyomi.core.common.util.lang.launchIO import tachiyomi.domain.source.interactor.GetRemoteManga import tachiyomi.i18n.MR import tachiyomi.i18n.sy.SYMR @@ -39,16 +44,26 @@ import tachiyomi.presentation.core.i18n.stringResource @Composable fun Screen.feedTab( // KMK --> - screenModel: FeedScreenModel + bulkFavoriteScreenModel: BulkFavoriteScreenModel, // KMK <-- ): TabContent { val navigator = LocalNavigator.currentOrThrow - /* KMK --> val screenModel = rememberScreenModel { FeedScreenModel() } - KMK <-- */ val state by screenModel.state.collectAsState() + // KMK --> + val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState() + + val scope = rememberCoroutineScope() val haptic = LocalHapticFeedback.current + + BackHandler(enabled = bulkFavoriteState.selectionMode) { + bulkFavoriteScreenModel.backHandler() + } + + LaunchedEffect(bulkFavoriteState.selectionMode) { + HomeScreen.showBottomNav(!bulkFavoriteState.selectionMode) + } // KMK <-- DisposableEffect(navigator.lastEvent) { @@ -65,46 +80,24 @@ fun Screen.feedTab( } } - // KMK --> - BackHandler(enabled = state.selectionMode) { - when { - state.selectionMode -> screenModel.clearSelection() - } - } - - LaunchedEffect(state.selectionMode, state.dialog) { - HomeScreen.showBottomNav(!state.selectionMode) - } - // KMK <-- - return TabContent( titleRes = SYMR.strings.feed, - actions = - // KMK --> - if (state.selectionMode) - persistentListOf( - AppBar.Action( - title = stringResource(MR.strings.action_select_all), - icon = Icons.Outlined.Bookmark, - onClick = { }, - ), - AppBar.Action( - title = stringResource(MR.strings.action_select_inverse), - icon = Icons.Filled.Bookmark, - onClick = { }, - ), - ) - else - // KMK <-- - persistentListOf( - AppBar.Action( - title = stringResource(MR.strings.action_add), - icon = Icons.Outlined.Add, - onClick = { - screenModel.openAddDialog() - }, - ), + actions = persistentListOf( + AppBar.Action( + title = stringResource(MR.strings.action_add), + icon = Icons.Outlined.Add, + onClick = { + screenModel.openAddDialog() + }, ), + // KMK --> + AppBar.Action( + title = stringResource(MR.strings.action_bulk_select), + icon = Icons.Outlined.Checklist, + onClick = bulkFavoriteScreenModel::toggleSelectionMode, + ), + // KMK <-- + ), content = { contentPadding, snackbarHostState -> FeedScreen( state = state, @@ -136,29 +129,38 @@ fun Screen.feedTab( onClickDelete = screenModel::openDeleteDialog, onClickManga = { manga -> // KMK --> - if (state.selectionMode) - screenModel.toggleSelection(manga) + if (bulkFavoriteState.selectionMode) + bulkFavoriteScreenModel.toggleSelection(manga) else - // KMK <-- + // KMK <-- navigator.push(MangaScreen(manga.id, true)) }, // KMK --> onLongClickManga = { manga -> - if (state.selectionMode) { + if (!bulkFavoriteState.selectionMode) + scope.launchIO { + val duplicateManga = bulkFavoriteScreenModel.getDuplicateLibraryManga(manga) + when { + manga.favorite -> bulkFavoriteScreenModel.setDialog(BulkFavoriteScreenModel.Dialog.RemoveManga(manga)) + duplicateManga != null -> bulkFavoriteScreenModel.setDialog( + BulkFavoriteScreenModel.Dialog.AddDuplicateManga( + manga, + duplicateManga, + ), + ) + else -> bulkFavoriteScreenModel.addFavorite(manga) + } + haptic.performHapticFeedback(HapticFeedbackType.LongPress) + } + else navigator.push(MangaScreen(manga.id, true)) - } else { - screenModel.toggleSelection(manga) - haptic.performHapticFeedback(HapticFeedbackType.LongPress) - } }, + selection = bulkFavoriteState.selection, // KMK <-- onRefresh = screenModel::init, getMangaState = { manga, source -> screenModel.getManga(initialManga = manga, source = source) }, ) - // KMK --> - val onDismissRequest = screenModel::dismissDialog - // KMK <-- state.dialog?.let { dialog -> when (dialog) { is FeedScreenModel.Dialog.AddFeed -> { @@ -194,43 +196,75 @@ fun Screen.feedTab( }, ) } - // KMK --> - is FeedScreenModel.Dialog.ChangeMangasCategory -> { - ChangeCategoryDialog( - initialSelection = dialog.initialSelection, - onDismissRequest = onDismissRequest, - onEditCategories = { navigator.push(CategoryScreen()) }, - onConfirm = { include, exclude -> - screenModel.setMangaCategories(dialog.mangas, include, exclude) - }, - ) - } - 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 --> + val onBulkDismissRequest = { bulkFavoriteScreenModel.setDialog(null) } + when (val dialog = bulkFavoriteState.dialog) { + is BulkFavoriteScreenModel.Dialog.AddDuplicateManga -> { + DuplicateMangaDialog( + onDismissRequest = onBulkDismissRequest, + onConfirm = { bulkFavoriteScreenModel.addFavorite(dialog.manga) }, + onOpenManga = { navigator.push(MangaScreen(dialog.duplicate.id)) }, + ) + } + is BulkFavoriteScreenModel.Dialog.RemoveManga -> { + RemoveMangaDialog( + onDismissRequest = onBulkDismissRequest, + onConfirm = { + bulkFavoriteScreenModel.changeMangaFavorite(dialog.manga) + }, + mangaToRemove = dialog.manga, + ) + } + is BulkFavoriteScreenModel.Dialog.ChangeMangaCategory -> { + ChangeCategoryDialog( + initialSelection = dialog.initialSelection, + onDismissRequest = onBulkDismissRequest, + onEditCategories = { navigator.push(CategoryScreen()) }, + onConfirm = { include, _ -> + bulkFavoriteScreenModel.changeMangaFavorite(dialog.manga) + bulkFavoriteScreenModel.moveMangaToCategories(dialog.manga, include) + }, + ) + } + is BulkFavoriteScreenModel.Dialog.ChangeMangasCategory -> { + ChangeCategoryDialog( + initialSelection = dialog.initialSelection, + onDismissRequest = onBulkDismissRequest, + onEditCategories = { navigator.push(CategoryScreen()) }, + onConfirm = { include, exclude -> + bulkFavoriteScreenModel.setMangasCategories(dialog.mangas, include, exclude) + }, + ) + } + is BulkFavoriteScreenModel.Dialog.AllowDuplicate -> { + AllowDuplicateDialog( + onDismissRequest = onBulkDismissRequest, + onAllowAllDuplicate = { + bulkFavoriteScreenModel.addFavoriteDuplicate() + }, + onSkipAllDuplicate = { + bulkFavoriteScreenModel.addFavoriteDuplicate(skipAllDuplicates = true) + }, + onOpenManga = { + navigator.push(MangaScreen(dialog.duplicatedManga.second.id)) + }, + onAllowDuplicate = { + bulkFavoriteScreenModel.addFavorite(startIdx = dialog.duplicatedManga.first + 1) + }, + onSkipDuplicate = { + bulkFavoriteScreenModel.removeDuplicateSelectedManga(index = dialog.duplicatedManga.first) + bulkFavoriteScreenModel.addFavorite(startIdx = dialog.duplicatedManga.first) + }, + duplicatedName = dialog.duplicatedManga.second.title, + ) + } + else -> {} + } + // KMK <-- + val internalErrString = stringResource(MR.strings.internal_error) val tooManyFeedsString = stringResource(SYMR.strings.too_many_in_feed) LaunchedEffect(Unit) { 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 2096ad42e..d9a540ab3 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 @@ -53,12 +53,10 @@ class MigrateSearchScreen(private val mangaId: Long, private val validSources: L }, onClickItem = { // KMK --> - if (bulkFavoriteState.selectionMode) { + if (bulkFavoriteState.selectionMode) bulkFavoriteScreenModel.toggleSelection(it) - } - else - // KMK <-- - { + else { + // KMK <-- // SY --> navigator.items .filterIsInstance() @@ -70,9 +68,7 @@ class MigrateSearchScreen(private val mangaId: Long, private val validSources: L }, onLongClickItem = { navigator.push(MangaScreen(it.id, true)) }, // KMK --> - bulkFavoriteState = bulkFavoriteState, - toggleSelectionMode = bulkFavoriteScreenModel::toggleSelectionMode, - addFavorite = bulkFavoriteScreenModel::addFavorite, + bulkFavoriteScreenModel = bulkFavoriteScreenModel, // KMK <-- ) 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 99e86b210..327db09f5 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 @@ -51,6 +51,7 @@ import eu.kanade.presentation.util.AssistContentScreen import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.online.HttpSource +import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel import eu.kanade.tachiyomi.ui.browse.extension.details.SourcePreferencesScreen import eu.kanade.tachiyomi.ui.browse.source.SourcesScreen import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel.Listing @@ -137,10 +138,11 @@ data class BrowseSourceScreen( } // KMK --> - BackHandler(enabled = state.selectionMode) { - when { - state.selectionMode -> screenModel.toggleSelectionMode() - } + val bulkFavoriteScreenModel = rememberScreenModel { BulkFavoriteScreenModel() } + val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState() + + BackHandler(enabled = bulkFavoriteState.selectionMode) { + bulkFavoriteScreenModel.backHandler() } // KMK <-- @@ -152,11 +154,11 @@ data class BrowseSourceScreen( topBar = { Column(modifier = Modifier.background(MaterialTheme.colorScheme.surface)) { // KMK --> - if (state.selectionMode) + if (bulkFavoriteState.selectionMode) SelectionToolbar( - selectedCount = state.selection.size, - onClickClearSelection = screenModel::toggleSelectionMode, - onChangeCategoryClicked = screenModel::addFavorite, + selectedCount = bulkFavoriteState.selection.size, + onClickClearSelection = bulkFavoriteScreenModel::toggleSelectionMode, + onChangeCategoryClicked = bulkFavoriteScreenModel::addFavorite, ) else // KMK <-- @@ -172,7 +174,7 @@ data class BrowseSourceScreen( onSettingsClick = { navigator.push(SourcePreferencesScreen(sourceId)) }, onSearch = screenModel::search, // KMK --> - toggleBulkSelectionMode = screenModel::toggleSelectionMode + toggleSelectionMode = bulkFavoriteScreenModel::toggleSelectionMode // KMK <-- ) @@ -269,18 +271,18 @@ data class BrowseSourceScreen( onLocalSourceHelpClick = onHelpClick, onMangaClick = { // KMK --> - if (state.selectionMode) - screenModel.toggleSelection(it) + if (bulkFavoriteState.selectionMode) + bulkFavoriteScreenModel.toggleSelection(it) else // KMK <-- navigator.push(MangaScreen(it.id, true, smartSearchConfig)) }, onMangaLongClick = { manga -> // KMK --> - if (state.selectionMode) { + if (bulkFavoriteState.selectionMode) navigator.push(MangaScreen(manga.id, true)) - } else { - // KMK <-- + else + // KMK <-- scope.launchIO { val duplicateManga = screenModel.getDuplicateLibraryManga(manga) when { @@ -294,11 +296,10 @@ data class BrowseSourceScreen( else -> screenModel.addFavorite(manga) } haptic.performHapticFeedback(HapticFeedbackType.LongPress) - } - } + } }, // KMK --> - selection = state.selection, + selection = bulkFavoriteState.selection, // KMK <-- ) } @@ -385,42 +386,47 @@ data class BrowseSourceScreen( screenModel.deleteSearch(dialog.idToDelete) }, ) - // KMK --> - is BrowseSourceScreenModel.Dialog.ChangeMangasCategory -> { + else -> {} + } + + // KMK --> + val onBulkDismissRequest = { bulkFavoriteScreenModel.setDialog(null) } + when (val dialog = bulkFavoriteState.dialog) { + is BulkFavoriteScreenModel.Dialog.ChangeMangasCategory -> { ChangeCategoryDialog( initialSelection = dialog.initialSelection, - onDismissRequest = onDismissRequest, + onDismissRequest = onBulkDismissRequest, onEditCategories = { navigator.push(CategoryScreen()) }, onConfirm = { include, exclude -> - screenModel.setMangaCategories(dialog.mangas, include, exclude) + bulkFavoriteScreenModel.setMangasCategories(dialog.mangas, include, exclude) }, ) } - is BrowseSourceScreenModel.Dialog.AllowDuplicate -> { + is BulkFavoriteScreenModel.Dialog.AllowDuplicate -> { AllowDuplicateDialog( - onDismissRequest = onDismissRequest, + onDismissRequest = onBulkDismissRequest, onAllowAllDuplicate = { - screenModel.addFavoriteDuplicate() + bulkFavoriteScreenModel.addFavoriteDuplicate() }, onSkipAllDuplicate = { - screenModel.addFavoriteDuplicate(skipAllDuplicates = true) + bulkFavoriteScreenModel.addFavoriteDuplicate(skipAllDuplicates = true) }, onOpenManga = { navigator.push(MangaScreen(dialog.duplicatedManga.second.id)) }, onAllowDuplicate = { - screenModel.addFavorite(startIdx = dialog.duplicatedManga.first + 1) + bulkFavoriteScreenModel.addFavorite(startIdx = dialog.duplicatedManga.first + 1) }, onSkipDuplicate = { - screenModel.removeDuplicateSelectedManga(index = dialog.duplicatedManga.first) - screenModel.addFavorite(startIdx = dialog.duplicatedManga.first) + bulkFavoriteScreenModel.removeDuplicateSelectedManga(index = dialog.duplicatedManga.first) + bulkFavoriteScreenModel.addFavorite(startIdx = dialog.duplicatedManga.first) }, duplicatedName = dialog.duplicatedManga.second.title, ) } - // KMK <-- else -> {} } + // KMK <-- LaunchedEffect(Unit) { queryEvent.receiveAsFlow() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt index b97f445de..5d42384e0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt @@ -6,9 +6,6 @@ import androidx.compose.runtime.Immutable import androidx.compose.runtime.getValue import androidx.compose.runtime.setValue import androidx.compose.ui.unit.dp -import androidx.compose.ui.util.fastAny -import androidx.compose.ui.util.fastForEach -import androidx.compose.ui.util.fastForEachIndexed import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.cachedIn @@ -36,8 +33,6 @@ import exh.metadata.metadata.RaisedSearchMetadata import exh.source.getMainSource import exh.source.mangaDexSourceIds import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.PersistentList -import kotlinx.collections.immutable.mutate import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.Flow @@ -339,7 +334,7 @@ open class BrowseSourceScreenModel( false -> Instant.now().toEpochMilli() }, ) - // TODO: also allow deleting chapters when remove favorite (just like in [MangaScreenModel]) + if (!new.favorite) { new = new.removeCovers(coverCache) } else { @@ -452,170 +447,6 @@ open class BrowseSourceScreenModel( } } - // KMK --> - fun toggleSelectionMode() { - if (state.value.selectionMode) - clearSelection() - mutableState.update { it.copy(selectionMode = !it.selectionMode) } - } - - private fun clearSelection() { - mutableState.update { it.copy(selection = persistentListOf()) } - } - - fun toggleSelection(manga: Manga) { - mutableState.update { state -> - val newSelection = state.selection.mutate { list -> - if (list.fastAny { it.id == manga.id }) { - list.removeAll { it.id == manga.id } - } else { - list.add(manga) - } - } - state.copy(selection = newSelection) - }.also { - if (state.value.selection.isEmpty()) - toggleSelectionMode() - } - } - - fun addFavorite(startIdx: Int = 0) { - screenModelScope.launch { - val mangaWithDup = getDuplicateLibraryManga(startIdx) - if (mangaWithDup != null) - setDialog(Dialog.AllowDuplicate(mangaWithDup)) - else - addFavoriteDuplicate() - } - } - - fun addFavoriteDuplicate(skipAllDuplicates: Boolean = false) { - screenModelScope.launch { - val mangaList = if (skipAllDuplicates) getNotDuplicateLibraryMangas() else state.value.selection - val categories = getCategories() - val defaultCategoryId = libraryPreferences.defaultCategory().get() - val defaultCategory = categories.find { it.id == defaultCategoryId.toLong() } - - when { - // Default category set - defaultCategory != null -> { - setMangaCategories(mangaList, listOf(defaultCategory.id), emptyList()) - } - - // Automatic 'Default' or no categories - defaultCategoryId == 0 || categories.isEmpty() -> { - // Automatic 'Default' or no categories - setMangaCategories(mangaList, emptyList(), emptyList()) - } - - else -> { - // Get indexes of the common categories to preselect. - val common = getCommonCategories(mangaList) - // Get indexes of the mix categories to preselect. - val mix = getMixCategories(mangaList) - val preselected = categories - .map { - when (it) { - in common -> CheckboxState.State.Checked(it) - in mix -> CheckboxState.TriState.Exclude(it) - else -> CheckboxState.State.None(it) - } - } - .toImmutableList() - setDialog(Dialog.ChangeMangasCategory(mangaList, preselected)) - } - } - } - } - - private suspend fun getNotDuplicateLibraryMangas(): List { - return state.value.selection.filterNot { manga -> - getDuplicateLibraryManga.await(manga).isNotEmpty() - } - } - - private suspend fun getDuplicateLibraryManga(startIdx: Int = 0): Pair? { - 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. - * - * @param mangaList the list of manga to move. - * @param addCategories the categories to add for all mangas. - * @param removeCategories the categories to remove in all mangas. - */ - fun setMangaCategories(mangaList: List, addCategories: List, removeCategories: List) { - screenModelScope.launchNonCancellable { - mangaList.fastForEach { manga -> - val categoryIds = getCategories.await(manga.id) - .map { it.id } - .subtract(removeCategories.toSet()) - .plus(addCategories) - .toList() - - moveMangaToCategoriesAndAddToLibrary(manga, categoryIds) - } - } - toggleSelectionMode() - } - - private fun moveMangaToCategoriesAndAddToLibrary(manga: Manga, categories: List) { - moveMangaToCategory(manga.id, categories) - if (manga.favorite) return - - screenModelScope.launchIO { - updateManga.awaitUpdateFavorite(manga.id, true) - } - } - - private fun moveMangaToCategory(mangaId: Long, categoryIds: List) { - screenModelScope.launchIO { - setMangaCategories.await(mangaId, categoryIds) - } - } - - /** - * Returns the common categories for the given list of manga. - * - * @param mangas the list of manga. - */ - private suspend fun getCommonCategories(mangas: List): Collection { - if (mangas.isEmpty()) return emptyList() - return mangas - .map { getCategories.await(it.id).toSet() } - .reduce { set1, set2 -> set1.intersect(set2) } - } - - /** - * Returns the mix (non-common) categories for the given list of manga. - * - * @param mangas the list of manga. - */ - private suspend fun getMixCategories(mangas: List): Collection { - if (mangas.isEmpty()) return emptyList() - val mangaCategories = mangas.map { getCategories.await(it.id).toSet() } - val common = mangaCategories.reduce { set1, set2 -> set1.intersect(set2) } - return mangaCategories.flatten().distinct().subtract(common) - } - // KMK <-- - sealed interface Dialog { data object Filter : Dialog data class RemoveManga(val manga: Manga) : Dialog @@ -630,13 +461,6 @@ open class BrowseSourceScreenModel( data class DeleteSavedSearch(val idToDelete: Long, val name: String) : Dialog data class CreateSavedSearch(val currentSavedSearches: ImmutableList) : Dialog // SY <-- - // KMK --> - data class ChangeMangasCategory( - val mangas: List, - val initialSelection: ImmutableList>, - ) : Dialog - data class AllowDuplicate(val duplicatedManga: Pair) : Dialog - // KMK <-- } @Immutable @@ -649,10 +473,6 @@ open class BrowseSourceScreenModel( val savedSearches: ImmutableList = persistentListOf(), val filterable: Boolean = true, // SY <-- - // KMK --> - val selection: PersistentList = persistentListOf(), - val selectionMode: Boolean = false, - // KMK <-- ) { val isUserQuery get() = listing is Listing.Search && !listing.query.isNullOrEmpty() } 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 33fa5c3ee..f71714ec4 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 @@ -4,6 +4,7 @@ import androidx.activity.compose.BackHandler import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalHapticFeedback @@ -12,12 +13,15 @@ import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.currentOrThrow import eu.kanade.presentation.browse.SourceFeedScreen +import eu.kanade.presentation.browse.components.RemoveMangaDialog import eu.kanade.presentation.browse.components.SourceFeedAddDialog import eu.kanade.presentation.browse.components.SourceFeedDeleteDialog import eu.kanade.presentation.category.components.ChangeCategoryDialog import eu.kanade.presentation.manga.AllowDuplicateDialog +import eu.kanade.presentation.manga.DuplicateMangaDialog import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.source.Source +import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreen import eu.kanade.tachiyomi.ui.browse.source.browse.SourceFilterDialog import eu.kanade.tachiyomi.ui.category.CategoryScreen @@ -25,6 +29,7 @@ import eu.kanade.tachiyomi.ui.manga.MangaScreen import eu.kanade.tachiyomi.util.system.toast import exh.md.follows.MangaDexFollowsScreen import exh.util.nullIfBlank +import tachiyomi.core.common.util.lang.launchIO import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.source.interactor.GetRemoteManga import tachiyomi.domain.source.model.SavedSearch @@ -37,7 +42,12 @@ class SourceFeedScreen(val sourceId: Long) : Screen() { val state by screenModel.state.collectAsState() val navigator = LocalNavigator.currentOrThrow val context = LocalContext.current + // KMK --> + val bulkFavoriteScreenModel = rememberScreenModel { BulkFavoriteScreenModel() } + val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState() + + val scope = rememberCoroutineScope() val haptic = LocalHapticFeedback.current // KMK <-- @@ -53,8 +63,8 @@ class SourceFeedScreen(val sourceId: Long) : Screen() { onClickDelete = screenModel::openDeleteFeed, onClickManga = { // KMK --> - if (state.selectionMode) - screenModel.toggleSelection(it) + if (bulkFavoriteState.selectionMode) + bulkFavoriteScreenModel.toggleSelection(it) else // KMK <-- onMangaClick(navigator, it) @@ -66,15 +76,25 @@ class SourceFeedScreen(val sourceId: Long) : Screen() { // KMK --> sourceId = screenModel.source.id, onLongClickManga = { manga -> - if (state.selectionMode) { + if (!bulkFavoriteState.selectionMode) + scope.launchIO { + val duplicateManga = bulkFavoriteScreenModel.getDuplicateLibraryManga(manga) + when { + manga.favorite -> bulkFavoriteScreenModel.setDialog(BulkFavoriteScreenModel.Dialog.RemoveManga(manga)) + duplicateManga != null -> bulkFavoriteScreenModel.setDialog( + BulkFavoriteScreenModel.Dialog.AddDuplicateManga( + manga, + duplicateManga, + ), + ) + else -> bulkFavoriteScreenModel.addFavorite(manga) + } + haptic.performHapticFeedback(HapticFeedbackType.LongPress) + } + else navigator.push(MangaScreen(manga.id, true)) - } else { - screenModel.toggleSelection(manga) - haptic.performHapticFeedback(HapticFeedbackType.LongPress) - } }, - screenModel = screenModel, - screenState = state, + bulkFavoriteScreenModel = bulkFavoriteScreenModel, // KMK <-- ) @@ -99,40 +119,6 @@ class SourceFeedScreen(val sourceId: Long) : Screen() { }, ) } - // KMK --> - is SourceFeedScreenModel.Dialog.ChangeMangasCategory -> { - ChangeCategoryDialog( - initialSelection = dialog.initialSelection, - onDismissRequest = onDismissRequest, - onEditCategories = { navigator.push(CategoryScreen()) }, - onConfirm = { include, exclude -> - screenModel.setMangaCategories(dialog.mangas, include, exclude) - }, - ) - } - is SourceFeedScreenModel.Dialog.AllowDuplicate -> { - AllowDuplicateDialog( - onDismissRequest = onDismissRequest, - onAllowAllDuplicate = { - screenModel.addFavoriteDuplicate() - }, - onSkipAllDuplicate = { - screenModel.addFavoriteDuplicate(skipAllDuplicates = true) - }, - onOpenManga = { - navigator.push(MangaScreen(dialog.duplicatedManga.second.id)) - }, - onAllowDuplicate = { - screenModel.addFavorite(startIdx = dialog.duplicatedManga.first + 1) - }, - onSkipDuplicate = { - screenModel.removeDuplicateSelectedManga(index = dialog.duplicatedManga.first) - screenModel.addFavorite(startIdx = dialog.duplicatedManga.first) - }, - duplicatedName = dialog.duplicatedManga.second.title, - ) - } - // KMK <-- SourceFeedScreenModel.Dialog.Filter -> { SourceFilterDialog( onDismissRequest = onDismissRequest, @@ -199,8 +185,79 @@ class SourceFeedScreen(val sourceId: Long) : Screen() { null -> Unit } - BackHandler(state.searchQuery != null) { - screenModel.search(null) + // KMK --> + val onBulkDismissRequest = { bulkFavoriteScreenModel.setDialog(null) } + when (val dialog = bulkFavoriteState.dialog) { + is BulkFavoriteScreenModel.Dialog.AddDuplicateManga -> { + DuplicateMangaDialog( + onDismissRequest = onBulkDismissRequest, + onConfirm = { bulkFavoriteScreenModel.addFavorite(dialog.manga) }, + onOpenManga = { navigator.push(MangaScreen(dialog.duplicate.id)) }, + ) + } + is BulkFavoriteScreenModel.Dialog.RemoveManga -> { + RemoveMangaDialog( + onDismissRequest = onBulkDismissRequest, + onConfirm = { + bulkFavoriteScreenModel.changeMangaFavorite(dialog.manga) + }, + mangaToRemove = dialog.manga, + ) + } + is BulkFavoriteScreenModel.Dialog.ChangeMangaCategory -> { + ChangeCategoryDialog( + initialSelection = dialog.initialSelection, + onDismissRequest = onBulkDismissRequest, + onEditCategories = { navigator.push(CategoryScreen()) }, + onConfirm = { include, _ -> + bulkFavoriteScreenModel.changeMangaFavorite(dialog.manga) + bulkFavoriteScreenModel.moveMangaToCategories(dialog.manga, include) + }, + ) + } + is BulkFavoriteScreenModel.Dialog.ChangeMangasCategory -> { + ChangeCategoryDialog( + initialSelection = dialog.initialSelection, + onDismissRequest = onBulkDismissRequest, + onEditCategories = { navigator.push(CategoryScreen()) }, + onConfirm = { include, exclude -> + bulkFavoriteScreenModel.setMangasCategories(dialog.mangas, include, exclude) + }, + ) + } + is BulkFavoriteScreenModel.Dialog.AllowDuplicate -> { + AllowDuplicateDialog( + onDismissRequest = onBulkDismissRequest, + onAllowAllDuplicate = { + bulkFavoriteScreenModel.addFavoriteDuplicate() + }, + onSkipAllDuplicate = { + bulkFavoriteScreenModel.addFavoriteDuplicate(skipAllDuplicates = true) + }, + onOpenManga = { + navigator.push(MangaScreen(dialog.duplicatedManga.second.id)) + }, + onAllowDuplicate = { + bulkFavoriteScreenModel.addFavorite(startIdx = dialog.duplicatedManga.first + 1) + }, + onSkipDuplicate = { + bulkFavoriteScreenModel.removeDuplicateSelectedManga(index = dialog.duplicatedManga.first) + bulkFavoriteScreenModel.addFavorite(startIdx = dialog.duplicatedManga.first) + }, + duplicatedName = dialog.duplicatedManga.second.title, + ) + } + else -> {} + } + // KMK <-- + + BackHandler(state.searchQuery != null/* KMK --> */ || bulkFavoriteState.selectionMode /* KMK <-- */) { + // KMK --> + if(bulkFavoriteState.selectionMode) + bulkFavoriteScreenModel.backHandler() + else + // KMK <-- + screenModel.search(null) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedScreenModel.kt index 6f7e8b701..32eeded6d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedScreenModel.kt @@ -5,9 +5,6 @@ import androidx.compose.runtime.Immutable import androidx.compose.runtime.State import androidx.compose.runtime.getValue import androidx.compose.runtime.produceState -import androidx.compose.ui.util.fastAny -import androidx.compose.ui.util.fastForEach -import androidx.compose.ui.util.fastForEachIndexed import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.screenModelScope import dev.icerock.moko.resources.StringResource @@ -25,15 +22,12 @@ import exh.source.getMainSource import exh.source.mangaDexSourceIds import exh.util.nullIfBlank import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.PersistentList -import kotlinx.collections.immutable.mutate import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update @@ -41,16 +35,10 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json -import tachiyomi.core.common.preference.CheckboxState import tachiyomi.core.common.util.lang.launchIO import tachiyomi.core.common.util.lang.launchNonCancellable import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.lang.withUIContext -import tachiyomi.domain.category.interactor.GetCategories -import tachiyomi.domain.category.interactor.SetMangaCategories -import tachiyomi.domain.category.model.Category -import tachiyomi.domain.library.service.LibraryPreferences -import tachiyomi.domain.manga.interactor.GetDuplicateLibraryManga import tachiyomi.domain.manga.interactor.GetManga import tachiyomi.domain.manga.interactor.NetworkToLocalManga import tachiyomi.domain.source.interactor.CountFeedSavedSearchBySourceId @@ -82,12 +70,6 @@ open class SourceFeedScreenModel( private val insertFeedSavedSearch: InsertFeedSavedSearch = Injekt.get(), private val deleteFeedSavedSearchById: DeleteFeedSavedSearchById = Injekt.get(), private val getExhSavedSearch: GetExhSavedSearch = Injekt.get(), - // KMK --> - private val getCategories: GetCategories = Injekt.get(), - private val setMangaCategories: SetMangaCategories = Injekt.get(), - private val libraryPreferences: LibraryPreferences = Injekt.get(), - private val getDuplicateLibraryManga: GetDuplicateLibraryManga = Injekt.get(), - // KMK <-- ) : StateScreenModel(SourceFeedState()) { val source = sourceManager.getOrStub(sourceId) @@ -326,188 +308,10 @@ open class SourceFeedScreenModel( mutableState.update { it.copy(dialog = null) } } - // KMK --> - fun clearSelection() { - mutableState.update { it.copy(selection = persistentListOf()) } - } - - fun toggleSelection(manga: DomainManga) { - mutableState.update { state -> - val newSelection = state.selection.mutate { list -> - if (list.fastAny { it.id == manga.id }) { - list.removeAll { it.id == manga.id } - } else { - list.add(manga) - } - } - state.copy(selection = newSelection) - } - } - - fun addFavorite(startIdx: Int = 0) { - screenModelScope.launch { - val mangaWithDup = getDuplicateLibraryManga(startIdx) - if (mangaWithDup != null) - setDialog(Dialog.AllowDuplicate(mangaWithDup)) - else - addFavoriteDuplicate() - } - } - - fun addFavoriteDuplicate(skipAllDuplicates: Boolean = false) { - screenModelScope.launch { - val mangaList = if (skipAllDuplicates) getNotDuplicateLibraryMangas() else state.value.selection - val categories = getCategories() - val defaultCategoryId = libraryPreferences.defaultCategory().get() - val defaultCategory = categories.find { it.id == defaultCategoryId.toLong() } - - when { - // Default category set - defaultCategory != null -> { - setMangaCategories(mangaList, listOf(defaultCategory.id), emptyList()) - } - - // Automatic 'Default' or no categories - defaultCategoryId == 0 || categories.isEmpty() -> { - // Automatic 'Default' or no categories - setMangaCategories(mangaList, emptyList(), emptyList()) - } - - else -> { - // Get indexes of the common categories to preselect. - val common = getCommonCategories(mangaList) - // Get indexes of the mix categories to preselect. - val mix = getMixCategories(mangaList) - val preselected = categories - .map { - when (it) { - in common -> CheckboxState.State.Checked(it) - in mix -> CheckboxState.TriState.Exclude(it) - else -> CheckboxState.State.None(it) - } - } - .toImmutableList() - setDialog(Dialog.ChangeMangasCategory(mangaList, preselected)) - } - } - } - } - - private suspend fun getNotDuplicateLibraryMangas(): List { - return state.value.selection.filterNot { manga -> - getDuplicateLibraryManga.await(manga).isNotEmpty() - } - } - - private suspend fun getDuplicateLibraryManga(startIdx: Int = 0): Pair? { - 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. - * - * @param mangaList the list of manga to move. - * @param addCategories the categories to add for all mangas. - * @param removeCategories the categories to remove in all mangas. - */ - fun setMangaCategories(mangaList: List, addCategories: List, removeCategories: List) { - screenModelScope.launchNonCancellable { - mangaList.fastForEach { manga -> - val categoryIds = getCategories.await(manga.id) - .map { it.id } - .subtract(removeCategories.toSet()) - .plus(addCategories) - .toList() - - moveMangaToCategoriesAndAddToLibrary(manga, categoryIds) - } - } - clearSelection() - } - - private fun moveMangaToCategoriesAndAddToLibrary(manga: DomainManga, categories: List) { - moveMangaToCategory(manga.id, categories) - if (manga.favorite) return - - screenModelScope.launchIO { - updateManga.awaitUpdateFavorite(manga.id, true) - } - } - - private fun moveMangaToCategory(mangaId: Long, categoryIds: List) { - screenModelScope.launchIO { - setMangaCategories.await(mangaId, categoryIds) - } - } - - /** - * Get user categories. - * - * @return List of categories, not including the default category - */ - suspend fun getCategories(): List { - return getCategories.subscribe() - .firstOrNull() - ?.filterNot { it.isSystemCategory } - .orEmpty() - } - - /** - * Returns the common categories for the given list of manga. - * - * @param mangas the list of manga. - */ - private suspend fun getCommonCategories(mangas: List): Collection { - if (mangas.isEmpty()) return emptyList() - return mangas - .map { getCategories.await(it.id).toSet() } - .reduce { set1, set2 -> set1.intersect(set2) } - } - - /** - * Returns the mix (non-common) categories for the given list of manga. - * - * @param mangas the list of manga. - */ - private suspend fun getMixCategories(mangas: List): Collection { - if (mangas.isEmpty()) return emptyList() - val mangaCategories = mangas.map { getCategories.await(it.id).toSet() } - val common = mangaCategories.reduce { set1, set2 -> set1.intersect(set2) } - return mangaCategories.flatten().distinct().subtract(common) - } - - private fun setDialog(dialog: Dialog?) { - mutableState.update { it.copy(dialog = dialog) } - } - // KMK <-- - sealed class Dialog { data object Filter : Dialog() data class DeleteFeed(val feed: FeedSavedSearch) : Dialog() data class AddFeed(val feedId: Long, val name: String) : Dialog() - // KMK --> - data class ChangeMangasCategory( - val mangas: List, - val initialSelection: ImmutableList>, - ) : Dialog() - data class AllowDuplicate(val duplicatedManga: Pair) : Dialog() - // KMK <-- } override fun onDispose() { @@ -523,14 +327,7 @@ data class SourceFeedState( val filters: FilterList = FilterList(), val savedSearches: ImmutableList = persistentListOf(), val dialog: SourceFeedScreenModel.Dialog? = null, - // KMK --> - val selection: PersistentList = persistentListOf(), - // KMK <-- ) { val isLoading get() = items.isEmpty() - - // KMK --> - val selectionMode = selection.isNotEmpty() - // KMK <-- } 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 96bf82ba6..a59b66ec4 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 @@ -48,12 +48,12 @@ class GlobalSearchScreen( } // KMK --> - val scope = rememberCoroutineScope() - val haptic = LocalHapticFeedback.current - val bulkFavoriteScreenModel = rememberScreenModel { BulkFavoriteScreenModel() } val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState() + val scope = rememberCoroutineScope() + val haptic = LocalHapticFeedback.current + BackHandler(enabled = bulkFavoriteState.selectionMode) { bulkFavoriteScreenModel.backHandler() } @@ -99,11 +99,7 @@ class GlobalSearchScreen( }, onLongClickItem = { manga -> // KMK --> - if (bulkFavoriteState.selectionMode) - // KMK <-- - navigator.push(MangaScreen(manga.id, true)) - // KMK --> - else + if (!bulkFavoriteState.selectionMode) scope.launchIO { val duplicateManga = bulkFavoriteScreenModel.getDuplicateLibraryManga(manga) when { @@ -118,29 +114,29 @@ class GlobalSearchScreen( } haptic.performHapticFeedback(HapticFeedbackType.LongPress) } + else // KMK <-- + navigator.push(MangaScreen(manga.id, true)) }, // KMK --> - bulkFavoriteState = bulkFavoriteState, - toggleSelectionMode = bulkFavoriteScreenModel::toggleSelectionMode, - addFavorite = bulkFavoriteScreenModel::addFavorite, + bulkFavoriteScreenModel = bulkFavoriteScreenModel, // KMK <-- ) } // KMK --> - val onDismissRequest = { bulkFavoriteScreenModel.setDialog(null) } + val onBulkDismissRequest = { bulkFavoriteScreenModel.setDialog(null) } when (val dialog = bulkFavoriteState.dialog) { is BulkFavoriteScreenModel.Dialog.AddDuplicateManga -> { DuplicateMangaDialog( - onDismissRequest = onDismissRequest, + onDismissRequest = onBulkDismissRequest, onConfirm = { bulkFavoriteScreenModel.addFavorite(dialog.manga) }, onOpenManga = { navigator.push(MangaScreen(dialog.duplicate.id)) }, ) } is BulkFavoriteScreenModel.Dialog.RemoveManga -> { RemoveMangaDialog( - onDismissRequest = onDismissRequest, + onDismissRequest = onBulkDismissRequest, onConfirm = { bulkFavoriteScreenModel.changeMangaFavorite(dialog.manga) }, @@ -150,7 +146,7 @@ class GlobalSearchScreen( is BulkFavoriteScreenModel.Dialog.ChangeMangaCategory -> { ChangeCategoryDialog( initialSelection = dialog.initialSelection, - onDismissRequest = onDismissRequest, + onDismissRequest = onBulkDismissRequest, onEditCategories = { navigator.push(CategoryScreen()) }, onConfirm = { include, _ -> bulkFavoriteScreenModel.changeMangaFavorite(dialog.manga) @@ -161,7 +157,7 @@ class GlobalSearchScreen( is BulkFavoriteScreenModel.Dialog.ChangeMangasCategory -> { ChangeCategoryDialog( initialSelection = dialog.initialSelection, - onDismissRequest = onDismissRequest, + onDismissRequest = onBulkDismissRequest, onEditCategories = { navigator.push(CategoryScreen()) }, onConfirm = { include, exclude -> bulkFavoriteScreenModel.setMangasCategories(dialog.mangas, include, exclude) @@ -170,7 +166,7 @@ class GlobalSearchScreen( } is BulkFavoriteScreenModel.Dialog.AllowDuplicate -> { AllowDuplicateDialog( - onDismissRequest = onDismissRequest, + onDismissRequest = onBulkDismissRequest, onAllowAllDuplicate = { bulkFavoriteScreenModel.addFavoriteDuplicate() }, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt index c6bbb429e..33895fb5a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt @@ -976,8 +976,8 @@ class MangaScreenModel( downloadManager.getQueuedDownloadOrNull(chapter.id) } // SY --> - val mangaMerged = mergedData?.manga?.get(chapter.mangaId) ?: manga - val source = mergedData?.sources?.find { mangaMerged.source == it.id }?.takeIf { mergedData.sources.size > 2 } + val manga = mergedData?.manga?.get(chapter.mangaId) ?: manga + val source = mergedData?.sources?.find { manga.source == it.id }?.takeIf { mergedData.sources.size > 2 } // SY <-- val downloaded = if (isLocal) { true @@ -986,8 +986,8 @@ class MangaScreenModel( // SY --> chapter.name, chapter.scanlator, - mangaMerged.ogTitle, - mangaMerged.source, + manga.ogTitle, + manga.source, // SY <-- ) }