fix(settings): partner card visual states — paired vs unpaired colors, border, avatar fallback

This commit is contained in:
null 2026-06-23 19:35:35 -05:00
parent 272c8997d0
commit e5c05abe90
2 changed files with 45 additions and 11 deletions

View File

@ -103,6 +103,9 @@ class SettingsDataStore @Inject constructor(
dataStore.edit { it[ONBOARDING_COMPLETE] = complete }.let {} dataStore.edit { it[ONBOARDING_COMPLETE] = complete }.let {}
override suspend fun setThemeMode(mode: ThemeMode) = override suspend fun setThemeMode(mode: ThemeMode) =
// Persist every explicit choice, including DEVICE/System. The system theme is the
// default only on first load (when no key is stored yet → fromStorageValue → DEVICE);
// once the user picks anything we save it rather than dropping back to "no preference".
dataStore.edit { it[THEME_MODE] = mode.name }.let {} dataStore.edit { it[THEME_MODE] = mode.name }.let {}
override suspend fun setBiometricLogin(enabled: Boolean) = override suspend fun setBiometricLogin(enabled: Boolean) =

View File

@ -2,8 +2,10 @@ package app.closer.ui.settings
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@ -363,6 +365,16 @@ fun SettingsScreen(
} }
// Partner card — fully tappable, visually obvious // Partner card — fully tappable, visually obvious
val partnerCardColor = if (state.isPaired) {
SettingsSoft.copy(alpha = 0.52f)
} else if (isCloserDarkTheme()) {
MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.74f)
} else {
MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.46f)
}
val partnerAccent = if (state.isPaired) SettingsPrimaryDeep else MaterialTheme.colorScheme.secondary
val partnerContentColor = if (state.isPaired) SettingsInk else MaterialTheme.colorScheme.onSecondaryContainer
val partnerMutedColor = if (state.isPaired) SettingsMuted else MaterialTheme.colorScheme.onSecondaryContainer.copy(alpha = 0.72f)
Card( Card(
onClick = { onClick = {
onNavigate( onNavigate(
@ -372,7 +384,8 @@ fun SettingsScreen(
}, },
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(22.dp), shape = RoundedCornerShape(22.dp),
colors = CardDefaults.cardColors(containerColor = SettingsSoft.copy(alpha = 0.52f)), colors = CardDefaults.cardColors(containerColor = partnerCardColor),
border = if (state.isPaired) null else BorderStroke(1.dp, partnerAccent.copy(alpha = 0.18f)),
elevation = CardDefaults.cardElevation(defaultElevation = 5.dp) elevation = CardDefaults.cardElevation(defaultElevation = 5.dp)
) { ) {
Row( Row(
@ -382,11 +395,28 @@ fun SettingsScreen(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp) horizontalArrangement = Arrangement.spacedBy(12.dp)
) { ) {
ProfileAvatar( if (state.isPaired) {
imageUrl = state.partnerPhotoUrl, ProfileAvatar(
fallbackIcon = if (state.isPaired) Icons.Filled.Favorite else Icons.Filled.FavoriteBorder, imageUrl = state.partnerPhotoUrl,
fallbackTint = if (state.isPaired) SettingsPrimaryDeep else SettingsMuted fallbackIcon = Icons.Filled.Favorite,
) fallbackTint = SettingsPrimaryDeep
)
} else {
Box(
modifier = Modifier
.size(48.dp)
.clip(RoundedCornerShape(16.dp))
.background(partnerAccent.copy(alpha = 0.13f)),
contentAlignment = Alignment.Center
) {
Icon(
Icons.Filled.FavoriteBorder,
contentDescription = null,
tint = partnerAccent,
modifier = Modifier.size(24.dp)
)
}
}
Column( Column(
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(2.dp) verticalArrangement = Arrangement.spacedBy(2.dp)
@ -394,7 +424,7 @@ fun SettingsScreen(
Text( Text(
text = if (state.isPaired) "Connected with" else "No partner yet", text = if (state.isPaired) "Connected with" else "No partner yet",
style = MaterialTheme.typography.labelMedium, style = MaterialTheme.typography.labelMedium,
color = SettingsMuted, color = partnerMutedColor,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
@ -403,15 +433,16 @@ fun SettingsScreen(
text = state.partnerName ?: "Your partner", text = state.partnerName ?: "Your partner",
style = MaterialTheme.typography.titleMedium, style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold, fontWeight = FontWeight.SemiBold,
color = SettingsInk, color = partnerContentColor,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
} else { } else {
Text( Text(
text = "Invite someone to connect", text = "Invite someone to connect",
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.titleSmall,
color = SettingsInk, fontWeight = FontWeight.SemiBold,
color = partnerContentColor,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
@ -421,7 +452,7 @@ fun SettingsScreen(
Icons.AutoMirrored.Filled.ArrowForwardIos, Icons.AutoMirrored.Filled.ArrowForwardIos,
contentDescription = null, contentDescription = null,
modifier = Modifier.size(16.dp), modifier = Modifier.size(16.dp),
tint = SettingsPrimaryDeep tint = partnerAccent
) )
} }
} }