@ -34,57 +34,52 @@ import com.tailscale.ipn.ui.model.IpnLocal
@OptIn ( ExperimentalCoilApi :: class )
@OptIn ( ExperimentalCoilApi :: class )
@Composable
@Composable
fun Avatar ( profile : IpnLocal . LoginProfile ? , size : Int = 50 , action : ( ( ) -> Unit ) ? = null ) {
fun Avatar (
profile : IpnLocal . LoginProfile ? ,
size : Int = 50 ,
action : ( ( ) -> Unit ) ? = null ,
isFocusable : Boolean = false
) {
var isFocused = remember { mutableStateOf ( false ) }
var isFocused = remember { mutableStateOf ( false ) }
val focusManager = LocalFocusManager . current
val focusManager = LocalFocusManager . current
// Outer Box for the larger focusable and clickable area
// Determine the modifier based on whether the avatar is focusable
Box (
val outerModifier =
contentAlignment = Alignment . Center ,
Modifier . then (
modifier = Modifier
if ( isFocusable ) {
. padding ( 4. dp )
Modifier . padding ( 4. dp )
. size ( ( size * 1.5f ) . dp ) // Focusable area is larger than the avatar
} else Modifier ) // Add padding if focusable
. clip ( CircleShape ) // Ensure both the focus and click area are circular
. size ( ( size * 1.5f ) . dp )
. background (
. clip ( CircleShape )
if ( isFocused . value ) MaterialTheme . colorScheme . surface
. background ( if ( isFocused . value ) MaterialTheme . colorScheme . surface else Color . Transparent )
else Color . Transparent ,
. onFocusChanged { focusState -> isFocused . value = focusState . isFocused }
)
. then ( if ( isFocusable ) Modifier . focusable ( ) else Modifier ) // Conditionally add focusable
. onFocusChanged { focusState ->
isFocused . value = focusState . isFocused
}
. focusable ( ) // Make this outer Box focusable (after onFocusChanged)
. clickable (
. clickable (
interactionSource = remember { MutableInteractionSource ( ) } ,
interactionSource = remember { MutableInteractionSource ( ) } ,
indication = ripple ( bounded = true ) , // Apply ripple effect inside circular bounds
indication = ripple ( bounded = true ) ,
onClick = {
onClick = {
action ?. invoke ( )
action ?. invoke ( )
focusManager . clearFocus ( ) // Clear focus after clicking the avatar
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)
// Inner Box to hold the avatar content (Icon or AsyncImage)
Box (
Box ( contentAlignment = Alignment . Center , modifier = Modifier . size ( size . dp ) . clip ( CircleShape ) ) {
contentAlignment = Alignment . Center ,
modifier = Modifier
. size ( size . dp )
. clip ( CircleShape )
) {
if ( profile ?. UserProfile ?. ProfilePicURL != null ) {
if ( profile ?. UserProfile ?. ProfilePicURL != null ) {
AsyncImage (
AsyncImage (
model = profile . UserProfile . ProfilePicURL ,
model = profile . UserProfile . ProfilePicURL ,
modifier = Modifier . size ( size . dp ) . clip ( CircleShape ) ,
modifier = Modifier . size ( size . dp ) . clip ( CircleShape ) ,
contentDescription = null
contentDescription = null )
)
} else {
} else {
Icon (
Icon (
imageVector = Icons . Default . Person ,
imageVector = Icons . Default . Person ,
contentDescription = stringResource ( R . string . settings _title ) ,
contentDescription = stringResource ( R . string . settings _title ) ,
modifier = Modifier
modifier =
. size ( ( size * 0.8f ) . dp )
Modifier . size ( ( size * 0.8f ) . dp )
. clip ( CircleShape ) // Icon size slightly smaller than the Box
. clip ( CircleShape ) // Icon size slightly smaller than the Box
)
)
}
}
}
}
}
}
}
}