From 10e349f76ee1f9bcde5e3502bcd45270d5cd496b Mon Sep 17 00:00:00 2001 From: arkon Date: Sun, 13 Nov 2022 22:27:59 -0500 Subject: [PATCH] Retain previous selected state when updating list states Fixes #8417 --- .../eu/kanade/core/util/CollectionUtils.kt | 8 +++ .../migration/manga/MigrateMangaPresenter.kt | 4 +- .../sources/MigrationSourcesPresenter.kt | 4 +- .../ui/browse/source/SourcesPresenter.kt | 4 +- .../ui/download/DownloadPresenter.kt | 2 +- .../tachiyomi/ui/manga/MangaScreenModel.kt | 54 +++++++++-------- .../tachiyomi/ui/updates/UpdatesPresenter.kt | 59 +++++++++++-------- 7 files changed, 79 insertions(+), 56 deletions(-) diff --git a/app/src/main/java/eu/kanade/core/util/CollectionUtils.kt b/app/src/main/java/eu/kanade/core/util/CollectionUtils.kt index 636f0a4f5b..42e5d51630 100644 --- a/app/src/main/java/eu/kanade/core/util/CollectionUtils.kt +++ b/app/src/main/java/eu/kanade/core/util/CollectionUtils.kt @@ -25,3 +25,11 @@ inline fun Map.mapNotNullKeys(transform: (Map.Entry) forEach { element -> transform(element)?.let { mutableMap[it] = element.value } } return mutableMap } + +fun HashSet.addOrRemove(value: E, shouldAdd: Boolean) { + if (shouldAdd) { + add(value) + } else { + remove(value) + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaPresenter.kt index cc34184db0..2960be6670 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaPresenter.kt @@ -31,8 +31,8 @@ class MigrateMangaPresenter( presenterScope.launchIO { getFavorites .subscribe(sourceId) - .catch { exception -> - logcat(LogPriority.ERROR, exception) + .catch { + logcat(LogPriority.ERROR, it) _events.send(Event.FailedFetchingFavorites) } .map { list -> diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrationSourcesPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrationSourcesPresenter.kt index 6ae0963b78..aabb376c27 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrationSourcesPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrationSourcesPresenter.kt @@ -32,8 +32,8 @@ class MigrationSourcesPresenter( fun onCreate() { presenterScope.launchIO { getSourcesWithFavoriteCount.subscribe() - .catch { exception -> - logcat(LogPriority.ERROR, exception) + .catch { + logcat(LogPriority.ERROR, it) _channel.send(Event.FailedFetchingSourcesWithCount) } .collectLatest { sources -> diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesPresenter.kt index fe2d50e82a..1c73a40525 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesPresenter.kt @@ -40,8 +40,8 @@ class SourcesPresenter( fun onCreate() { presenterScope.launchIO { getEnabledSources.subscribe() - .catch { exception -> - logcat(LogPriority.ERROR, exception) + .catch { + logcat(LogPriority.ERROR, it) _events.send(Event.FailedFetchingSources) } .onStart { delay(500) } // Defer to avoid crashing on initial render diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadPresenter.kt index 458c666c81..e4280f767b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadPresenter.kt @@ -35,7 +35,7 @@ class DownloadPresenter : BasePresenter() { presenterScope.launch { downloadQueue.updatedFlow() - .catch { error -> logcat(LogPriority.ERROR, error) } + .catch { logcat(LogPriority.ERROR, it) } .map { downloads -> downloads .groupBy { it.source } 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 b63c06f04c..27f485c528 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 @@ -8,6 +8,7 @@ import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.coroutineScope import eu.kanade.core.prefs.CheckboxState import eu.kanade.core.prefs.mapAsCheckboxState +import eu.kanade.core.util.addOrRemove import eu.kanade.data.chapter.NoChaptersException import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.category.interactor.GetCategories @@ -84,6 +85,7 @@ class MangaInfoScreenModel( basePreferences: BasePreferences = Injekt.get(), private val downloadPreferences: DownloadPreferences = Injekt.get(), private val libraryPreferences: LibraryPreferences = Injekt.get(), + private val uiPreferences: UiPreferences = Injekt.get(), private val trackManager: TrackManager = Injekt.get(), private val sourceManager: SourceManager = Injekt.get(), private val downloadManager: DownloadManager = Injekt.get(), @@ -120,6 +122,7 @@ class MangaInfoScreenModel( get() = successState?.processedChapters private val selectedPositions: Array = arrayOf(-1, -1) // first and last selected index in list + private val selectedChapterIds: HashSet = HashSet() /** * Helper function to update the UI state only if it's currently in success state @@ -141,7 +144,6 @@ class MangaInfoScreenModel( init { val toChapterItemsParams: List.(manga: Manga) -> List = { manga -> - val uiPreferences = Injekt.get() toChapterItems( context = context, manga = manga, @@ -529,6 +531,7 @@ class MangaInfoScreenModel( it + 1, ) }, + selected = chapter.id in selectedChapterIds, ) } } @@ -832,51 +835,54 @@ class MangaInfoScreenModel( ) { updateSuccessState { successState -> val newChapters = successState.processedChapters.toMutableList().apply { - val modifiedIndex = successState.processedChapters.indexOfFirst { it == item } - if (modifiedIndex < 0) return@apply + val selectedIndex = successState.processedChapters.indexOfFirst { it.chapter.id == item.chapter.id } + if (selectedIndex < 0) return@apply - val oldItem = get(modifiedIndex) - if ((oldItem.selected && selected) || (!oldItem.selected && !selected)) return@apply + val selectedItem = get(selectedIndex) + if ((selectedItem.selected && selected) || (!selectedItem.selected && !selected)) return@apply val firstSelection = none { it.selected } - var newItem = removeAt(modifiedIndex) - add(modifiedIndex, newItem.copy(selected = selected)) + set(selectedIndex, selectedItem.copy(selected = selected)) + selectedChapterIds.addOrRemove(item.chapter.id, selected) if (selected && userSelected && fromLongPress) { if (firstSelection) { - selectedPositions[0] = modifiedIndex - selectedPositions[1] = modifiedIndex + selectedPositions[0] = selectedIndex + selectedPositions[1] = selectedIndex } else { // Try to select the items in-between when possible val range: IntRange - if (modifiedIndex < selectedPositions[0]) { - range = modifiedIndex + 1 until selectedPositions[0] - selectedPositions[0] = modifiedIndex - } else if (modifiedIndex > selectedPositions[1]) { - range = (selectedPositions[1] + 1) until modifiedIndex - selectedPositions[1] = modifiedIndex + 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 { - newItem = removeAt(it) - add(it, newItem.copy(selected = true)) + val inbetweenItem = get(it) + if (!inbetweenItem.selected) { + selectedChapterIds.add(inbetweenItem.chapter.id) + set(it, inbetweenItem.copy(selected = true)) + } } } } else if (userSelected && !fromLongPress) { if (!selected) { - if (modifiedIndex == selectedPositions[0]) { + if (selectedIndex == selectedPositions[0]) { selectedPositions[0] = indexOfFirst { it.selected } - } else if (modifiedIndex == selectedPositions[1]) { + } else if (selectedIndex == selectedPositions[1]) { selectedPositions[1] = indexOfLast { it.selected } } } else { - if (modifiedIndex < selectedPositions[0]) { - selectedPositions[0] = modifiedIndex - } else if (modifiedIndex > selectedPositions[1]) { - selectedPositions[1] = modifiedIndex + if (selectedIndex < selectedPositions[0]) { + selectedPositions[0] = selectedIndex + } else if (selectedIndex > selectedPositions[1]) { + selectedPositions[1] = selectedIndex } } } @@ -888,6 +894,7 @@ class MangaInfoScreenModel( fun toggleAllSelection(selected: Boolean) { updateSuccessState { successState -> val newChapters = successState.chapters.map { + selectedChapterIds.addOrRemove(it.chapter.id, selected) it.copy(selected = selected) } selectedPositions[0] = -1 @@ -899,6 +906,7 @@ class MangaInfoScreenModel( fun invertSelection() { updateSuccessState { successState -> val newChapters = successState.chapters.map { + selectedChapterIds.addOrRemove(it.chapter.id, !it.selected) it.copy(selected = !it.selected) } selectedPositions[0] = -1 diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesPresenter.kt index 74bfd4e738..c61a5e9eec 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesPresenter.kt @@ -4,6 +4,7 @@ import android.os.Bundle import androidx.compose.runtime.Immutable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import eu.kanade.core.util.addOrRemove import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.chapter.interactor.GetChapter import eu.kanade.domain.chapter.interactor.SetReadStatus @@ -72,6 +73,7 @@ class UpdatesPresenter( // First and last selected index in list private val selectedPositions: Array = arrayOf(-1, -1) + private val selectedChapterIds: HashSet = HashSet() override fun onCreate(savedState: Bundle?) { super.onCreate(savedState) @@ -100,7 +102,7 @@ class UpdatesPresenter( presenterScope.launchIO { downloadManager.queue.statusFlow() - .catch { error -> logcat(LogPriority.ERROR, error) } + .catch { logcat(LogPriority.ERROR, it) } .collect { withUIContext { updateDownloadState(it) @@ -110,7 +112,7 @@ class UpdatesPresenter( presenterScope.launchIO { downloadManager.queue.progressFlow() - .catch { error -> logcat(LogPriority.ERROR, error) } + .catch { logcat(LogPriority.ERROR, it) } .collect { withUIContext { updateDownloadState(it) @@ -120,27 +122,26 @@ class UpdatesPresenter( } private fun List.toUpdateItems(): List { - return this - .distinctBy { it.chapterId } - .map { - val activeDownload = downloadManager.queue.find { download -> it.chapterId == download.chapter.id } - val downloaded = downloadManager.isChapterDownloaded( - it.chapterName, - it.scanlator, - it.mangaTitle, - it.sourceId, - ) - val downloadState = when { - activeDownload != null -> activeDownload.status - downloaded -> Download.State.DOWNLOADED - else -> Download.State.NOT_DOWNLOADED - } - UpdatesItem( - update = it, - downloadStateProvider = { downloadState }, - downloadProgressProvider = { activeDownload?.progress ?: 0 }, - ) + return this.map { + val activeDownload = downloadManager.queue.find { download -> it.chapterId == download.chapter.id } + val downloaded = downloadManager.isChapterDownloaded( + it.chapterName, + it.scanlator, + it.mangaTitle, + it.sourceId, + ) + val downloadState = when { + activeDownload != null -> activeDownload.status + downloaded -> Download.State.DOWNLOADED + else -> Download.State.NOT_DOWNLOADED } + UpdatesItem( + update = it, + downloadStateProvider = { downloadState }, + downloadProgressProvider = { activeDownload?.progress ?: 0 }, + selected = it.chapterId in selectedChapterIds, + ) + } } /** @@ -155,12 +156,14 @@ class UpdatesPresenter( } if (modifiedIndex < 0) return@apply - val item = removeAt(modifiedIndex) - .copy( + val item = get(modifiedIndex) + set( + modifiedIndex, + item.copy( downloadStateProvider = { download.status }, downloadProgressProvider = { download.progress }, - ) - add(modifiedIndex, item) + ), + ) } } @@ -281,6 +284,7 @@ class UpdatesPresenter( val firstSelection = none { it.selected } set(selectedIndex, selectedItem.copy(selected = selected)) + selectedChapterIds.addOrRemove(item.update.chapterId, selected) if (selected && userSelected && fromLongPress) { if (firstSelection) { @@ -303,6 +307,7 @@ class UpdatesPresenter( range.forEach { val inbetweenItem = get(it) if (!inbetweenItem.selected) { + selectedChapterIds.add(inbetweenItem.update.chapterId) set(it, inbetweenItem.copy(selected = true)) } } @@ -327,6 +332,7 @@ class UpdatesPresenter( fun toggleAllSelection(selected: Boolean) { state.items = items.map { + selectedChapterIds.addOrRemove(it.update.chapterId, selected) it.copy(selected = selected) } selectedPositions[0] = -1 @@ -335,6 +341,7 @@ class UpdatesPresenter( fun invertSelection() { state.items = items.map { + selectedChapterIds.addOrRemove(it.update.chapterId, !it.selected) it.copy(selected = !it.selected) } selectedPositions[0] = -1