Make ExtensionStoresViewModel collect stores only while subscribed (#3717)
Assisted-by: Claude:claude-opus-5
This commit is contained in:
+2
-2
@@ -2,9 +2,9 @@ package eu.kanade.presentation.more.settings.screen.browse
|
|||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
@@ -27,7 +27,7 @@ class ExtensionStoresScreen(
|
|||||||
val navigator = LocalNavigator.currentOrThrow
|
val navigator = LocalNavigator.currentOrThrow
|
||||||
|
|
||||||
val viewModel = viewModel<ExtensionStoresViewModel>()
|
val viewModel = viewModel<ExtensionStoresViewModel>()
|
||||||
val state by viewModel.state.collectAsState()
|
val state by viewModel.state.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
LaunchedEffect(url) {
|
LaunchedEffect(url) {
|
||||||
url?.let { viewModel.addFromDeeplink(url) }
|
url?.let { viewModel.addFromDeeplink(url) }
|
||||||
|
|||||||
+36
-58
@@ -1,12 +1,19 @@
|
|||||||
package eu.kanade.presentation.more.settings.screen.browse
|
package eu.kanade.presentation.more.settings.screen.browse
|
||||||
|
|
||||||
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.tachiyomi.extension.ExtensionManager
|
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.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import mihon.core.viewmodel.StateViewModel
|
|
||||||
import mihon.domain.extension.interactor.AddExtensionStore
|
import mihon.domain.extension.interactor.AddExtensionStore
|
||||||
import mihon.domain.extension.interactor.GetExtensionStores
|
import mihon.domain.extension.interactor.GetExtensionStores
|
||||||
import mihon.domain.extension.interactor.RemoveExtensionStore
|
import mihon.domain.extension.interactor.RemoveExtensionStore
|
||||||
@@ -15,6 +22,7 @@ import mihon.domain.extension.model.ExtensionStore
|
|||||||
import tachiyomi.core.common.util.lang.launchIO
|
import tachiyomi.core.common.util.lang.launchIO
|
||||||
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 ExtensionStoresViewModel(
|
class ExtensionStoresViewModel(
|
||||||
private val getExtensionStores: GetExtensionStores = Injekt.get(),
|
private val getExtensionStores: GetExtensionStores = Injekt.get(),
|
||||||
@@ -22,32 +30,18 @@ class ExtensionStoresViewModel(
|
|||||||
private val removeExtensionStore: RemoveExtensionStore = Injekt.get(),
|
private val removeExtensionStore: RemoveExtensionStore = Injekt.get(),
|
||||||
private val updateExtensionStores: UpdateExtensionStores = Injekt.get(),
|
private val updateExtensionStores: UpdateExtensionStores = Injekt.get(),
|
||||||
private val extensionManager: ExtensionManager = Injekt.get(),
|
private val extensionManager: ExtensionManager = Injekt.get(),
|
||||||
) : StateViewModel<ExtensionStoreScreenState>(ExtensionStoreScreenState.Loading) {
|
) : ViewModel() {
|
||||||
|
|
||||||
private inline fun updateSuccessState(
|
private val dialog = MutableStateFlow<ExtensionStoreDialog?>(null)
|
||||||
func: (ExtensionStoreScreenState.Success) -> ExtensionStoreScreenState.Success,
|
|
||||||
) {
|
|
||||||
mutableState.update {
|
|
||||||
when (it) {
|
|
||||||
ExtensionStoreScreenState.Loading -> it
|
|
||||||
is ExtensionStoreScreenState.Success -> func(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
val state: StateFlow<ExtensionStoreScreenState> = combine(
|
||||||
viewModelScope.launchIO {
|
getExtensionStores.subscribe(),
|
||||||
getExtensionStores.subscribe()
|
dialog,
|
||||||
.collectLatest { stores ->
|
) { stores, dialog ->
|
||||||
mutableState.update {
|
ExtensionStoreScreenState.Success(stores = stores, dialog = dialog)
|
||||||
when (it) {
|
|
||||||
ExtensionStoreScreenState.Loading -> ExtensionStoreScreenState.Success(stores = stores)
|
|
||||||
is ExtensionStoreScreenState.Success -> it.copy(stores = stores)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
.flowOn(Dispatchers.IO)
|
||||||
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5.seconds), ExtensionStoreScreenState.Loading)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates and adds a new repo to the database.
|
* Creates and adds a new repo to the database.
|
||||||
@@ -56,14 +50,12 @@ class ExtensionStoresViewModel(
|
|||||||
*/
|
*/
|
||||||
fun createRepo(baseUrl: String) {
|
fun createRepo(baseUrl: String) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
updateSuccessState {
|
dialog.update {
|
||||||
it.copy(
|
when (it) {
|
||||||
dialog = when (it.dialog) {
|
is ExtensionStoreDialog.Create -> it.copy(processing = true)
|
||||||
is ExtensionStoreDialog.Create -> it.dialog.copy(processing = true)
|
is ExtensionStoreDialog.Confirm -> it.copy(processing = true)
|
||||||
is ExtensionStoreDialog.Confirm -> it.dialog.copy(processing = true)
|
else -> it
|
||||||
else -> it.dialog
|
}
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
addExtensionStore(baseUrl)
|
addExtensionStore(baseUrl)
|
||||||
.onSuccess {
|
.onSuccess {
|
||||||
@@ -71,20 +63,18 @@ class ExtensionStoresViewModel(
|
|||||||
dismissDialog()
|
dismissDialog()
|
||||||
}
|
}
|
||||||
.onFailure { throwable ->
|
.onFailure { throwable ->
|
||||||
updateSuccessState {
|
dialog.update {
|
||||||
it.copy(
|
when (it) {
|
||||||
dialog = when (it.dialog) {
|
is ExtensionStoreDialog.Create -> it.copy(
|
||||||
is ExtensionStoreDialog.Create -> it.dialog.copy(
|
|
||||||
processing = false,
|
processing = false,
|
||||||
errorMessage = throwable.message ?: "unknown error",
|
errorMessage = throwable.message ?: "unknown error",
|
||||||
)
|
)
|
||||||
is ExtensionStoreDialog.Confirm -> it.dialog.copy(
|
is ExtensionStoreDialog.Confirm -> it.copy(
|
||||||
processing = false,
|
processing = false,
|
||||||
errorMessage = throwable.message ?: "unknown error",
|
errorMessage = throwable.message ?: "unknown error",
|
||||||
)
|
)
|
||||||
else -> it.dialog
|
else -> it
|
||||||
},
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,14 +84,10 @@ class ExtensionStoresViewModel(
|
|||||||
* Refreshes information for each repository.
|
* Refreshes information for each repository.
|
||||||
*/
|
*/
|
||||||
fun refreshRepos() {
|
fun refreshRepos() {
|
||||||
val status = state.value
|
|
||||||
|
|
||||||
if (status is ExtensionStoreScreenState.Success) {
|
|
||||||
viewModelScope.launchIO {
|
viewModelScope.launchIO {
|
||||||
updateExtensionStores()
|
updateExtensionStores()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes the given repo from the database
|
* Deletes the given repo from the database
|
||||||
@@ -114,26 +100,18 @@ class ExtensionStoresViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun addFromDeeplink(storeIndexUrl: String) {
|
fun addFromDeeplink(storeIndexUrl: String) {
|
||||||
updateSuccessState { state ->
|
viewModelScope.launchIO {
|
||||||
state.copy(
|
val alreadyExists = getExtensionStores.get().any { it.indexUrl == storeIndexUrl }
|
||||||
dialog = ExtensionStoreDialog.Confirm(
|
dialog.update { ExtensionStoreDialog.Confirm(url = storeIndexUrl, alreadyExists = alreadyExists) }
|
||||||
url = storeIndexUrl,
|
|
||||||
alreadyExists = state.stores.any { it.indexUrl == storeIndexUrl },
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showDialog(dialog: ExtensionStoreDialog) {
|
fun showDialog(dialog: ExtensionStoreDialog) {
|
||||||
updateSuccessState { state ->
|
this.dialog.update { dialog }
|
||||||
state.copy(dialog = dialog)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun dismissDialog() {
|
fun dismissDialog() {
|
||||||
updateSuccessState {
|
dialog.update { null }
|
||||||
it.copy(dialog = null)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user