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.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.SystemUpdate import androidx.compose.material.icons.filled.SystemUpdate
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button import androidx.compose.material3.Button
@ -31,7 +30,6 @@ import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
@ -290,33 +288,34 @@ internal fun SettingsScreen(
singleLine = true, singleLine = true,
modifier = Modifier.weight(1f) modifier = Modifier.weight(1f)
) )
val isNonDefaultUrl = uiState.serverUrl != de.bollwerk.app.domain.model.SettingsKey.DEFAULT_SERVER_URL
if (isNonDefaultUrl) {
Spacer(modifier = Modifier.width(8.dp)) Spacer(modifier = Modifier.width(8.dp))
IconButton(onClick = viewModel::resetServerUrl) { TextButton(onClick = viewModel::resetServerUrl) {
Icon( Text("Reset")
imageVector = Icons.Default.Refresh, }
contentDescription = "Standard-Server wiederherstellen"
)
} }
} }
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(12.dp))
if (uiState.isLoggedIn) { if (uiState.isLoggedIn) {
Row( ElevatedCard(modifier = Modifier.fillMaxWidth()) {
modifier = Modifier.fillMaxWidth(), Column(modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp)) {
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Text( Text(
text = "Angemeldet als: ${uiState.loggedInUsername}", text = "Angemeldet als: ${uiState.loggedInUsername}",
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.primary, color = MaterialTheme.colorScheme.primary
modifier = Modifier.weight(1f)
) )
TextButton(onClick = viewModel::logout) { Spacer(modifier = Modifier.height(8.dp))
OutlinedButton(
onClick = viewModel::logout,
modifier = Modifier.fillMaxWidth()
) {
Text("Abmelden") Text("Abmelden")
} }
} }
}
} else { } else {
OutlinedTextField( OutlinedTextField(
value = uiState.loginUsername, value = uiState.loginUsername,
@ -367,15 +366,7 @@ internal fun SettingsScreen(
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
val isSyncEnabled = uiState.isLoggedIn && SyncStatusCard(uiState = uiState)
uiState.serverUrl.isNotBlank() &&
!uiState.isSyncing
SyncStatusCard(
uiState = uiState,
isSyncEnabled = isSyncEnabled,
onSyncClick = viewModel::pullSync
)
if (de.bollwerk.app.BuildConfig.FEATURE_CAMERA_ENABLED) { if (de.bollwerk.app.BuildConfig.FEATURE_CAMERA_ENABLED) {
Spacer(modifier = Modifier.height(32.dp)) Spacer(modifier = Modifier.height(32.dp))
@ -549,18 +540,12 @@ internal fun SettingsScreen(
@Composable @Composable
private fun SyncStatusCard( private fun SyncStatusCard(
uiState: SettingsUiState, uiState: SettingsUiState
isSyncEnabled: Boolean,
onSyncClick: () -> Unit
) { ) {
ElevatedCard(modifier = Modifier.fillMaxWidth()) { ElevatedCard(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp)) { Column(modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp)) {
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(10.dp) horizontalArrangement = Arrangement.spacedBy(10.dp)
) { ) {
@ -589,24 +574,21 @@ private fun SyncStatusCard(
color = MaterialTheme.colorScheme.onSurfaceVariant color = MaterialTheme.colorScheme.onSurfaceVariant
) )
} }
if (uiState.lastSyncTime != null) {
Text(
text = "Letzte Sync: ${uiState.lastSyncTime}",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
} }
} }
if (uiState.isSyncing) { if (uiState.isSyncing) {
Spacer(modifier = Modifier.height(8.dp))
CircularProgressIndicator( CircularProgressIndicator(
modifier = Modifier.size(24.dp), modifier = Modifier.size(24.dp),
strokeWidth = 2.dp strokeWidth = 2.dp
) )
} else {
IconButton(
onClick = onSyncClick,
enabled = isSyncEnabled
) {
Icon(
imageVector = Icons.Default.Refresh,
contentDescription = "Jetzt synchronisieren"
)
}
}
} }
AnimatedVisibility( AnimatedVisibility(
visible = uiState.syncActivity != null, visible = uiState.syncActivity != null,
@ -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
)
}
} }
} }
} }