diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/extensions/ExtensionsScreen.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/extensions/ExtensionsScreen.kt index 7e7e8be8..f383aed6 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/extensions/ExtensionsScreen.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/extensions/ExtensionsScreen.kt @@ -8,6 +8,7 @@ package ca.gosyer.jui.ui.extensions import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import ca.gosyer.jui.ui.extensions.components.ExtensionsScreenContent import ca.gosyer.jui.ui.viewModel import cafe.adriel.voyager.core.screen.Screen @@ -20,11 +21,12 @@ class ExtensionsScreen : Screen { @Composable override fun Content() { val vm = viewModel { extensionsViewModel() } + val state by vm.state.collectAsState() ExtensionsScreenContent( extensions = vm.extensions.collectAsState().value, isLoading = vm.isLoading.collectAsState().value, - query = vm.searchQuery.collectAsState().value, + query = state.searchQuery, setQuery = vm::setQuery, enabledLangs = vm.enabledLangs.collectAsState().value, availableLangs = vm.availableLangs.collectAsState().value, diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/extensions/ExtensionsScreenViewModel.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/extensions/ExtensionsScreenViewModel.kt index 23b2d2c9..28c9d66b 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/extensions/ExtensionsScreenViewModel.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/extensions/ExtensionsScreenViewModel.kt @@ -31,7 +31,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update @@ -41,6 +40,12 @@ import okio.FileSystem import okio.Source import kotlin.random.Random +data class ExtensionsState( + val extensionList: List? = null, + val searchQuery: String? = null, + val workingExtensions: List = emptyList() +) + @Inject class ExtensionsScreenViewModel( private val getExtensionList: GetExtensionList, @@ -51,31 +56,32 @@ class ExtensionsScreenViewModel( extensionPreferences: ExtensionPreferences, contextWrapper: ContextWrapper, ) : ViewModel(contextWrapper) { - private val extensionList = MutableStateFlow?>(null) + private val _state = MutableStateFlow(ExtensionsState()) + val state = _state.asStateFlow() private val _enabledLangs = extensionPreferences.languages().asStateFlow() val enabledLangs = _enabledLangs.map { it.toImmutableSet() } .stateIn(scope, SharingStarted.Eagerly, persistentSetOf()) - private val _searchQuery = MutableStateFlow(null) - val searchQuery = _searchQuery.asStateFlow() - - private val workingExtensions = MutableStateFlow>(emptyList()) - val extensions = combine( - searchQuery, - extensionList, - enabledLangs, - workingExtensions, - ) { searchQuery, extensions, enabledLangs, workingExtensions -> - search(searchQuery, extensions, enabledLangs, workingExtensions) + _state, + enabledLangs + ) { state, enabledLangs -> + search( + state.searchQuery, + state.extensionList, + enabledLangs, + state.workingExtensions + ) }.stateIn(scope, SharingStarted.Eagerly, persistentListOf()) - val availableLangs = extensionList.filterNotNull().map { langs -> - langs.map { it.lang }.distinct().toImmutableList() + val availableLangs = _state.map { state -> + state.extensionList?.map { it.lang }.orEmpty().distinct().toImmutableList() }.stateIn(scope, SharingStarted.Eagerly, persistentListOf()) - val isLoading = extensionList.map { it == null }.stateIn(scope, SharingStarted.Eagerly, true) + val isLoading = _state.map { state -> + state.extensionList == null + }.stateIn(scope, SharingStarted.Eagerly, true) init { scope.launch { @@ -84,7 +90,10 @@ class ExtensionsScreenViewModel( } private suspend fun getExtensions() { - extensionList.value = getExtensionList.await(onError = { toast(it.message.orEmpty()) }).orEmpty() + val extensionList = getExtensionList.await(onError = { toast(it.message.orEmpty()) }).orEmpty() + _state.update { current -> + current.copy(extensionList = extensionList) + } } fun install(source: Source) { @@ -99,7 +108,6 @@ class ExtensionsScreenViewModel( installExtensionFile.await(file, onError = { toast(it.message.orEmpty()) }) } catch (e: Exception) { log.warn(e) { "Error creating apk file" } - // todo toast if error e.throwIfCancellation() } @@ -110,30 +118,42 @@ class ExtensionsScreenViewModel( fun install(extension: Extension) { log.info { "Install clicked" } scope.launch { - workingExtensions.update { it + extension.apkName } + _state.update { current -> + current.copy(workingExtensions = current.workingExtensions + extension.apkName) + } installExtension.await(extension, onError = { toast(it.message.orEmpty()) }) getExtensions() - workingExtensions.update { it - extension.apkName } + _state.update { current -> + current.copy(workingExtensions = current.workingExtensions - extension.apkName) + } } } fun update(extension: Extension) { log.info { "Update clicked" } scope.launch { - workingExtensions.update { it + extension.apkName } + _state.update { current -> + current.copy(workingExtensions = current.workingExtensions + extension.apkName) + } updateExtension.await(extension, onError = { toast(it.message.orEmpty()) }) getExtensions() - workingExtensions.update { it - extension.apkName } + _state.update { current -> + current.copy(workingExtensions = current.workingExtensions - extension.apkName) + } } } fun uninstall(extension: Extension) { log.info { "Uninstall clicked" } scope.launch { - workingExtensions.update { it + extension.apkName } + _state.update { current -> + current.copy(workingExtensions = current.workingExtensions + extension.apkName) + } uninstallExtension.await(extension, onError = { toast(it.message.orEmpty()) }) getExtensions() - workingExtensions.update { it - extension.apkName } + _state.update { current -> + current.copy(workingExtensions = current.workingExtensions - extension.apkName) + } } } @@ -142,7 +162,9 @@ class ExtensionsScreenViewModel( } fun setQuery(query: String) { - _searchQuery.value = query + _state.update { current -> + current.copy(searchQuery = query) + } } private fun search( diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/manga/MangaScreenViewModel.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/manga/MangaScreenViewModel.kt index 899a934e..359b1e21 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/manga/MangaScreenViewModel.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/manga/MangaScreenViewModel.kt @@ -123,7 +123,7 @@ class MangaScreenViewModel( .map { dateHandler.getDateFormat(it) } - .asStateFlow(dateHandler.getDateFormat(uiPreferences.dateFormat().get())) + .stateIn(scope, SharingStarted.Eagerly, dateHandler.getDateFormat(uiPreferences.dateFormat().get())) init { DownloadService.registerWatch(params.mangaId)