feat(settings): server-sync UI aufräumen (#108)

- Login-Status + Logout auf eigene ElevatedCard
- Logout-Button als OutlinedButton (Material-Button statt TextLink)
- Letzte Sync direkt unter Verbindungsstatus ohne Divider
- Refresh-IconButton neben Serverstatus entfernt
- Server-URL Reset nur sichtbar wenn nicht-default Adresse
- Manuelle Sync-Buttons entfernt (vollautomatisch)
This commit is contained in:
Jens Reinemann 2026-05-18 10:09:58 +02:00
parent bdd8cb4b11
commit 887cdbd3f7

View file

@ -21,7 +21,6 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.SystemUpdate
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
@ -31,7 +30,6 @@ import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField
@ -290,31 +288,32 @@ internal fun SettingsScreen(
singleLine = true,
modifier = Modifier.weight(1f)
)
Spacer(modifier = Modifier.width(8.dp))
IconButton(onClick = viewModel::resetServerUrl) {
Icon(
imageVector = Icons.Default.Refresh,
contentDescription = "Standard-Server wiederherstellen"
)
val isNonDefaultUrl = uiState.serverUrl != de.bollwerk.app.domain.model.SettingsKey.DEFAULT_SERVER_URL
if (isNonDefaultUrl) {
Spacer(modifier = Modifier.width(8.dp))
TextButton(onClick = viewModel::resetServerUrl) {
Text("Reset")
}
}
}
Spacer(modifier = Modifier.height(12.dp))
if (uiState.isLoggedIn) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = "Angemeldet als: ${uiState.loggedInUsername}",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.weight(1f)
)
TextButton(onClick = viewModel::logout) {
Text("Abmelden")
ElevatedCard(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp)) {
Text(
text = "Angemeldet als: ${uiState.loggedInUsername}",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.primary
)
Spacer(modifier = Modifier.height(8.dp))
OutlinedButton(
onClick = viewModel::logout,
modifier = Modifier.fillMaxWidth()
) {
Text("Abmelden")
}
}
}
} else {
@ -367,15 +366,7 @@ internal fun SettingsScreen(
Spacer(modifier = Modifier.height(16.dp))
val isSyncEnabled = uiState.isLoggedIn &&
uiState.serverUrl.isNotBlank() &&
!uiState.isSyncing
SyncStatusCard(
uiState = uiState,
isSyncEnabled = isSyncEnabled,
onSyncClick = viewModel::pullSync
)
SyncStatusCard(uiState = uiState)
if (de.bollwerk.app.BuildConfig.FEATURE_CAMERA_ENABLED) {
Spacer(modifier = Modifier.height(32.dp))
@ -549,65 +540,56 @@ internal fun SettingsScreen(
@Composable
private fun SyncStatusCard(
uiState: SettingsUiState,
isSyncEnabled: Boolean,
onSyncClick: () -> Unit
uiState: SettingsUiState
) {
ElevatedCard(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp)) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
horizontalArrangement = Arrangement.spacedBy(10.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(10.dp)
) {
val (dotColor, statusText) = when (uiState.connectionState) {
is ConnectionState.Connected -> Color(0xFF4CAF50) to "Verbunden"
is ConnectionState.Connecting -> Color(0xFFFFC107) to "Verbinde…"
is ConnectionState.Disconnected -> Color(0xFFF44336) to "Keine Verbindung"
is ConnectionState.NotConfigured -> Color.Gray to "Nicht angemeldet"
}
val disconnectedSeconds =
(uiState.connectionState as? ConnectionState.Disconnected)?.reconnectInSeconds
val (dotColor, statusText) = when (uiState.connectionState) {
is ConnectionState.Connected -> Color(0xFF4CAF50) to "Verbunden"
is ConnectionState.Connecting -> Color(0xFFFFC107) to "Verbinde…"
is ConnectionState.Disconnected -> Color(0xFFF44336) to "Keine Verbindung"
is ConnectionState.NotConfigured -> Color.Gray to "Nicht angemeldet"
}
val disconnectedSeconds =
(uiState.connectionState as? ConnectionState.Disconnected)?.reconnectInSeconds
Text(
text = "",
color = dotColor,
style = MaterialTheme.typography.bodyLarge
)
Column {
Text(
text = "",
color = dotColor,
style = MaterialTheme.typography.bodyLarge
text = statusText,
style = MaterialTheme.typography.bodyMedium
)
Column {
if (disconnectedSeconds != null) {
Text(
text = statusText,
style = MaterialTheme.typography.bodyMedium
text = "Nächster Versuch in $disconnectedSeconds Sek.",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
if (disconnectedSeconds != null) {
Text(
text = "Nächster Versuch in $disconnectedSeconds Sek.",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
}
if (uiState.isSyncing) {
CircularProgressIndicator(
modifier = Modifier.size(24.dp),
strokeWidth = 2.dp
)
} else {
IconButton(
onClick = onSyncClick,
enabled = isSyncEnabled
) {
Icon(
imageVector = Icons.Default.Refresh,
contentDescription = "Jetzt synchronisieren"
if (uiState.lastSyncTime != null) {
Text(
text = "Letzte Sync: ${uiState.lastSyncTime}",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
}
if (uiState.isSyncing) {
Spacer(modifier = Modifier.height(8.dp))
CircularProgressIndicator(
modifier = Modifier.size(24.dp),
strokeWidth = 2.dp
)
}
AnimatedVisibility(
visible = uiState.syncActivity != null,
enter = fadeIn(),
@ -623,14 +605,6 @@ private fun SyncStatusCard(
)
}
}
if (uiState.lastSyncTime != null) {
HorizontalDivider(modifier = Modifier.padding(top = 12.dp, bottom = 8.dp))
Text(
text = "Letzte Synchronisierung: ${uiState.lastSyncTime}",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
}
}