diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionStoresScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionStoresScreen.kt index a46b9dca8..570fc6768 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionStoresScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionStoresScreen.kt @@ -2,9 +2,9 @@ package eu.kanade.presentation.more.settings.screen.browse import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.platform.LocalContext +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow @@ -27,7 +27,7 @@ class ExtensionStoresScreen( val navigator = LocalNavigator.currentOrThrow val viewModel = viewModel() - val state by viewModel.state.collectAsState() + val state by viewModel.state.collectAsStateWithLifecycle() LaunchedEffect(url) { url?.let { viewModel.addFromDeeplink(url) } diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionStoresViewModel.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionStoresViewModel.kt index c5c7406e0..792c9c967 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionStoresViewModel.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionStoresViewModel.kt @@ -1,12 +1,19 @@ package eu.kanade.presentation.more.settings.screen.browse import androidx.compose.runtime.Immutable +import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import eu.kanade.tachiyomi.extension.ExtensionManager -import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.WhileSubscribed +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import mihon.core.viewmodel.StateViewModel import mihon.domain.extension.interactor.AddExtensionStore import mihon.domain.extension.interactor.GetExtensionStores import mihon.domain.extension.interactor.RemoveExtensionStore @@ -15,6 +22,7 @@ import mihon.domain.extension.model.ExtensionStore import tachiyomi.core.common.util.lang.launchIO import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get +import kotlin.time.Duration.Companion.seconds class ExtensionStoresViewModel( private val getExtensionStores: GetExtensionStores = Injekt.get(), @@ -22,32 +30,18 @@ class ExtensionStoresViewModel( private val removeExtensionStore: RemoveExtensionStore = Injekt.get(), private val updateExtensionStores: UpdateExtensionStores = Injekt.get(), private val extensionManager: ExtensionManager = Injekt.get(), -) : StateViewModel(ExtensionStoreScreenState.Loading) { +) : ViewModel() { - private inline fun updateSuccessState( - func: (ExtensionStoreScreenState.Success) -> ExtensionStoreScreenState.Success, - ) { - mutableState.update { - when (it) { - ExtensionStoreScreenState.Loading -> it - is ExtensionStoreScreenState.Success -> func(it) - } - } - } + private val dialog = MutableStateFlow(null) - init { - viewModelScope.launchIO { - getExtensionStores.subscribe() - .collectLatest { stores -> - mutableState.update { - when (it) { - ExtensionStoreScreenState.Loading -> ExtensionStoreScreenState.Success(stores = stores) - is ExtensionStoreScreenState.Success -> it.copy(stores = stores) - } - } - } - } + val state: StateFlow = combine( + getExtensionStores.subscribe(), + dialog, + ) { stores, dialog -> + ExtensionStoreScreenState.Success(stores = stores, dialog = dialog) } + .flowOn(Dispatchers.IO) + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5.seconds), ExtensionStoreScreenState.Loading) /** * Creates and adds a new repo to the database. @@ -56,14 +50,12 @@ class ExtensionStoresViewModel( */ fun createRepo(baseUrl: String) { viewModelScope.launch { - updateSuccessState { - it.copy( - dialog = when (it.dialog) { - is ExtensionStoreDialog.Create -> it.dialog.copy(processing = true) - is ExtensionStoreDialog.Confirm -> it.dialog.copy(processing = true) - else -> it.dialog - }, - ) + dialog.update { + when (it) { + is ExtensionStoreDialog.Create -> it.copy(processing = true) + is ExtensionStoreDialog.Confirm -> it.copy(processing = true) + else -> it + } } addExtensionStore(baseUrl) .onSuccess { @@ -71,20 +63,18 @@ class ExtensionStoresViewModel( dismissDialog() } .onFailure { throwable -> - updateSuccessState { - it.copy( - dialog = when (it.dialog) { - is ExtensionStoreDialog.Create -> it.dialog.copy( - processing = false, - errorMessage = throwable.message ?: "unknown error", - ) - is ExtensionStoreDialog.Confirm -> it.dialog.copy( - processing = false, - errorMessage = throwable.message ?: "unknown error", - ) - else -> it.dialog - }, - ) + dialog.update { + when (it) { + is ExtensionStoreDialog.Create -> it.copy( + processing = false, + errorMessage = throwable.message ?: "unknown error", + ) + is ExtensionStoreDialog.Confirm -> it.copy( + processing = false, + errorMessage = throwable.message ?: "unknown error", + ) + else -> it + } } } } @@ -94,12 +84,8 @@ class ExtensionStoresViewModel( * Refreshes information for each repository. */ fun refreshRepos() { - val status = state.value - - if (status is ExtensionStoreScreenState.Success) { - viewModelScope.launchIO { - updateExtensionStores() - } + viewModelScope.launchIO { + updateExtensionStores() } } @@ -114,26 +100,18 @@ class ExtensionStoresViewModel( } fun addFromDeeplink(storeIndexUrl: String) { - updateSuccessState { state -> - state.copy( - dialog = ExtensionStoreDialog.Confirm( - url = storeIndexUrl, - alreadyExists = state.stores.any { it.indexUrl == storeIndexUrl }, - ), - ) + viewModelScope.launchIO { + val alreadyExists = getExtensionStores.get().any { it.indexUrl == storeIndexUrl } + dialog.update { ExtensionStoreDialog.Confirm(url = storeIndexUrl, alreadyExists = alreadyExists) } } } fun showDialog(dialog: ExtensionStoreDialog) { - updateSuccessState { state -> - state.copy(dialog = dialog) - } + this.dialog.update { dialog } } fun dismissDialog() { - updateSuccessState { - it.copy(dialog = null) - } + dialog.update { null } } }