From c68efd0e86f929c202edb38a69789644c194e39f Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 8 Aug 2026 20:04:29 +0600 Subject: [PATCH] Make DownloadQueueViewModel collect downloads only while subscribed (#3727) Assisted-by: Claude:claude-opus-5 --- .../ui/download/DownloadQueueScreen.kt | 6 +-- .../ui/download/DownloadQueueViewModel.kt | 37 ++++++++----------- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueScreen.kt index 11c4c88bf..c78800677 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueScreen.kt @@ -19,7 +19,6 @@ import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.animateFloatingActionButton import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -41,6 +40,7 @@ import androidx.compose.ui.unit.sp import androidx.compose.ui.viewinterop.AndroidView import androidx.core.view.ViewCompat import androidx.core.view.updatePadding +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import androidx.recyclerview.widget.LinearLayoutManager import cafe.adriel.voyager.navigator.LocalNavigator @@ -66,7 +66,7 @@ object DownloadQueueScreen : Screen() { val navigator = LocalNavigator.currentOrThrow val scope = rememberCoroutineScope() val viewModel = viewModel() - val downloadList by viewModel.state.collectAsState() + val downloadList by viewModel.state.collectAsStateWithLifecycle() val downloadCount by remember { derivedStateOf { downloadList.sumOf { it.subItems.size } } } @@ -198,7 +198,7 @@ object DownloadQueueScreen : Screen() { ) }, floatingActionButton = { - val isRunning by viewModel.isDownloaderRunning.collectAsState() + val isRunning by viewModel.isDownloaderRunning.collectAsStateWithLifecycle() SmallExtendedFloatingActionButton( text = { val id = if (isRunning) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueViewModel.kt index 5601ae07a..9b9cd3ef4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueViewModel.kt @@ -10,27 +10,36 @@ import eu.kanade.tachiyomi.databinding.DownloadListBinding import eu.kanade.tachiyomi.source.model.Page import kotlinx.coroutines.Job import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.WhileSubscribed import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.seconds class DownloadQueueViewModel( private val downloadManager: DownloadManager = Injekt.get(), ) : ViewModel() { - private val _state = MutableStateFlow(emptyList()) - val state = _state.asStateFlow() + val state: StateFlow> = downloadManager.queueState + .map { downloads -> + downloads + .groupBy { it.source } + .map { entry -> + DownloadHeaderItem(entry.key.id, entry.key.name, entry.value.size).apply { + addSubItems(0, entry.value.map { DownloadItem(it, this) }) + } + } + } + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5.seconds), emptyList()) lateinit var controllerBinding: DownloadListBinding @@ -116,22 +125,6 @@ class DownloadQueueViewModel( } } - init { - viewModelScope.launch { - downloadManager.queueState - .map { downloads -> - downloads - .groupBy { it.source } - .map { entry -> - DownloadHeaderItem(entry.key.id, entry.key.name, entry.value.size).apply { - addSubItems(0, entry.value.map { DownloadItem(it, this) }) - } - } - } - .collect { newList -> _state.update { newList } } - } - } - override fun onCleared() { for (job in progressJobs.values) { job.cancel() @@ -141,7 +134,7 @@ class DownloadQueueViewModel( } val isDownloaderRunning = downloadManager.isDownloaderRunning - .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), false) + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5.seconds), false) fun getDownloadStatusFlow() = downloadManager.statusFlow() fun getDownloadProgressFlow() = downloadManager.progressFlow()