From 3f22a9e1122c7155b2c12ca99f2c7b5c7874c548 Mon Sep 17 00:00:00 2001 From: Jonathan Nobels Date: Fri, 5 Sep 2025 14:20:14 -0400 Subject: [PATCH] android: hide placeholder when avatar is loaded fixes tailscale/corp#32012 We need to hide the placeholder once the user's avatar is loaded. Signed-off-by: Jonathan Nobels --- .../java/com/tailscale/ipn/ui/view/Avatar.kt | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/Avatar.kt b/android/src/main/java/com/tailscale/ipn/ui/view/Avatar.kt index 54adf27..e4adf8d 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/Avatar.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/Avatar.kt @@ -29,6 +29,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import coil.annotation.ExperimentalCoilApi import coil.compose.AsyncImage +import coil.compose.AsyncImagePainter import com.tailscale.ipn.R import com.tailscale.ipn.ui.model.IpnLocal import com.tailscale.ipn.ui.util.AndroidTVUtil @@ -44,6 +45,7 @@ fun Avatar( ) { val isFocused = remember { mutableStateOf(false) } val focusManager = LocalFocusManager.current + val isIconLoaded = remember { mutableStateOf(false) } // Outer Box for the larger focusable and clickable area Box( @@ -73,20 +75,28 @@ fun Avatar( 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.conditional(AndroidTVUtil.isAndroidTV(), { size((size * 0.8f).dp) }) - .clip(CircleShape) // Icon size slightly smaller than the Box - ) + if (!isIconLoaded.value) { + Icon( + imageVector = Icons.Default.Person, + contentDescription = stringResource(R.string.settings_title), + modifier = + Modifier.conditional( + AndroidTVUtil.isAndroidTV(), { 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) + contentDescription = null, + onState = { state -> + if (state is AsyncImagePainter.State.Success) { + isIconLoaded.value = true + } + }) } } }