Use DownloadStatOperation to update screen
This commit is contained in:
parent
c00fe31a3f
commit
75fd51d328
11 changed files with 105 additions and 125 deletions
|
@ -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) }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 }),
|
||||
),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Reference in a new issue