From a096ca15d1ec7086016e3d5dae9eb4f65cfb76a7 Mon Sep 17 00:00:00 2001 From: null Date: Wed, 1 Jul 2026 02:19:18 -0500 Subject: [PATCH] feat(settings): add Change password row to SecurityScreen (email/password accounts only) --- .../app/closer/ui/settings/SecurityScreen.kt | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/app/src/main/java/app/closer/ui/settings/SecurityScreen.kt b/app/src/main/java/app/closer/ui/settings/SecurityScreen.kt index 174f2cdd..cd153b7e 100644 --- a/app/src/main/java/app/closer/ui/settings/SecurityScreen.kt +++ b/app/src/main/java/app/closer/ui/settings/SecurityScreen.kt @@ -66,6 +66,8 @@ import app.closer.ui.components.CloserGlyphs data class SecurityUiState( val biometricLoginEnabled: Boolean = false, + // Only email/password accounts have a password to change (hidden for Google sign-in / not signed in). + val canChangePassword: Boolean = false, val hasRecoveryPhrase: Boolean = false, val recoveryPhrase: String? = null, // The couple key IS on this device (encrypt/decrypt works) even when no phrase is stored — this is the @@ -92,6 +94,11 @@ class SecurityViewModel @Inject constructor( // Whether the couple key itself is present on this device (set up), independent of the phrase. private val _coupleKeyPresent = MutableStateFlow(false) + // Account type is fixed for the life of this screen: only an email/password account (signed in, not + // Google, not anonymous) has a password to change. + private val canChangePassword = + authRepository.isSignedIn && !authRepository.isGoogleAccount && !authRepository.isAnonymous + init { viewModelScope.launch { val uid = authRepository.currentUserId ?: return@launch @@ -109,6 +116,7 @@ class SecurityViewModel @Inject constructor( ) { settings, phrase, stored, keyPresent -> SecurityUiState( biometricLoginEnabled = settings.biometricLoginEnabled, + canChangePassword = canChangePassword, hasRecoveryPhrase = stored != null, recoveryPhrase = phrase, coupleKeyPresent = keyPresent @@ -269,6 +277,28 @@ fun SecurityScreen( Divider(modifier = Modifier.padding(horizontal = 16.dp), thickness = 0.5.dp) + // Change password — only for email/password accounts (Google users have no password). + if (state.canChangePassword) { + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { onNavigate(AppRoute.CHANGE_PASSWORD) } + .padding(horizontal = 16.dp, vertical = 14.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + Icon(CloserGlyphs.Key, contentDescription = null, tint = SettingsMuted) + Text( + text = "Change password", + style = MaterialTheme.typography.bodyLarge, + color = SettingsInk, + modifier = Modifier.weight(1f) + ) + Icon(CloserGlyphs.Forward, contentDescription = null, tint = SettingsMuted) + } + Divider(modifier = Modifier.padding(horizontal = 16.dp), thickness = 0.5.dp) + } + // Recovery phrase row — greyed out until a phrase is stored on-device Row( modifier = Modifier