android: fix issue where default avatar wasn't shown (#558)

Always render the default icon first so that if the profile picture is not loaded or has an issue, the default is shown.

Fixes tailscale/corp#24217

Signed-off-by: kari-ts <kari@tailscale.com>
kari/test
kari-ts 1 year ago committed by GitHub
parent 18ca09d0f3
commit 4c4148bd8e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -43,43 +43,53 @@ fun Avatar(
var isFocused = remember { mutableStateOf(false) }
val focusManager = LocalFocusManager.current
// Determine the modifier based on whether the avatar is focusable
val outerModifier =
Modifier.then(
if (isFocusable) {
Modifier.padding(4.dp)
} else Modifier) // Add padding if focusable
.size((size * 1.5f).dp)
.clip(CircleShape)
.background(if (isFocused.value) MaterialTheme.colorScheme.surface else Color.Transparent)
.onFocusChanged { focusState -> isFocused.value = focusState.isFocused }
.then(if (isFocusable) Modifier.focusable() else Modifier) // Conditionally add focusable
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = ripple(bounded = true),
onClick = {
action?.invoke()
focusManager.clearFocus() // Clear focus after clicking
})
// Outer Box for the larger focusable and clickable area
Box(contentAlignment = Alignment.Center, modifier = outerModifier) {
// Inner Box to hold the avatar content (Icon or AsyncImage)
Box(contentAlignment = Alignment.Center, modifier = Modifier.size(size.dp).clip(CircleShape)) {
if (profile?.UserProfile?.ProfilePicURL != null) {
AsyncImage(
model = profile.UserProfile.ProfilePicURL,
modifier = Modifier.size(size.dp).clip(CircleShape),
contentDescription = null)
} else {
Icon(
imageVector = Icons.Default.Person,
contentDescription = stringResource(R.string.settings_title),
modifier =
Modifier.size((size * 0.8f).dp)
.clip(CircleShape) // Icon size slightly smaller than the Box
// Outer Box for the larger focusable and clickable area
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.padding(4.dp)
.size((size * 1.5f).dp) // Focusable area is larger than the avatar
.clip(CircleShape) // Ensure both the focus and click area are circular
.background(
if (isFocused.value) MaterialTheme.colorScheme.surface
else Color.Transparent,
)
.onFocusChanged { focusState ->
isFocused.value = focusState.isFocused
}
.focusable() // Make this outer Box focusable (after onFocusChanged)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = ripple(bounded = true), // Apply ripple effect inside circular bounds
onClick = {
action?.invoke()
focusManager.clearFocus() // Clear focus after clicking the avatar
}
)
}
}
) {
// Inner Box to hold the avatar content (Icon or AsyncImage)
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.size(size.dp)
.clip(CircleShape)
) {
// Always display the default icon as a background layer
Icon(
imageVector = Icons.Default.Person,
contentDescription = stringResource(R.string.settings_title),
modifier =
Modifier.size((size * 0.8f).dp)
.clip(CircleShape) // Icon size slightly smaller than the Box
)
// Overlay the profile picture if available
profile?.UserProfile?.ProfilePicURL?.let { url ->
AsyncImage(
model = url,
modifier = Modifier.size(size.dp).clip(CircleShape),
contentDescription = null)
}
}
}
}

Loading…
Cancel
Save