Use DownloadStatOperation to update screen

This commit is contained in:
semenvav 2023-11-04 15:30:19 +02:00
parent c00fe31a3f
commit 75fd51d328
11 changed files with 105 additions and 125 deletions

View file

@ -0,0 +1,26 @@
package eu.kanade.presentation.more.download
import eu.kanade.presentation.more.download.data.DownloadStatManga
fun getDownloadStatMangaSort(
sortingMode: SortingMode,
sortDescending: Boolean,
): (
DownloadStatManga,
DownloadStatManga,
) -> Int {
return when (sortingMode) {
SortingMode.BY_ALPHABET -> when (sortDescending) {
true -> { m1, m2 -> m1.libraryManga.manga.title.compareTo(m2.libraryManga.manga.title) }
false -> { m1, m2 -> m2.libraryManga.manga.title.compareTo(m1.libraryManga.manga.title) }
}
SortingMode.BY_SIZE -> when (sortDescending) {
true -> { m1, m2 -> m2.folderSize.compareTo(m1.folderSize) }
false -> { m1, m2 -> m1.folderSize.compareTo(m2.folderSize) }
}
SortingMode.BY_CHAPTERS -> when (sortDescending) {
true -> { m1, m2 -> m2.downloadChaptersCount.compareTo(m1.downloadChaptersCount) }
false -> { m1, m2 -> m1.downloadChaptersCount.compareTo(m2.downloadChaptersCount) }
}
}
}

View file

@ -86,8 +86,8 @@ class DownloadStatsScreen : Screen() {
sortMode = state.sortMode,
groupByMode = state.groupByMode,
showNotDownloaded = state.showNotDownloaded,
onSort = screenModel::runSort,
onGroup = screenModel::runGroupBy,
onSort = screenModel::changeSortMode,
onGroup = screenModel::changeGroupByMode,
toggleShowNotDownloaded = screenModel::toggleShowNoDownload,
)
}

View file

@ -4,7 +4,7 @@ import android.content.Context
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.coroutineScope
import cafe.adriel.voyager.core.model.screenModelScope
import eu.kanade.core.preference.asState
import eu.kanade.presentation.more.download.components.graphic.GraphGroupByMode
import eu.kanade.presentation.more.download.components.graphic.GraphicPoint
@ -14,6 +14,8 @@ import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.download.DownloadProvider
import eu.kanade.tachiyomi.util.lang.toRelativeString
import eu.kanade.tachiyomi.util.storage.DiskUtil
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.runBlocking
import tachiyomi.core.preference.PreferenceStore
@ -48,74 +50,82 @@ class DownloadStatsScreenModel(
private val getManga: GetManga = Injekt.get(),
) : StateScreenModel<DownloadStatsScreenState>(DownloadStatsScreenState()) {
var activeCategoryIndex: Int by preferenceStore.getInt("downloadStatSelectedTab", 0).asState(coroutineScope)
var activeCategoryIndex: Int by preferenceStore.getInt("downloadStatSelectedTab", 0).asState(screenModelScope)
private lateinit var lastSelectedManga: LibraryManga
init {
coroutineScope.launchIO {
screenModelScope.launchIO {
val sortMode = preferenceStore.getEnum("sort_mode", SortingMode.BY_ALPHABET).get()
mutableState.update { state ->
val categories = getCategories.await().associateBy { group -> group.id }
val operations = getDownloadStatOperations.await()
state.copy(
items = getLibraryManga.await().filter { libraryManga ->
(downloadManager.getDownloadCount(libraryManga.manga) > 0) || operations.any { it.mangaId == libraryManga.id }
}.mapNotNull { libraryManga ->
items = getLibraryManga.await().map { libraryManga ->
val source = sourceManager.getOrStub(libraryManga.manga.source)
val path = downloadProvider.findMangaDir(
libraryManga.manga.title,
source,
)?.filePath
val downloadChaptersCount = downloadManager.getDownloadCount(libraryManga.manga)
if (downloadChaptersCount == 0) {
DownloadStatManga(
libraryManga = libraryManga,
source = source,
category = categories[libraryManga.category]!!,
)
} else if (path != null) {
DownloadStatManga(
libraryManga = libraryManga,
source = source,
folderSize = DiskUtil.getDirectorySize(File(path)),
downloadChaptersCount = downloadChaptersCount,
category = categories[libraryManga.category]!!,
)
} else {
null
}
DownloadStatManga(
libraryManga = libraryManga,
source = source,
folderSize = if(path != null) DiskUtil.getDirectorySize(File(path)) else 0,
downloadChaptersCount = downloadManager.getDownloadCount(libraryManga.manga),
category = categories[libraryManga.category]!!,
)
},
groupByMode = preferenceStore.getEnum("group_by_mode", GroupByMode.NONE).get(),
sortMode = sortMode,
descendingOrder = preferenceStore.getBoolean("descending_order", false).get(),
searchQuery = preferenceStore.getString("search_query", "").get().takeIf { string -> string != "" },
downloadStatOperations = operations,
downloadStatOperations = getDownloadStatOperations.await(),
showNotDownloaded = preferenceStore.getBoolean("show_no_downloaded", false).get(),
isLoading = false,
)
}
runSort(sortMode, true)
}
screenModelScope.launchIO {
getDownloadStatOperations.subscribe().distinctUntilChanged().collectLatest { operations ->
mutableState.update { state ->
val oldOperationsId = state.downloadStatOperations.map { it.id }.toHashSet()
val newOperations = operations.mapNotNull { if(!oldOperationsId.contains(it.id)) it else null }.groupBy { it.mangaId }
val newItems = state.items.map { item ->
if(newOperations.containsKey(item.libraryManga.id)){
item.copy(
folderSize = item.folderSize + newOperations[item.libraryManga.id]!!.sumOf { it.size },
downloadChaptersCount = item.downloadChaptersCount + newOperations[item.libraryManga.id]!!.sumOf { it.units }.toInt(),
)
} else item
}
state.copy(
items = newItems,
downloadStatOperations = operations,
)
}
}
}
}
fun runSort(
fun changeSortMode(
mode: SortingMode,
initSort: Boolean = false,
) {
when (mode) {
SortingMode.BY_ALPHABET -> sortByAlphabet(initSort)
SortingMode.BY_SIZE -> sortBySize(initSort)
SortingMode.BY_CHAPTERS -> sortByChapters(initSort)
mutableState.update { state ->
val descendingOrder = if (state.sortMode == mode) !state.descendingOrder else false
preferenceStore.getBoolean("descending_order", false).set(descendingOrder)
state.copy(
descendingOrder = descendingOrder,
sortMode = mode,
)
}
preferenceStore.getEnum("sort_mode", SortingMode.BY_ALPHABET).set(mode)
}
fun runGroupBy(mode: GroupByMode) {
when (mode) {
GroupByMode.NONE -> unGroup()
GroupByMode.BY_CATEGORY -> groupByCategory()
GroupByMode.BY_SOURCE -> groupBySource()
fun changeGroupByMode(mode: GroupByMode) {
mutableState.update {
it.copy(
groupByMode = mode,
)
}
preferenceStore.getEnum("group_by_mode", GroupByMode.NONE).set(mode)
}
@ -129,42 +139,6 @@ class DownloadStatsScreenModel(
}
}
private fun sortByAlphabet(initSort: Boolean) {
mutableState.update { state ->
val descendingOrder = if (initSort) state.descendingOrder else if (state.sortMode == SortingMode.BY_ALPHABET) !state.descendingOrder else false
preferenceStore.getBoolean("descending_order", false).set(descendingOrder)
state.copy(
items = if (descendingOrder) state.items.sortedByDescending { it.libraryManga.manga.title } else state.items.sortedBy { it.libraryManga.manga.title },
descendingOrder = descendingOrder,
sortMode = SortingMode.BY_ALPHABET,
)
}
}
private fun sortBySize(initSort: Boolean) {
mutableState.update { state ->
val descendingOrder = if (initSort) state.descendingOrder else if (state.sortMode == SortingMode.BY_SIZE) !state.descendingOrder else false
preferenceStore.getBoolean("descending_order", false).set(descendingOrder)
state.copy(
items = if (descendingOrder) state.items.sortedByDescending { it.folderSize } else state.items.sortedBy { it.folderSize },
descendingOrder = descendingOrder,
sortMode = SortingMode.BY_SIZE,
)
}
}
private fun sortByChapters(initSort: Boolean) {
mutableState.update { state ->
val descendingOrder = if (initSort) state.descendingOrder else if (state.sortMode == SortingMode.BY_CHAPTERS) !state.descendingOrder else false
preferenceStore.getBoolean("descending_order", false).set(descendingOrder)
state.copy(
items = if (descendingOrder) state.items.sortedByDescending { it.downloadChaptersCount } else state.items.sortedBy { it.downloadChaptersCount },
descendingOrder = descendingOrder,
sortMode = SortingMode.BY_CHAPTERS,
)
}
}
fun categoryMap(
items: List<DownloadStatManga>,
groupMode: GroupByMode,
@ -186,13 +160,13 @@ class DownloadStatsScreenModel(
sortedMap
}
SortingMode.BY_SIZE -> {
val compareFun: (String) -> Comparable<*> = { it: String -> unsortedMap[it]?.sumOf { manga -> manga.folderSize } ?: 0 }
val compareFun: (String) -> Comparable<*> = { unsortedMap[it]?.sumOf { manga -> manga.folderSize } ?: 0 }
val sortedMap = TreeMap<String, List<DownloadStatManga>>(if (descendingOrder) { compareByDescending { compareFun(it) } } else { compareBy { compareFun(it) } })
sortedMap.putAll(unsortedMap)
sortedMap
}
SortingMode.BY_CHAPTERS -> {
val compareFun: (String) -> Comparable<*> = { it: String -> unsortedMap[it]?.sumOf { manga -> manga.downloadChaptersCount } ?: 0 }
val compareFun: (String) -> Comparable<*> = { unsortedMap[it]?.sumOf { manga -> manga.downloadChaptersCount } ?: 0 }
val sortedMap = TreeMap<String, List<DownloadStatManga>>(if (descendingOrder) { compareByDescending { compareFun(it) } } else { compareBy { compareFun(it) } })
sortedMap.putAll(unsortedMap)
sortedMap
@ -200,30 +174,6 @@ class DownloadStatsScreenModel(
}
}
private fun groupBySource() {
mutableState.update {
it.copy(
groupByMode = GroupByMode.BY_SOURCE,
)
}
}
private fun groupByCategory() {
mutableState.update {
it.copy(
groupByMode = GroupByMode.BY_CATEGORY,
)
}
}
private fun unGroup() {
mutableState.update {
it.copy(
groupByMode = GroupByMode.NONE,
)
}
}
fun toggleSelection(
item: DownloadStatManga,
) {
@ -246,17 +196,13 @@ class DownloadStatsScreenModel(
val processedItems = if (state.groupByMode == GroupByMode.NONE) {
state.processedItems(false)
} else {
val temp = mutableListOf<DownloadStatManga>()
categoryMap(
items = state.processedItems(false),
groupMode = state.groupByMode,
sortMode = state.sortMode,
descendingOrder = state.descendingOrder,
defaultCategoryName = null,
).map {
temp.addAll(it.value)
}
temp
).flatMap { it.value }
}
val lastSelectedIndex =
processedItems.indexOfFirst { it.libraryManga.id == lastSelectedManga.id && it.libraryManga.category == lastSelectedManga.category }
@ -344,18 +290,11 @@ class DownloadStatsScreenModel(
}
fun deleteMangas(manga: List<DownloadStatManga>) {
coroutineScope.launchNonCancellable {
screenModelScope.launchNonCancellable {
manga.forEach { manga ->
downloadManager.deleteManga(manga.libraryManga.manga, manga.source)
}
}
val toDeleteIds = manga.map { it.libraryManga.manga.id }.toHashSet()
toggleAllSelection(false)
mutableState.update { state ->
state.copy(
items = state.items.map { if (it.libraryManga.manga.id in toDeleteIds) it.copy(downloadChaptersCount = 0, folderSize = 0) else it },
)
}
}
fun showDeleteAlert(items: List<DownloadStatManga>) {
@ -495,6 +434,6 @@ sealed class Dialog {
data class DeleteManga(val items: List<DownloadStatManga>) : Dialog()
data class DownloadStatOperationInfo(val downloadStatOperation: DownloadStatOperation) : Dialog()
data class MultiMangaDownloadStatOperationInfo(val downloadStatOperation: List<DownloadStatOperation>) : Dialog()
object DownloadStatOperationStart : Dialog()
object SettingsSheet : Dialog()
data object DownloadStatOperationStart : Dialog()
data object SettingsSheet : Dialog()
}

View file

@ -1,6 +1,7 @@
package eu.kanade.presentation.more.download
import androidx.compose.runtime.Immutable
import androidx.compose.ui.util.fastAny
import eu.kanade.presentation.more.download.data.DownloadStatManga
import tachiyomi.domain.stat.model.DownloadStatOperation
@ -31,9 +32,9 @@ data class DownloadStatsScreenState(
}
fun processedItems(unique: Boolean): List<DownloadStatManga> {
return (if (unique) uniqueItems() else items).filter {
if (!showNotDownloaded) it.downloadChaptersCount > 0 else true
}.filter {
return (if (unique) uniqueItems() else items)
.filter { item -> item.downloadChaptersCount > 0 || if (showNotDownloaded) downloadStatOperations.fastAny { it.mangaId == item.libraryManga.id } else false }
.filter {
if (searchQuery != null) {
it.libraryManga.manga.title.contains(searchQuery, true) ||
if (groupByMode == GroupByMode.BY_SOURCE) { it.source.name.contains(searchQuery, true) } else { false } ||
@ -42,5 +43,6 @@ data class DownloadStatsScreenState(
true
}
}
.sortedWith { manga1, manga2 -> getDownloadStatMangaSort(sortMode, descendingOrder).invoke(manga1, manga2) }
}
}

View file

@ -199,7 +199,7 @@ private fun MangaInfoColumn(
Text(
text = String.format(
stringResource(R.string.download_stat_operation_deleted),
deleteItems.sumOf { it.units },
abs(deleteItems.sumOf { it.units }),
folderSizeText(
folderSize = abs(deleteItems.sumOf { it.size }),
),

View file

@ -371,7 +371,7 @@ fun MangaDownloadStatSection(
TitleRow(
titles = listOf(
stringResource(R.string.deleted_chapters),
items.filter { it.size < 0 }.sumOf { it.units }.toString(),
abs(items.filter { it.size < 0 }.sumOf { it.units }).toString(),
folderSizeText(items.filter { it.size < 0 }.sumOf { abs(it.size) }),
),
titleStyle = MaterialTheme.typography.bodyMedium,

View file

@ -65,7 +65,7 @@ fun DeletedStatsRow(
StatsSection(R.string.deleted_chapters) {
Row {
StatsItem(
data.size.toString(),
abs(data.sumOf { it.units }).toString(),
stringResource(R.string.label_total_chapters),
)
StatsItem(

View file

@ -232,7 +232,7 @@ class DownloadManager(
DownloadStatOperation.create().copy(
mangaId = manga.id,
size = chapterDirs.sumOf { DiskUtil.getDirectorySize(File(it.filePath!!)) } * -1,
units = filteredChapters.size.toLong(),
units = filteredChapters.size.toLong() * -1,
),
)
@ -265,7 +265,7 @@ class DownloadManager(
DownloadStatOperation.create().copy(
mangaId = manga.id,
size = dirSize * -1,
units = cache.getDownloadCount(manga).toLong(),
units = cache.getDownloadCount(manga).toLong() * -1,
),
)
}

View file

@ -1,5 +1,6 @@
package tachiyomi.data.stat
import kotlinx.coroutines.flow.Flow
import tachiyomi.data.DatabaseHandler
import tachiyomi.domain.stat.model.DownloadStatOperation
import tachiyomi.domain.stat.repository.DownloadStatRepository
@ -12,6 +13,10 @@ class DownloadStatRepositoryImpl(
return handler.awaitList { download_statQueries.getStatOperations(DownloadStatActionMapper) }
}
override suspend fun getStatOperationsAsFlow(): Flow<List<DownloadStatOperation>> {
return handler.subscribeToList{ download_statQueries.getStatOperations(DownloadStatActionMapper) }
}
override suspend fun insert(operation: DownloadStatOperation) {
handler.await {
download_statQueries.insert(

View file

@ -1,5 +1,6 @@
package tachiyomi.domain.stat.interactor
import kotlinx.coroutines.flow.Flow
import tachiyomi.domain.stat.model.DownloadStatOperation
import tachiyomi.domain.stat.repository.DownloadStatRepository
@ -10,4 +11,8 @@ class GetDownloadStatOperations(
suspend fun await(): List<DownloadStatOperation> {
return repository.getStatOperations()
}
suspend fun subscribe(): Flow<List<DownloadStatOperation>> {
return repository.getStatOperationsAsFlow()
}
}

View file

@ -1,10 +1,13 @@
package tachiyomi.domain.stat.repository
import kotlinx.coroutines.flow.Flow
import tachiyomi.domain.stat.model.DownloadStatOperation
interface DownloadStatRepository {
suspend fun getStatOperations(): List<DownloadStatOperation>
suspend fun getStatOperationsAsFlow(): Flow<List<DownloadStatOperation>>
suspend fun insert(operation: DownloadStatOperation)
}