feat(home): partner photoUrl loaded and displayed in identity card

- PartnerHomeViewModel: loads partner photoUrl alongside name
- PartnerIdentityCard: shows AsyncImage when photoUrl available, fallback to initial letter
This commit is contained in:
null 2026-06-24 15:20:32 -05:00
parent a8fbbaa286
commit adb61715fe
1 changed files with 37 additions and 18 deletions

View File

@ -44,7 +44,10 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import coil.compose.AsyncImage
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
@ -77,6 +80,7 @@ data class PartnerHomeUiState(
val isLoading: Boolean = true,
val error: String? = null,
val partnerName: String? = null,
val partnerPhotoUrl: String? = null,
val streakCount: Int = 0,
val hasPartnerAnsweredToday: Boolean = false,
val coupleId: String? = null,
@ -119,11 +123,13 @@ class PartnerHomeViewModel @Inject constructor(
return@launch
}
val partnerId = couple.userIds.firstOrNull { it != uid }
val partnerName = partnerId?.let { pid ->
runCatching { userRepository.getUser(pid)?.displayName }
.onFailure { Log.w(TAG, "Could not load partner name", it) }
val partner = partnerId?.let { pid ->
runCatching { userRepository.getUser(pid) }
.onFailure { Log.w(TAG, "Could not load partner", it) }
.getOrNull()
}
val partnerName = partner?.displayName
val partnerPhotoUrl = partner?.photoUrl
val dailyAssignment = runCatching {
answerDataSource.getDailyQuestionAssignment(couple.id)
}.getOrNull()
@ -132,6 +138,7 @@ class PartnerHomeViewModel @Inject constructor(
it.copy(
isLoading = false,
partnerName = partnerName,
partnerPhotoUrl = partnerPhotoUrl,
streakCount = couple.streakCount,
coupleId = couple.id,
dailyQuestionId = dailyAssignment?.questionId,
@ -269,7 +276,7 @@ private fun PartnerHomeContent(
.padding(horizontal = 20.dp, vertical = 12.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
PartnerIdentityCard(name = state.partnerName, streakCount = state.streakCount)
PartnerIdentityCard(name = state.partnerName, photoUrl = state.partnerPhotoUrl, streakCount = state.streakCount)
PartnerActivityCard(
partnerName = state.partnerName,
@ -299,6 +306,7 @@ private fun PartnerHomeContent(
@Composable
private fun PartnerIdentityCard(
name: String?,
photoUrl: String?,
streakCount: Int,
modifier: Modifier = Modifier
) {
@ -313,20 +321,31 @@ private fun PartnerIdentityCard(
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Surface(
shape = CircleShape,
color = CloserPalette.PurpleDeep.copy(alpha = 0.14f),
modifier = Modifier.size(56.dp)
) {
Box(contentAlignment = Alignment.Center) {
Text(
text = (name?.firstOrNull()?.uppercaseChar() ?: '?').toString(),
style = MaterialTheme.typography.headlineMedium.copy(
fontWeight = FontWeight.SemiBold,
fontSize = 24.sp
),
color = CloserPalette.PurpleDeep
)
if (!photoUrl.isNullOrBlank()) {
AsyncImage(
model = photoUrl,
contentDescription = name,
contentScale = ContentScale.Crop,
modifier = Modifier
.size(56.dp)
.clip(CircleShape)
)
} else {
Surface(
shape = CircleShape,
color = CloserPalette.PurpleDeep.copy(alpha = 0.14f),
modifier = Modifier.size(56.dp)
) {
Box(contentAlignment = Alignment.Center) {
Text(
text = (name?.firstOrNull()?.uppercaseChar() ?: '?').toString(),
style = MaterialTheme.typography.headlineMedium.copy(
fontWeight = FontWeight.SemiBold,
fontSize = 24.sp
),
color = CloserPalette.PurpleDeep
)
}
}
}