impl VBoxUpdateFetcher

This commit is contained in:
2026-07-25 21:09:26 +03:00
parent aa6c62d283
commit c49a5ed087
7 changed files with 47 additions and 18 deletions
@@ -1,7 +0,0 @@
package ru.shadowsparky.vbox.shared.domain
import kotlinx.coroutines.flow.StateFlow
interface ActivityInfo {
val isActiveFlow: StateFlow<Boolean>
}
@@ -0,0 +1,27 @@
package ru.shadowsparky.vbox.shared.data
import io.ktor.client.HttpClient
import org.koin.core.annotation.Factory
import ru.shadowsparky.domain.AppConfig
import ru.shadowsparky.updater.domain.UpdateFetcher
import ru.shadowsparky.updater.domain.UpdateInfo
import ru.shadowsparky.vbox.shared.domain.DYNAMIC_PREFIX
import ru.shadowsparky.vbox.shared.domain.ServerConfigurationRepository
import ru.shadowsparky.vbox.shared.domain.getServerConfiguration
@Factory
class VBoxUpdateFetcher(
private val httpClient: HttpClient,
private val serverConfigurationProvider: ServerConfigurationRepository,
private val appConfig: AppConfig
) : UpdateFetcher {
override suspend fun fetch(): UpdateInfo {
return httpClient.get<UpdateInfo>(
"$DYNAMIC_PREFIX/checkUpdates",
mapOf(
"versionCode" to appConfig.versionCode.toString()
),
serverConfigurationProvider.getServerConfiguration()
)
}
}
@@ -39,7 +39,7 @@ class SettingsComponent(
val showOffline: () -> Unit,
val showTags: () -> Unit
) : ComponentContext by context {
private val updateComponent = updateComponentFactory.create(childContext("UpdateComponent"))
val updateComponent = updateComponentFactory.create(childContext("UpdateComponent"))
val hasUpdateSupport = updateComponent.hasSupport
@@ -27,6 +27,7 @@ import ru.shadowsparky.ui.applyBlurSource
import ru.shadowsparky.ui.components.ExpressiveLazyColumn
import ru.shadowsparky.ui.components.SwitchPreference
import ru.shadowsparky.ui.components.TextPreference
import ru.shadowsparky.updater.presentation.UpdateBottomSheet
@Composable
fun SettingsContent(comp: SettingsComponent, paddingValues: PaddingValues) {
@@ -73,7 +74,7 @@ fun SettingsContent(comp: SettingsComponent, paddingValues: PaddingValues) {
modifier = Modifier.clickable { comp.showTags() }
)
}
if (comp.hasUpdateSupport) {
if (comp.updateComponent.hasSupport) {
item {
TextPreference(
text = stringResource(Res.string.check_for_updates),
@@ -115,6 +116,7 @@ fun SettingsContent(comp: SettingsComponent, paddingValues: PaddingValues) {
}
}
)
UpdateBottomSheet(comp.updateComponent)
LaunchedEffect(null) {
withFrameNanos {}
requester.requestFocus()
@@ -5,6 +5,7 @@ import com.russhwolf.settings.Settings
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import ru.shadowsparky.domain.DispatcherProvider
@@ -27,7 +28,7 @@ class SettingsKeyValueStorage(
}
override fun get(key: String): Flow<String?> {
return impl.get(key)
return impl.get(key).flowOn(dispatcherProvider.io)
}
override suspend fun remove(key: String) = withContext(dispatcherProvider.io) {
@@ -18,11 +18,11 @@ import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
@@ -38,6 +38,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.arkivanov.decompose.extensions.compose.subscribeAsState
import org.jetbrains.compose.resources.stringResource
import ru.shadowsparky.ui.components.Loading
import ru.shadowsparky.updater.domain.UpdateState
@OptIn(ExperimentalMaterial3Api::class)
@@ -54,11 +55,15 @@ fun UpdateBottomSheet(
val isVisible = remember(state) { state !is UpdateState.Idle && state !is UpdateState.UpToDate }
if (isVisible) {
ModalBottomSheet(onDismissRequest = {}, sheetState = sheetState, modifier = modifier) {
ModalBottomSheet(
onDismissRequest = { component.reset() },
sheetState = sheetState,
modifier = modifier
) {
AnimatedContent(
targetState = state,
label = "UpdateStateAnimation",
modifier = Modifier.fillMaxWidth().padding(start = 16.dp, end = 16.dp, bottom = 32.dp)
modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)
) { currentState ->
when (currentState) {
is UpdateState.Checking -> UpdateCheckingContent()
@@ -90,11 +95,8 @@ fun UpdateBottomSheet(
}
@Composable
private fun UpdateCheckingContent(modifier: Modifier = Modifier) {
Column(
modifier = modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) { CircularProgressIndicator() }
private fun UpdateCheckingContent() {
Loading(Modifier.fillMaxSize())
}
@Composable
@@ -65,6 +65,10 @@ class UpdateComponent(
fun installApk(path: String) {
scope.launch { interactor.installApk(path) }
}
fun reset() {
_state.value = UpdateState.Idle
}
}
@Factory