Retain previous selected state when updating list states

Fixes #8417
This commit is contained in:
arkon 2022-11-13 22:27:59 -05:00
parent b1ccebf329
commit 10e349f76e
7 changed files with 79 additions and 56 deletions

View File

@ -25,3 +25,11 @@ inline fun <K, V, R> Map<out K, V>.mapNotNullKeys(transform: (Map.Entry<K?, V>)
forEach { element -> transform(element)?.let { mutableMap[it] = element.value } } forEach { element -> transform(element)?.let { mutableMap[it] = element.value } }
return mutableMap return mutableMap
} }
fun <E> HashSet<E>.addOrRemove(value: E, shouldAdd: Boolean) {
if (shouldAdd) {
add(value)
} else {
remove(value)
}
}

View File

@ -31,8 +31,8 @@ class MigrateMangaPresenter(
presenterScope.launchIO { presenterScope.launchIO {
getFavorites getFavorites
.subscribe(sourceId) .subscribe(sourceId)
.catch { exception -> .catch {
logcat(LogPriority.ERROR, exception) logcat(LogPriority.ERROR, it)
_events.send(Event.FailedFetchingFavorites) _events.send(Event.FailedFetchingFavorites)
} }
.map { list -> .map { list ->

View File

@ -32,8 +32,8 @@ class MigrationSourcesPresenter(
fun onCreate() { fun onCreate() {
presenterScope.launchIO { presenterScope.launchIO {
getSourcesWithFavoriteCount.subscribe() getSourcesWithFavoriteCount.subscribe()
.catch { exception -> .catch {
logcat(LogPriority.ERROR, exception) logcat(LogPriority.ERROR, it)
_channel.send(Event.FailedFetchingSourcesWithCount) _channel.send(Event.FailedFetchingSourcesWithCount)
} }
.collectLatest { sources -> .collectLatest { sources ->

View File

@ -40,8 +40,8 @@ class SourcesPresenter(
fun onCreate() { fun onCreate() {
presenterScope.launchIO { presenterScope.launchIO {
getEnabledSources.subscribe() getEnabledSources.subscribe()
.catch { exception -> .catch {
logcat(LogPriority.ERROR, exception) logcat(LogPriority.ERROR, it)
_events.send(Event.FailedFetchingSources) _events.send(Event.FailedFetchingSources)
} }
.onStart { delay(500) } // Defer to avoid crashing on initial render .onStart { delay(500) } // Defer to avoid crashing on initial render

View File

@ -35,7 +35,7 @@ class DownloadPresenter : BasePresenter<DownloadController>() {
presenterScope.launch { presenterScope.launch {
downloadQueue.updatedFlow() downloadQueue.updatedFlow()
.catch { error -> logcat(LogPriority.ERROR, error) } .catch { logcat(LogPriority.ERROR, it) }
.map { downloads -> .map { downloads ->
downloads downloads
.groupBy { it.source } .groupBy { it.source }

View File

@ -8,6 +8,7 @@ import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.coroutineScope import cafe.adriel.voyager.core.model.coroutineScope
import eu.kanade.core.prefs.CheckboxState import eu.kanade.core.prefs.CheckboxState
import eu.kanade.core.prefs.mapAsCheckboxState import eu.kanade.core.prefs.mapAsCheckboxState
import eu.kanade.core.util.addOrRemove
import eu.kanade.data.chapter.NoChaptersException import eu.kanade.data.chapter.NoChaptersException
import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.category.interactor.GetCategories import eu.kanade.domain.category.interactor.GetCategories
@ -84,6 +85,7 @@ class MangaInfoScreenModel(
basePreferences: BasePreferences = Injekt.get(), basePreferences: BasePreferences = Injekt.get(),
private val downloadPreferences: DownloadPreferences = Injekt.get(), private val downloadPreferences: DownloadPreferences = Injekt.get(),
private val libraryPreferences: LibraryPreferences = Injekt.get(), private val libraryPreferences: LibraryPreferences = Injekt.get(),
private val uiPreferences: UiPreferences = Injekt.get(),
private val trackManager: TrackManager = Injekt.get(), private val trackManager: TrackManager = Injekt.get(),
private val sourceManager: SourceManager = Injekt.get(), private val sourceManager: SourceManager = Injekt.get(),
private val downloadManager: DownloadManager = Injekt.get(), private val downloadManager: DownloadManager = Injekt.get(),
@ -120,6 +122,7 @@ class MangaInfoScreenModel(
get() = successState?.processedChapters get() = successState?.processedChapters
private val selectedPositions: Array<Int> = arrayOf(-1, -1) // first and last selected index in list private val selectedPositions: Array<Int> = arrayOf(-1, -1) // first and last selected index in list
private val selectedChapterIds: HashSet<Long> = HashSet()
/** /**
* Helper function to update the UI state only if it's currently in success state * Helper function to update the UI state only if it's currently in success state
@ -141,7 +144,6 @@ class MangaInfoScreenModel(
init { init {
val toChapterItemsParams: List<Chapter>.(manga: Manga) -> List<ChapterItem> = { manga -> val toChapterItemsParams: List<Chapter>.(manga: Manga) -> List<ChapterItem> = { manga ->
val uiPreferences = Injekt.get<UiPreferences>()
toChapterItems( toChapterItems(
context = context, context = context,
manga = manga, manga = manga,
@ -529,6 +531,7 @@ class MangaInfoScreenModel(
it + 1, it + 1,
) )
}, },
selected = chapter.id in selectedChapterIds,
) )
} }
} }
@ -832,51 +835,54 @@ class MangaInfoScreenModel(
) { ) {
updateSuccessState { successState -> updateSuccessState { successState ->
val newChapters = successState.processedChapters.toMutableList().apply { val newChapters = successState.processedChapters.toMutableList().apply {
val modifiedIndex = successState.processedChapters.indexOfFirst { it == item } val selectedIndex = successState.processedChapters.indexOfFirst { it.chapter.id == item.chapter.id }
if (modifiedIndex < 0) return@apply if (selectedIndex < 0) return@apply
val oldItem = get(modifiedIndex) val selectedItem = get(selectedIndex)
if ((oldItem.selected && selected) || (!oldItem.selected && !selected)) return@apply if ((selectedItem.selected && selected) || (!selectedItem.selected && !selected)) return@apply
val firstSelection = none { it.selected } val firstSelection = none { it.selected }
var newItem = removeAt(modifiedIndex) set(selectedIndex, selectedItem.copy(selected = selected))
add(modifiedIndex, newItem.copy(selected = selected)) selectedChapterIds.addOrRemove(item.chapter.id, selected)
if (selected && userSelected && fromLongPress) { if (selected && userSelected && fromLongPress) {
if (firstSelection) { if (firstSelection) {
selectedPositions[0] = modifiedIndex selectedPositions[0] = selectedIndex
selectedPositions[1] = modifiedIndex selectedPositions[1] = selectedIndex
} else { } else {
// Try to select the items in-between when possible // Try to select the items in-between when possible
val range: IntRange val range: IntRange
if (modifiedIndex < selectedPositions[0]) { if (selectedIndex < selectedPositions[0]) {
range = modifiedIndex + 1 until selectedPositions[0] range = selectedIndex + 1 until selectedPositions[0]
selectedPositions[0] = modifiedIndex selectedPositions[0] = selectedIndex
} else if (modifiedIndex > selectedPositions[1]) { } else if (selectedIndex > selectedPositions[1]) {
range = (selectedPositions[1] + 1) until modifiedIndex range = (selectedPositions[1] + 1) until selectedIndex
selectedPositions[1] = modifiedIndex selectedPositions[1] = selectedIndex
} else { } else {
// Just select itself // Just select itself
range = IntRange.EMPTY range = IntRange.EMPTY
} }
range.forEach { range.forEach {
newItem = removeAt(it) val inbetweenItem = get(it)
add(it, newItem.copy(selected = true)) if (!inbetweenItem.selected) {
selectedChapterIds.add(inbetweenItem.chapter.id)
set(it, inbetweenItem.copy(selected = true))
}
} }
} }
} else if (userSelected && !fromLongPress) { } else if (userSelected && !fromLongPress) {
if (!selected) { if (!selected) {
if (modifiedIndex == selectedPositions[0]) { if (selectedIndex == selectedPositions[0]) {
selectedPositions[0] = indexOfFirst { it.selected } selectedPositions[0] = indexOfFirst { it.selected }
} else if (modifiedIndex == selectedPositions[1]) { } else if (selectedIndex == selectedPositions[1]) {
selectedPositions[1] = indexOfLast { it.selected } selectedPositions[1] = indexOfLast { it.selected }
} }
} else { } else {
if (modifiedIndex < selectedPositions[0]) { if (selectedIndex < selectedPositions[0]) {
selectedPositions[0] = modifiedIndex selectedPositions[0] = selectedIndex
} else if (modifiedIndex > selectedPositions[1]) { } else if (selectedIndex > selectedPositions[1]) {
selectedPositions[1] = modifiedIndex selectedPositions[1] = selectedIndex
} }
} }
} }
@ -888,6 +894,7 @@ class MangaInfoScreenModel(
fun toggleAllSelection(selected: Boolean) { fun toggleAllSelection(selected: Boolean) {
updateSuccessState { successState -> updateSuccessState { successState ->
val newChapters = successState.chapters.map { val newChapters = successState.chapters.map {
selectedChapterIds.addOrRemove(it.chapter.id, selected)
it.copy(selected = selected) it.copy(selected = selected)
} }
selectedPositions[0] = -1 selectedPositions[0] = -1
@ -899,6 +906,7 @@ class MangaInfoScreenModel(
fun invertSelection() { fun invertSelection() {
updateSuccessState { successState -> updateSuccessState { successState ->
val newChapters = successState.chapters.map { val newChapters = successState.chapters.map {
selectedChapterIds.addOrRemove(it.chapter.id, !it.selected)
it.copy(selected = !it.selected) it.copy(selected = !it.selected)
} }
selectedPositions[0] = -1 selectedPositions[0] = -1

View File

@ -4,6 +4,7 @@ import android.os.Bundle
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import eu.kanade.core.util.addOrRemove
import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.chapter.interactor.GetChapter import eu.kanade.domain.chapter.interactor.GetChapter
import eu.kanade.domain.chapter.interactor.SetReadStatus import eu.kanade.domain.chapter.interactor.SetReadStatus
@ -72,6 +73,7 @@ class UpdatesPresenter(
// First and last selected index in list // First and last selected index in list
private val selectedPositions: Array<Int> = arrayOf(-1, -1) private val selectedPositions: Array<Int> = arrayOf(-1, -1)
private val selectedChapterIds: HashSet<Long> = HashSet()
override fun onCreate(savedState: Bundle?) { override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState) super.onCreate(savedState)
@ -100,7 +102,7 @@ class UpdatesPresenter(
presenterScope.launchIO { presenterScope.launchIO {
downloadManager.queue.statusFlow() downloadManager.queue.statusFlow()
.catch { error -> logcat(LogPriority.ERROR, error) } .catch { logcat(LogPriority.ERROR, it) }
.collect { .collect {
withUIContext { withUIContext {
updateDownloadState(it) updateDownloadState(it)
@ -110,7 +112,7 @@ class UpdatesPresenter(
presenterScope.launchIO { presenterScope.launchIO {
downloadManager.queue.progressFlow() downloadManager.queue.progressFlow()
.catch { error -> logcat(LogPriority.ERROR, error) } .catch { logcat(LogPriority.ERROR, it) }
.collect { .collect {
withUIContext { withUIContext {
updateDownloadState(it) updateDownloadState(it)
@ -120,9 +122,7 @@ class UpdatesPresenter(
} }
private fun List<UpdatesWithRelations>.toUpdateItems(): List<UpdatesItem> { private fun List<UpdatesWithRelations>.toUpdateItems(): List<UpdatesItem> {
return this return this.map {
.distinctBy { it.chapterId }
.map {
val activeDownload = downloadManager.queue.find { download -> it.chapterId == download.chapter.id } val activeDownload = downloadManager.queue.find { download -> it.chapterId == download.chapter.id }
val downloaded = downloadManager.isChapterDownloaded( val downloaded = downloadManager.isChapterDownloaded(
it.chapterName, it.chapterName,
@ -139,6 +139,7 @@ class UpdatesPresenter(
update = it, update = it,
downloadStateProvider = { downloadState }, downloadStateProvider = { downloadState },
downloadProgressProvider = { activeDownload?.progress ?: 0 }, downloadProgressProvider = { activeDownload?.progress ?: 0 },
selected = it.chapterId in selectedChapterIds,
) )
} }
} }
@ -155,12 +156,14 @@ class UpdatesPresenter(
} }
if (modifiedIndex < 0) return@apply if (modifiedIndex < 0) return@apply
val item = removeAt(modifiedIndex) val item = get(modifiedIndex)
.copy( set(
modifiedIndex,
item.copy(
downloadStateProvider = { download.status }, downloadStateProvider = { download.status },
downloadProgressProvider = { download.progress }, downloadProgressProvider = { download.progress },
),
) )
add(modifiedIndex, item)
} }
} }
@ -281,6 +284,7 @@ class UpdatesPresenter(
val firstSelection = none { it.selected } val firstSelection = none { it.selected }
set(selectedIndex, selectedItem.copy(selected = selected)) set(selectedIndex, selectedItem.copy(selected = selected))
selectedChapterIds.addOrRemove(item.update.chapterId, selected)
if (selected && userSelected && fromLongPress) { if (selected && userSelected && fromLongPress) {
if (firstSelection) { if (firstSelection) {
@ -303,6 +307,7 @@ class UpdatesPresenter(
range.forEach { range.forEach {
val inbetweenItem = get(it) val inbetweenItem = get(it)
if (!inbetweenItem.selected) { if (!inbetweenItem.selected) {
selectedChapterIds.add(inbetweenItem.update.chapterId)
set(it, inbetweenItem.copy(selected = true)) set(it, inbetweenItem.copy(selected = true))
} }
} }
@ -327,6 +332,7 @@ class UpdatesPresenter(
fun toggleAllSelection(selected: Boolean) { fun toggleAllSelection(selected: Boolean) {
state.items = items.map { state.items = items.map {
selectedChapterIds.addOrRemove(it.update.chapterId, selected)
it.copy(selected = selected) it.copy(selected = selected)
} }
selectedPositions[0] = -1 selectedPositions[0] = -1
@ -335,6 +341,7 @@ class UpdatesPresenter(
fun invertSelection() { fun invertSelection() {
state.items = items.map { state.items = items.map {
selectedChapterIds.addOrRemove(it.update.chapterId, !it.selected)
it.copy(selected = !it.selected) it.copy(selected = !it.selected)
} }
selectedPositions[0] = -1 selectedPositions[0] = -1