Make MigrateSourceViewModel collect sources only while subscribed (#3723)

Assisted-by: Claude:claude-opus-5
This commit is contained in:
AntsyLich
2026-08-08 11:59:17 +06:00
committed by GitHub
parent fab4cbd279
commit 3cb9883d62
2 changed files with 35 additions and 43 deletions
@@ -3,9 +3,9 @@ package eu.kanade.tachiyomi.ui.browse.migration.sources
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.HelpOutline import androidx.compose.material.icons.automirrored.outlined.HelpOutline
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.platform.LocalUriHandler
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
@@ -22,7 +22,7 @@ fun Screen.migrateSourceTab(): TabContent {
val uriHandler = LocalUriHandler.current val uriHandler = LocalUriHandler.current
val navigator = LocalNavigator.currentOrThrow val navigator = LocalNavigator.currentOrThrow
val viewModel = viewModel<MigrateSourceViewModel>() val viewModel = viewModel<MigrateSourceViewModel>()
val state by viewModel.state.collectAsState() val state by viewModel.state.collectAsStateWithLifecycle()
return TabContent( return TabContent(
titleRes = MR.strings.label_migration, titleRes = MR.strings.label_migration,
@@ -1,79 +1,71 @@
package eu.kanade.tachiyomi.ui.browse.migration.sources package eu.kanade.tachiyomi.ui.browse.migration.sources
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount
import eu.kanade.domain.source.interactor.SetMigrateSorting import eu.kanade.domain.source.interactor.SetMigrateSorting
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.WhileSubscribed
import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.stateIn
import logcat.LogPriority import logcat.LogPriority
import mihon.core.viewmodel.StateViewModel import tachiyomi.core.common.preference.getAndSet
import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.core.common.util.system.logcat import tachiyomi.core.common.util.system.logcat
import tachiyomi.domain.source.model.Source import tachiyomi.domain.source.model.Source
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import kotlin.time.Duration.Companion.seconds
class MigrateSourceViewModel( class MigrateSourceViewModel(
preferences: SourcePreferences = Injekt.get(), private val preferences: SourcePreferences = Injekt.get(),
private val getSourcesWithFavoriteCount: GetSourcesWithFavoriteCount = Injekt.get(), private val getSourcesWithFavoriteCount: GetSourcesWithFavoriteCount = Injekt.get(),
private val setMigrateSorting: SetMigrateSorting = Injekt.get(), ) : ViewModel() {
) : StateViewModel<MigrateSourceViewModel.State>(State()) {
private val _channel = Channel<Event>(Int.MAX_VALUE) private val _channel = Channel<Event>(Int.MAX_VALUE)
val channel = _channel.receiveAsFlow() val channel = _channel.receiveAsFlow()
init { val state: StateFlow<State> = combine(
viewModelScope.launchIO {
getSourcesWithFavoriteCount.subscribe() getSourcesWithFavoriteCount.subscribe()
.catch { .catch {
logcat(LogPriority.ERROR, it) logcat(LogPriority.ERROR, it)
_channel.send(Event.FailedFetchingSourcesWithCount) _channel.send(Event.FailedFetchingSourcesWithCount)
} },
.collectLatest { sources -> preferences.migrationSortingMode.changes(),
mutableState.update { preferences.migrationSortingDirection.changes(),
it.copy( ) { sources, sortingMode, sortingDirection ->
State(
isLoading = false, isLoading = false,
items = sources, items = sources,
sortingMode = sortingMode,
sortingDirection = sortingDirection,
) )
} }
} .flowOn(Dispatchers.IO)
} .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5.seconds), State())
preferences.migrationSortingDirection.changes()
.onEach { mutableState.update { state -> state.copy(sortingDirection = it) } }
.launchIn(viewModelScope)
preferences.migrationSortingMode.changes()
.onEach { mutableState.update { state -> state.copy(sortingMode = it) } }
.launchIn(viewModelScope)
}
fun toggleSortingMode() { fun toggleSortingMode() {
with(state.value) { preferences.migrationSortingMode.getAndSet { mode ->
val newMode = when (sortingMode) { when (mode) {
SetMigrateSorting.Mode.ALPHABETICAL -> SetMigrateSorting.Mode.TOTAL SetMigrateSorting.Mode.ALPHABETICAL -> SetMigrateSorting.Mode.TOTAL
SetMigrateSorting.Mode.TOTAL -> SetMigrateSorting.Mode.ALPHABETICAL SetMigrateSorting.Mode.TOTAL -> SetMigrateSorting.Mode.ALPHABETICAL
} }
setMigrateSorting.await(newMode, sortingDirection)
} }
} }
fun toggleSortingDirection() { fun toggleSortingDirection() {
with(state.value) { preferences.migrationSortingDirection.getAndSet { direction ->
val newDirection = when (sortingDirection) { when (direction) {
SetMigrateSorting.Direction.ASCENDING -> SetMigrateSorting.Direction.DESCENDING SetMigrateSorting.Direction.ASCENDING -> SetMigrateSorting.Direction.DESCENDING
SetMigrateSorting.Direction.DESCENDING -> SetMigrateSorting.Direction.ASCENDING SetMigrateSorting.Direction.DESCENDING -> SetMigrateSorting.Direction.ASCENDING
} }
setMigrateSorting.await(sortingMode, newDirection)
} }
} }