diff --git a/app/src/main/java/eu/kanade/presentation/more/download/DownloadStatMangaSort.kt b/app/src/main/java/eu/kanade/presentation/more/download/DownloadStatMangaSort.kt new file mode 100644 index 0000000000..5039157660 --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/more/download/DownloadStatMangaSort.kt @@ -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) } + } + } +} diff --git a/app/src/main/java/eu/kanade/presentation/more/download/DownloadStatsScreen.kt b/app/src/main/java/eu/kanade/presentation/more/download/DownloadStatsScreen.kt index e07ed970e5..b63e35f86d 100644 --- a/app/src/main/java/eu/kanade/presentation/more/download/DownloadStatsScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/download/DownloadStatsScreen.kt @@ -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, ) } diff --git a/app/src/main/java/eu/kanade/presentation/more/download/DownloadStatsScreenModel.kt b/app/src/main/java/eu/kanade/presentation/more/download/DownloadStatsScreenModel.kt index b9c0fc643d..048eb9dee5 100644 --- a/app/src/main/java/eu/kanade/presentation/more/download/DownloadStatsScreenModel.kt +++ b/app/src/main/java/eu/kanade/presentation/more/download/DownloadStatsScreenModel.kt @@ -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()) { - 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, 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>(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>(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() 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) { - 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) { @@ -495,6 +434,6 @@ sealed class Dialog { data class DeleteManga(val items: List) : Dialog() data class DownloadStatOperationInfo(val downloadStatOperation: DownloadStatOperation) : Dialog() data class MultiMangaDownloadStatOperationInfo(val downloadStatOperation: List) : Dialog() - object DownloadStatOperationStart : Dialog() - object SettingsSheet : Dialog() + data object DownloadStatOperationStart : Dialog() + data object SettingsSheet : Dialog() } diff --git a/app/src/main/java/eu/kanade/presentation/more/download/DownloadStatsScreenState.kt b/app/src/main/java/eu/kanade/presentation/more/download/DownloadStatsScreenState.kt index 51732512ac..c361f66aa7 100644 --- a/app/src/main/java/eu/kanade/presentation/more/download/DownloadStatsScreenState.kt +++ b/app/src/main/java/eu/kanade/presentation/more/download/DownloadStatsScreenState.kt @@ -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 { - 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) } } } diff --git a/app/src/main/java/eu/kanade/presentation/more/download/components/DownloadStatDialog.kt b/app/src/main/java/eu/kanade/presentation/more/download/components/DownloadStatDialog.kt index ecf3777b31..57329803e7 100644 --- a/app/src/main/java/eu/kanade/presentation/more/download/components/DownloadStatDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/more/download/components/DownloadStatDialog.kt @@ -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 }), ), diff --git a/app/src/main/java/eu/kanade/presentation/more/download/components/DownloadstatsContent.kt b/app/src/main/java/eu/kanade/presentation/more/download/components/DownloadstatsContent.kt index 41ce557b73..b3b7606184 100644 --- a/app/src/main/java/eu/kanade/presentation/more/download/components/DownloadstatsContent.kt +++ b/app/src/main/java/eu/kanade/presentation/more/download/components/DownloadstatsContent.kt @@ -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, diff --git a/app/src/main/java/eu/kanade/presentation/more/download/components/OverAllTabContent.kt b/app/src/main/java/eu/kanade/presentation/more/download/components/OverAllTabContent.kt index 6af5bde561..b3646ce53c 100644 --- a/app/src/main/java/eu/kanade/presentation/more/download/components/OverAllTabContent.kt +++ b/app/src/main/java/eu/kanade/presentation/more/download/components/OverAllTabContent.kt @@ -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( diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt index 4c1a1553eb..bf9c4150a6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt @@ -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, ), ) } diff --git a/data/src/main/java/tachiyomi/data/stat/DownloadStatRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/stat/DownloadStatRepositoryImpl.kt index 1a755a38ac..8e5311be77 100644 --- a/data/src/main/java/tachiyomi/data/stat/DownloadStatRepositoryImpl.kt +++ b/data/src/main/java/tachiyomi/data/stat/DownloadStatRepositoryImpl.kt @@ -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> { + return handler.subscribeToList{ download_statQueries.getStatOperations(DownloadStatActionMapper) } + } + override suspend fun insert(operation: DownloadStatOperation) { handler.await { download_statQueries.insert( diff --git a/domain/src/main/java/tachiyomi/domain/stat/interactor/GetDownloadStatOperations.kt b/domain/src/main/java/tachiyomi/domain/stat/interactor/GetDownloadStatOperations.kt index 371e14f3b0..c88589a946 100644 --- a/domain/src/main/java/tachiyomi/domain/stat/interactor/GetDownloadStatOperations.kt +++ b/domain/src/main/java/tachiyomi/domain/stat/interactor/GetDownloadStatOperations.kt @@ -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 { return repository.getStatOperations() } + + suspend fun subscribe(): Flow> { + return repository.getStatOperationsAsFlow() + } } diff --git a/domain/src/main/java/tachiyomi/domain/stat/repository/DownloadStatRepository.kt b/domain/src/main/java/tachiyomi/domain/stat/repository/DownloadStatRepository.kt index 77a2c9fcac..d5333c5034 100644 --- a/domain/src/main/java/tachiyomi/domain/stat/repository/DownloadStatRepository.kt +++ b/domain/src/main/java/tachiyomi/domain/stat/repository/DownloadStatRepository.kt @@ -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 + suspend fun getStatOperationsAsFlow(): Flow> + suspend fun insert(operation: DownloadStatOperation) }