add health check button to settings screen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,15 +2,20 @@ package me.hgsky.synq.ui.settings
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
@@ -21,7 +26,9 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavController
|
||||
@@ -31,6 +38,7 @@ import me.hgsky.synq.data.SynqSettings
|
||||
@Composable
|
||||
fun SettingsScreen(nav: NavController, vm: SettingsViewModel = viewModel()) {
|
||||
val saved by vm.settings.collectAsState()
|
||||
val ping by vm.ping.collectAsState()
|
||||
|
||||
var url by remember(saved.serverUrl) { mutableStateOf(saved.serverUrl) }
|
||||
var token by remember(saved.token) { mutableStateOf(saved.token) }
|
||||
@@ -81,10 +89,46 @@ fun SettingsScreen(nav: NavController, vm: SettingsViewModel = viewModel()) {
|
||||
label = { Text("device label") },
|
||||
singleLine = true,
|
||||
)
|
||||
Button(
|
||||
onClick = ::saveAndBack,
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) { Text("save") }
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
OutlinedButton(
|
||||
onClick = {
|
||||
vm.save(SynqSettings(url.trim(), token.trim(), device.trim().ifEmpty { "android" }))
|
||||
vm.checkConnection(url.trim())
|
||||
},
|
||||
modifier = Modifier.weight(1f),
|
||||
enabled = ping !is PingState.Checking,
|
||||
) {
|
||||
if (ping is PingState.Checking) {
|
||||
CircularProgressIndicator(modifier = Modifier.size(16.dp), strokeWidth = 2.dp)
|
||||
} else {
|
||||
Text("check connection")
|
||||
}
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = ::saveAndBack,
|
||||
modifier = Modifier.weight(1f),
|
||||
) { Text("save") }
|
||||
}
|
||||
|
||||
when (val p = ping) {
|
||||
is PingState.Ok -> Text(
|
||||
"✓ reachable",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = Color(0xFF2E7D32),
|
||||
)
|
||||
is PingState.Failed -> Text(
|
||||
"✗ unreachable — check URL and that server is running",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,23 @@ package me.hgsky.synq.ui.settings
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import me.hgsky.synq.SynqApp
|
||||
import me.hgsky.synq.data.SynqSettings
|
||||
import me.hgsky.synq.data.api.SynqApiClient
|
||||
|
||||
sealed class PingState {
|
||||
object Idle : PingState()
|
||||
object Checking : PingState()
|
||||
data class Ok(val url: String) : PingState()
|
||||
data class Failed(val url: String) : PingState()
|
||||
}
|
||||
|
||||
class SettingsViewModel(app: Application) : AndroidViewModel(app) {
|
||||
|
||||
@@ -19,7 +31,19 @@ class SettingsViewModel(app: Application) : AndroidViewModel(app) {
|
||||
SynqSettings(),
|
||||
)
|
||||
|
||||
private val _ping = MutableStateFlow<PingState>(PingState.Idle)
|
||||
val ping: StateFlow<PingState> = _ping.asStateFlow()
|
||||
|
||||
fun save(settings: SynqSettings) {
|
||||
viewModelScope.launch { repo.save(settings) }
|
||||
}
|
||||
|
||||
fun checkConnection(url: String) {
|
||||
if (_ping.value is PingState.Checking) return
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
_ping.value = PingState.Checking
|
||||
val ok = SynqApiClient().checkHealth(url.trim())
|
||||
_ping.value = if (ok) PingState.Ok(url) else PingState.Failed(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user