parent
b1ccebf329
commit
10e349f76e
7 changed files with 79 additions and 56 deletions
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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 ->
|
||||||
|
|
|
@ -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 ->
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 }
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,27 +122,26 @@ class UpdatesPresenter(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun List<UpdatesWithRelations>.toUpdateItems(): List<UpdatesItem> {
|
private fun List<UpdatesWithRelations>.toUpdateItems(): List<UpdatesItem> {
|
||||||
return this
|
return this.map {
|
||||||
.distinctBy { it.chapterId }
|
val activeDownload = downloadManager.queue.find { download -> it.chapterId == download.chapter.id }
|
||||||
.map {
|
val downloaded = downloadManager.isChapterDownloaded(
|
||||||
val activeDownload = downloadManager.queue.find { download -> it.chapterId == download.chapter.id }
|
it.chapterName,
|
||||||
val downloaded = downloadManager.isChapterDownloaded(
|
it.scanlator,
|
||||||
it.chapterName,
|
it.mangaTitle,
|
||||||
it.scanlator,
|
it.sourceId,
|
||||||
it.mangaTitle,
|
)
|
||||||
it.sourceId,
|
val downloadState = when {
|
||||||
)
|
activeDownload != null -> activeDownload.status
|
||||||
val downloadState = when {
|
downloaded -> Download.State.DOWNLOADED
|
||||||
activeDownload != null -> activeDownload.status
|
else -> Download.State.NOT_DOWNLOADED
|
||||||
downloaded -> Download.State.DOWNLOADED
|
|
||||||
else -> Download.State.NOT_DOWNLOADED
|
|
||||||
}
|
|
||||||
UpdatesItem(
|
|
||||||
update = it,
|
|
||||||
downloadStateProvider = { downloadState },
|
|
||||||
downloadProgressProvider = { activeDownload?.progress ?: 0 },
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
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
|
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
|
||||||
|
|
Reference in a new issue