From f756be0ab7b98795bc26f9377bd53d7cef06023e Mon Sep 17 00:00:00 2001 From: gabriel20xx Date: Mon, 27 Oct 2025 18:36:39 +0100 Subject: [PATCH] feat: add server password update functionality in Settings screen with validation and feedback --- .../app/ui/screens/SettingsScreen.kt | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/CollabTableAndroid/app/src/main/java/com/collabtable/app/ui/screens/SettingsScreen.kt b/CollabTableAndroid/app/src/main/java/com/collabtable/app/ui/screens/SettingsScreen.kt index aaab44b..2a45d51 100644 --- a/CollabTableAndroid/app/src/main/java/com/collabtable/app/ui/screens/SettingsScreen.kt +++ b/CollabTableAndroid/app/src/main/java/com/collabtable/app/ui/screens/SettingsScreen.kt @@ -49,6 +49,8 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.VisualTransformation import com.collabtable.app.R import com.collabtable.app.data.api.ApiClient import com.collabtable.app.data.database.CollabTableDatabase @@ -82,6 +84,9 @@ fun SettingsScreen( val syncIntervalMs by preferencesManager.syncPollIntervalMs.collectAsState() var syncIntervalInput by remember(syncIntervalMs) { mutableStateOf(syncIntervalMs.toString()) } var syncIntervalError by remember { mutableStateOf(null) } + var passwordInput by remember { mutableStateOf("") } + var passwordError by remember { mutableStateOf(null) } + var passwordUpdated by remember { mutableStateOf(false) } // Removed test connectivity logic; the connection indicator is shown via ConnectionStatusAction @@ -144,6 +149,82 @@ fun SettingsScreen( Spacer(modifier = Modifier.height(8.dp)) + // Authentication: allow updating server password without leaving + Text( + text = "Authentication", + style = MaterialTheme.typography.titleMedium, + ) + Card( + modifier = Modifier.fillMaxWidth(), + colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant), + ) { + Column( + modifier = Modifier.padding(12.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text( + text = "Server password", + style = MaterialTheme.typography.labelSmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + TextField( + value = passwordInput, + onValueChange = { + passwordInput = it + passwordError = null + passwordUpdated = false + }, + modifier = Modifier.weight(1f), + singleLine = true, + placeholder = { Text("Enter server password") }, + visualTransformation = PasswordVisualTransformation(), + ) + Button(onClick = { + val trimmed = passwordInput.trim() + if (trimmed.isEmpty()) { + passwordError = "Password cannot be empty" + } else { + preferencesManager.setServerPassword(trimmed) + // Trigger a quick sync in background to validate + coroutineScope.launch(Dispatchers.IO) { + try { + syncRepository.performSync() + } catch (_: Exception) { } + } + passwordUpdated = true + passwordError = null + } + }) { + Text("Update") + } + } + if (passwordError != null) { + Text( + text = passwordError!!, + color = MaterialTheme.colorScheme.error, + style = MaterialTheme.typography.labelSmall, + ) + } else if (passwordUpdated) { + Text( + text = "Password updated", + color = MaterialTheme.colorScheme.onSurfaceVariant, + style = MaterialTheme.typography.labelSmall, + ) + } else { + Text( + text = "Tip: This must match the server's SERVER_PASSWORD", + color = MaterialTheme.colorScheme.onSurfaceVariant, + style = MaterialTheme.typography.labelSmall, + ) + } + } + } + Text( text = "Appearance", style = MaterialTheme.typography.titleMedium,