diff --git a/app/src/main/java/eu/kanade/presentation/libraryUpdateError/LibraryUpdateErrorScreen.kt b/app/src/main/java/eu/kanade/presentation/libraryUpdateError/LibraryUpdateErrorScreen.kt index db940cafd..034d2387c 100644 --- a/app/src/main/java/eu/kanade/presentation/libraryUpdateError/LibraryUpdateErrorScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/libraryUpdateError/LibraryUpdateErrorScreen.kt @@ -67,7 +67,7 @@ fun LibraryUpdateErrorScreen( onInvertSelection: () -> Unit, onErrorsDelete: () -> Unit, onErrorDelete: (Long) -> Unit, - onErrorSelected: (LibraryUpdateErrorItem, Boolean, Boolean, Boolean) -> Unit, + onErrorSelected: (LibraryUpdateErrorItem, Boolean, Boolean) -> Unit, navigateUp: () -> Unit, ) { BackHandler(enabled = state.selectionMode, onBack = { onSelectAll(false) }) diff --git a/app/src/main/java/eu/kanade/presentation/libraryUpdateError/components/LibraryUpdateErrorUiItem.kt b/app/src/main/java/eu/kanade/presentation/libraryUpdateError/components/LibraryUpdateErrorUiItem.kt index 1d1f2d13e..c834a61d5 100644 --- a/app/src/main/java/eu/kanade/presentation/libraryUpdateError/components/LibraryUpdateErrorUiItem.kt +++ b/app/src/main/java/eu/kanade/presentation/libraryUpdateError/components/LibraryUpdateErrorUiItem.kt @@ -44,7 +44,7 @@ import tachiyomi.presentation.core.util.selectedBackground internal fun LazyListScope.libraryUpdateErrorUiItems( uiModels: List, selectionMode: Boolean, - onErrorSelected: (LibraryUpdateErrorItem, Boolean, Boolean, Boolean) -> Unit, + onErrorSelected: (LibraryUpdateErrorItem, Boolean, Boolean) -> Unit, onClick: (LibraryUpdateErrorItem) -> Unit, onClickCover: (LibraryUpdateErrorItem) -> Unit, onDelete: (Long) -> Unit, @@ -81,7 +81,6 @@ internal fun LazyListScope.libraryUpdateErrorUiItems( selectionMode -> onErrorSelected( libraryUpdateErrorItem, !libraryUpdateErrorItem.selected, - true, false, ) @@ -93,7 +92,6 @@ internal fun LazyListScope.libraryUpdateErrorUiItems( libraryUpdateErrorItem, !libraryUpdateErrorItem.selected, true, - true, ) }, onClickCover = { onClickCover(libraryUpdateErrorItem) }.takeIf { !selectionMode }, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaScreen.kt index 4dc5b482f..670b88ec0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaScreen.kt @@ -128,11 +128,9 @@ data class MigrateMangaScreen( // KMK --> bottomBar = { MigrateMangaBottomBar( - selected = state.selection, + selectionMode = state.selectionMode, onMultiMigrateClicked = { - val selection = state.selection - .map { it.manga.id } - navigator.push(MigrationConfigScreen(selection)) + navigator.push(MigrationConfigScreen(state.selection)) }, enableScrollToTop = enableScrollToTop, enableScrollToBottom = enableScrollToBottom, @@ -187,7 +185,7 @@ data class MigrateMangaScreen( contentPadding: PaddingValues, state: MigrateMangaScreenModel.State, // KMK --> - onMangaSelected: (MigrateMangaItem, Boolean, Boolean, Boolean) -> Unit, + onMangaSelected: (Manga, Boolean, Boolean) -> Unit, // KMK <-- onClickItem: (Manga) -> Unit, onClickCover: (Manga) -> Unit, @@ -197,20 +195,23 @@ data class MigrateMangaScreen( contentPadding = contentPadding, ) { items(items = state.titles) { manga -> + // KMK --> + val isSelected = manga.id in state.selection + // KMK <-- MigrateMangaItem( - manga = manga.manga, - isSelected = manga.selected, + manga = manga, + isSelected = isSelected, onClickItem = { // KMK --> when { - state.selectionMode -> onMangaSelected(manga, !manga.selected, true, false) + state.selectionMode -> onMangaSelected(manga, !isSelected, false) // KMK <-- else -> onClickItem(it) } }, onClickCover = onClickCover, // KMK --> - onLongClick = { onMangaSelected(manga, !manga.selected, true, true) }, + onLongClick = { onMangaSelected(manga, !isSelected, true) }, modifier = Modifier.animateItemFastScroll(), // KMK <-- ) @@ -293,7 +294,7 @@ data class MigrateMangaScreen( @Composable private fun MigrateMangaBottomBar( modifier: Modifier = Modifier, - selected: List, + selectionMode: Boolean, onMultiMigrateClicked: () -> Unit, enableScrollToTop: Boolean, enableScrollToBottom: Boolean, @@ -302,7 +303,7 @@ data class MigrateMangaScreen( ) { val scope = rememberCoroutineScope() val animatedElevation by animateDpAsState( - targetValue = if (selected.isNotEmpty()) 3.dp else 0.dp, + targetValue = if (selectionMode) 3.dp else 0.dp, label = "elevation", ) Surface( @@ -350,7 +351,7 @@ data class MigrateMangaScreen( toConfirm = confirm[1], onLongClick = { onLongClickItem(1) }, onClick = onMultiMigrateClicked, - enabled = selected.isNotEmpty(), + enabled = selectionMode, ) Button( title = stringResource(KMR.strings.action_scroll_to_bottom), diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaScreenModel.kt index d9e2a8f3b..b2e4e734a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaScreenModel.kt @@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.ui.browse.migration.manga import androidx.compose.runtime.Immutable import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.screenModelScope -import eu.kanade.core.util.addOrRemove import eu.kanade.tachiyomi.source.Source import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -17,6 +16,7 @@ import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import logcat.LogPriority +import mihon.core.common.utils.mutate import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.manga.interactor.GetFavorites import tachiyomi.domain.manga.model.Manga @@ -36,7 +36,6 @@ class MigrateMangaScreenModel( // KMK --> // First and last selected index in list private val selectedPositions: Array = arrayOf(-1, -1) - private val selectedMangaIds: HashSet = HashSet() // KMK <-- init { @@ -50,120 +49,133 @@ class MigrateMangaScreenModel( logcat(LogPriority.ERROR, it) _events.send(MigrationMangaEvent.FailedFetchingFavorites) mutableState.update { state -> - state.copy(titleList = persistentListOf()) + state.copy( + titleList = persistentListOf(), + // KMK --> + selection = emptySet(), + // KMK <-- + ) } } - // KMK --> - .map { manga -> - toMigrationMangaScreenItems(manga) - } - // KMK <-- .map { manga -> manga - .sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.manga.title }) + .sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.title }) .toImmutableList() } .collectLatest { list -> - mutableState.update { it.copy(titleList = list) } + // KMK --> + mutableState.update { state -> + val titleIds = list.map { it.id }.toSet() + val selection = state.selection.intersect(titleIds).toMutableSet() + updateSelectedPositions(list, selection) + state.copy( + titleList = list, + selection = selection, + ) + } + // KMK <-- } } } - // KMK --> - private fun toMigrationMangaScreenItems(mangas: List): List { - return mangas.map { manga -> - MigrateMangaItem( - manga = manga, - selected = manga.id in selectedMangaIds, - ) - } - } - fun toggleSelection( - item: MigrateMangaItem, + item: Manga, + // KMK --> selected: Boolean, - userSelected: Boolean = false, fromLongPress: Boolean = false, ) { mutableState.update { state -> - val newItems = state.titles.toMutableList().apply { - val selectedIndex = indexOfFirst { it.manga.id == item.manga.id } - if (selectedIndex < 0) return@apply + if (item.id in state.selection == selected) return@update state + val selectedIndex = state.titles.indexOfFirst { it.id == item.id } + if (selectedIndex < 0) return@update state - val selectedItem = get(selectedIndex) - if (selectedItem.selected == selected) return@apply + val selection = state.selection.mutate { list -> + state.titles.run { + val firstSelection = list.isEmpty() + if (selected) list.add(item.id) else list.remove(item.id) - val firstSelection = none { it.selected } - set(selectedIndex, selectedItem.copy(selected = selected)) - selectedMangaIds.addOrRemove(item.manga.id, selected) - - if (selected && userSelected && fromLongPress) { - if (firstSelection) { - selectedPositions[0] = selectedIndex - selectedPositions[1] = selectedIndex - } else { - // Try to select the items in-between when possible - val range: IntRange - if (selectedIndex < selectedPositions[0]) { - range = selectedIndex + 1 until selectedPositions[0] + if (selected && fromLongPress) { + if (firstSelection) { selectedPositions[0] = selectedIndex - } else if (selectedIndex > selectedPositions[1]) { - range = (selectedPositions[1] + 1) until selectedIndex selectedPositions[1] = selectedIndex } else { - // Just select itself - range = IntRange.EMPTY - } + // Try to select the items in-between when possible + val range: IntRange + if (selectedIndex < selectedPositions[0]) { + range = selectedIndex + 1 until selectedPositions[0] + selectedPositions[0] = selectedIndex + } else if (selectedIndex > selectedPositions[1]) { + range = (selectedPositions[1] + 1) until selectedIndex + selectedPositions[1] = selectedIndex + } else { + // Just select itself + range = IntRange.EMPTY + } - range.forEach { - val inBetweenItem = get(it) - if (!inBetweenItem.selected) { - selectedMangaIds.add(inBetweenItem.manga.id) - set(it, inBetweenItem.copy(selected = true)) + range.forEach { + val inBetweenItem = get(it) + if (inBetweenItem.id !in list) { + list.add(inBetweenItem.id) + } } } - } - } else if (userSelected && !fromLongPress) { - if (!selected) { - if (selectedIndex == selectedPositions[0]) { - selectedPositions[0] = indexOfFirst { it.selected } - } else if (selectedIndex == selectedPositions[1]) { - selectedPositions[1] = indexOfLast { it.selected } - } - } else { - if (selectedIndex < selectedPositions[0]) { - selectedPositions[0] = selectedIndex - } else if (selectedIndex > selectedPositions[1]) { - selectedPositions[1] = selectedIndex + } else if (!fromLongPress) { + if (!selected) { + if (selectedIndex == selectedPositions[0]) { + selectedPositions[0] = indexOfFirst { it.id in list } + } else if (selectedIndex == selectedPositions[1]) { + selectedPositions[1] = indexOfLast { it.id in list } + } + } else { + if (selectedIndex < selectedPositions[0]) { + selectedPositions[0] = selectedIndex + } else if (selectedIndex > selectedPositions[1]) { + selectedPositions[1] = selectedIndex + } } } } } - state.copy(titleList = newItems.toImmutableList()) + // KMK <-- + state.copy(selection = selection) } } + // KMK --> + private fun updateSelectedPositions(titles: List, selection: Set) { + if (selection.isEmpty()) { + selectedPositions[0] = -1 + selectedPositions[1] = -1 + return + } + + selectedPositions[0] = titles.indexOfFirst { it.id in selection } + selectedPositions[1] = titles.indexOfLast { it.id in selection } + } + fun toggleAllSelection(selected: Boolean = true) { mutableState.update { state -> - val newItems = state.titles.map { - selectedMangaIds.addOrRemove(it.manga.id, selected) - it.copy(selected = selected) + val selection = if (selected) { + state.titles.mapTo(mutableSetOf()) { it.id } + } else { + emptySet() } selectedPositions[0] = -1 selectedPositions[1] = -1 - state.copy(titleList = newItems.toImmutableList()) + state.copy(selection = selection) } } fun invertSelection() { mutableState.update { state -> - val newItems = state.titles.map { - selectedMangaIds.addOrRemove(it.manga.id, !it.selected) - it.copy(selected = !it.selected) + val selection = state.selection.mutate { list -> + state.titles.forEach { item -> + if (!list.remove(item.id)) list.add(item.id) + } } selectedPositions[0] = -1 selectedPositions[1] = -1 - state.copy(titleList = newItems.toImmutableList()) + state.copy(selection = selection) } } // KMK <-- @@ -177,13 +189,11 @@ class MigrateMangaScreenModel( @Immutable data class State( val source: Source? = null, - private val titleList: ImmutableList? = null, + val selection: Set = emptySet(), + private val titleList: ImmutableList? = null, ) { - // KMK --> - val selection = titles.filter { it.selected } - // KMK <-- - val titles: ImmutableList + val titles: ImmutableList get() = titleList ?: persistentListOf() val isLoading: Boolean @@ -199,11 +209,3 @@ class MigrateMangaScreenModel( sealed interface MigrationMangaEvent { data object FailedFetchingFavorites : MigrationMangaEvent } - -// KMK --> -@Immutable -data class MigrateMangaItem( - val manga: Manga, - val selected: Boolean, -) -// KMK <-- diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt index 4fb192350..34e6d80d6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt @@ -298,14 +298,14 @@ class HistoryScreenModel( selectionOptions: HistorySelectionOptions, ) { val (selected, fromLongPress) = selectionOptions - if (item.chapterId in state.value.selection == selected) return mutableState.update { state -> + if (item.chapterId in state.selection == selected) return@update state + val selectedIndex = state.list.indexOfFirst { it.chapterId == item.chapterId } + if (selectedIndex < 0) return@update state + val selection = state.selection.mutate { list -> state.list.run { - val selectedIndex = indexOfFirst { it.chapterId == item.chapterId } - if (selectedIndex < 0) return@run - val firstSelection = list.isEmpty() if (selected) list.add(item.chapterId) else list.remove(item.chapterId) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/libraryUpdateError/LibraryUpdateErrorScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/libraryUpdateError/LibraryUpdateErrorScreenModel.kt index b63cf8ab7..85068c6ea 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/libraryUpdateError/LibraryUpdateErrorScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/libraryUpdateError/LibraryUpdateErrorScreenModel.kt @@ -62,22 +62,20 @@ class LibraryUpdateErrorScreenModel( fun toggleSelection( item: LibraryUpdateErrorItem, selected: Boolean, - userSelected: Boolean = false, fromLongPress: Boolean = false, ) { mutableState.update { state -> + val selectedIndex = state.items.indexOfFirst { it.error.errorId == item.error.errorId } + if (selectedIndex < 0) return@update state + val selectedItem = state.items[selectedIndex] + if (selectedItem.selected == selected) return@update state + val newItems = state.items.toMutableList().apply { - val selectedIndex = indexOfFirst { it.error.errorId == item.error.errorId } - if (selectedIndex < 0) return@apply - - val selectedItem = get(selectedIndex) - if (selectedItem.selected == selected) return@apply - val firstSelection = none { it.selected } set(selectedIndex, selectedItem.copy(selected = selected)) selectedErrorIds.addOrRemove(item.error.errorId, selected) - if (selected && userSelected && fromLongPress) { + if (selected && fromLongPress) { if (firstSelection) { selectedPositions[0] = selectedIndex selectedPositions[1] = selectedIndex @@ -96,14 +94,14 @@ class LibraryUpdateErrorScreenModel( } range.forEach { - val inbetweenItem = get(it) - if (!inbetweenItem.selected) { - selectedErrorIds.add(inbetweenItem.error.errorId) - set(it, inbetweenItem.copy(selected = true)) + val inBetweenItem = get(it) + if (!inBetweenItem.selected) { + selectedErrorIds.add(inBetweenItem.error.errorId) + set(it, inBetweenItem.copy(selected = true)) } } } - } else if (userSelected && !fromLongPress) { + } else if (!fromLongPress) { if (!selected) { if (selectedIndex == selectedPositions[0]) { selectedPositions[0] = indexOfFirst { it.selected } 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 e0ad1faf4..3a886c134 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 @@ -555,8 +555,8 @@ class MangaScreenModel( } val vibrantColor = it.getBestColor() ?: return@launchIO mangaCover.vibrantCoverColor = vibrantColor - updateSuccessState { - it.copy(seedColor = Color(vibrantColor)) + updateSuccessState { state -> + state.copy(seedColor = Color(vibrantColor)) } } } @@ -1729,13 +1729,14 @@ class MangaScreenModel( fromLongPress: Boolean = false, ) { updateSuccessState { successState -> + // KMK --> + val selectedIndex = successState.processedChapters.indexOfFirst { it.id == item.chapter.id } + if (selectedIndex < 0) return@updateSuccessState successState + val selectedItem = successState.processedChapters[selectedIndex] + if (selectedItem.selected == selected) return@updateSuccessState successState + // KMK <-- + val newChapters = successState.processedChapters.toMutableList().apply { - val selectedIndex = successState.processedChapters.indexOfFirst { it.id == item.chapter.id } - if (selectedIndex < 0) return@apply - - val selectedItem = get(selectedIndex) - if ((selectedItem.selected && selected) || (!selectedItem.selected && !selected)) return@apply - val firstSelection = none { it.selected } set(selectedIndex, selectedItem.copy(selected = selected)) selectedChapterIds.addOrRemove(item.id, selected) @@ -1759,10 +1760,10 @@ class MangaScreenModel( } range.forEach { - val inbetweenItem = get(it) - if (!inbetweenItem.selected) { - selectedChapterIds.add(inbetweenItem.id) - set(it, inbetweenItem.copy(selected = true)) + val inBetweenItem = get(it) + if (!inBetweenItem.selected) { + selectedChapterIds.add(inBetweenItem.id) + set(it, inBetweenItem.copy(selected = true)) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt index 5fbaba32d..a36426637 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt @@ -379,13 +379,14 @@ class UpdatesScreenModel( val (selected, userSelected, fromLongPress, isGroup, isExpanded) = selectionOptions // KMK <-- mutableState.update { state -> + // KMK --> + val selectedIndex = state.items.indexOfFirst { it.update.chapterId == item.update.chapterId } + if (selectedIndex < 0) return@update state + val selectedItem = state.items[selectedIndex] + if (selectedItem.selected == selected) return@update state + // KMK <-- + val newItems = state.items.toMutableList().apply { - val selectedIndex = indexOfFirst { it.update.chapterId == item.update.chapterId } - if (selectedIndex < 0) return@apply - - val selectedItem = get(selectedIndex) - if (selectedItem.selected == selected) return@apply - val firstSelection = none { it.selected } set(selectedIndex, selectedItem.copy(selected = selected)) selectedChapterIds.addOrRemove(item.update.chapterId, selected) @@ -428,10 +429,10 @@ class UpdatesScreenModel( } range.forEach { - val inbetweenItem = get(it) - if (!inbetweenItem.selected) { - selectedChapterIds.add(inbetweenItem.update.chapterId) - set(it, inbetweenItem.copy(selected = true)) + val inBetweenItem = get(it) + if (!inBetweenItem.selected) { + selectedChapterIds.add(inBetweenItem.update.chapterId) + set(it, inBetweenItem.copy(selected = true)) } } }