Merge pull request #4923 from element-hq/feature/bma/spaceAvatar2

Iterate on avatar to be able to render Space avatar Part2
This commit is contained in:
Benoit Marty 2025-06-24 12:05:59 +02:00 committed by GitHub
commit 5a5195351d
56 changed files with 386 additions and 193 deletions

View file

@ -39,6 +39,7 @@ import io.element.android.libraries.designsystem.background.OnboardingBackground
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Icon
@ -74,7 +75,8 @@ internal fun IncomingCallScreen(
name = notificationData.senderName, name = notificationData.senderName,
url = notificationData.avatarUrl, url = notificationData.avatarUrl,
size = AvatarSize.IncomingCall, size = AvatarSize.IncomingCall,
) ),
avatarType = AvatarType.User,
) )
Spacer(modifier = Modifier.height(24.dp)) Spacer(modifier = Modifier.height(24.dp))
Text( Text(

View file

@ -40,6 +40,7 @@ import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtom
import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtomSize import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtomSize
import io.element.android.libraries.designsystem.components.async.AsyncActionView import io.element.android.libraries.designsystem.components.async.AsyncActionView
import io.element.android.libraries.designsystem.components.async.AsyncActionViewDefaults import io.element.android.libraries.designsystem.components.async.AsyncActionViewDefaults
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.components.list.ListItemContent
@ -220,6 +221,7 @@ private fun RoomNameWithAvatar(
) { ) {
UnsavedAvatar( UnsavedAvatar(
avatarUri = avatarUri, avatarUri = avatarUri,
avatarSize = AvatarSize.EditRoomDetails,
avatarType = AvatarType.Room(), avatarType = AvatarType.Room(),
modifier = Modifier.clickable(onClick = onAvatarClick), modifier = Modifier.clickable(onClick = onAvatarClick),
) )

View file

@ -38,6 +38,7 @@ import io.element.android.libraries.designsystem.components.PageTitle
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Button
@ -149,6 +150,7 @@ private fun NotificationRow(
) { ) {
Avatar( Avatar(
avatarData = AvatarData(id = avatarColorsId, name = avatarLetter, size = AvatarSize.NotificationsOptIn), avatarData = AvatarData(id = avatarColorsId, name = avatarLetter, size = AvatarSize.NotificationsOptIn),
avatarType = AvatarType.User,
) )
Column(Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(12.dp)) { Column(Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(12.dp)) {
Box( Box(

View file

@ -26,6 +26,7 @@ import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.text.toPx import io.element.android.libraries.designsystem.text.toPx
@ -36,6 +37,7 @@ import kotlinx.collections.immutable.toImmutableList
* Draw a row of avatars (they must all have the same size), from start to end. * Draw a row of avatars (they must all have the same size), from start to end.
* @param avatarDataList the avatars to render. Note: they will all be rendered, the caller may * @param avatarDataList the avatars to render. Note: they will all be rendered, the caller may
* want to limit the list size * want to limit the list size
* @param avatarType the type of avatars to render
* @param modifier Jetpack Compose modifier * @param modifier Jetpack Compose modifier
* @param overlapRatio the overlap ration. When 0f, avatars will render without overlap, when 1f * @param overlapRatio the overlap ration. When 0f, avatars will render without overlap, when 1f
* only the first avatar will be visible * only the first avatar will be visible
@ -43,6 +45,7 @@ import kotlinx.collections.immutable.toImmutableList
@Composable @Composable
fun AvatarRow( fun AvatarRow(
avatarDataList: ImmutableList<AvatarData>, avatarDataList: ImmutableList<AvatarData>,
avatarType: AvatarType,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
overlapRatio: Float = 0.5f, overlapRatio: Float = 0.5f,
) { ) {
@ -58,35 +61,36 @@ fun AvatarRow(
.forEachIndexed { index, avatarData -> .forEachIndexed { index, avatarData ->
Avatar( Avatar(
modifier = Modifier modifier = Modifier
.padding(start = avatarSize * (1 - overlapRatio) * (lastItemIndex - index)) .padding(start = avatarSize * (1 - overlapRatio) * (lastItemIndex - index))
.graphicsLayer { .graphicsLayer {
compositingStrategy = CompositingStrategy.Offscreen compositingStrategy = CompositingStrategy.Offscreen
}
.drawWithContent {
// Draw content and clear the pixels for the avatar on the left (right in RTL).
drawContent()
val xOffset = if (isRtl) {
size.width - avatarSizePx * (overlapRatio - 0.5f)
} else {
0f + avatarSizePx * (overlapRatio - 0.5f)
} }
.drawWithContent { if (index < lastItemIndex) {
// Draw content and clear the pixels for the avatar on the left (right in RTL). drawCircle(
drawContent() color = Color.Black,
val xOffset = if (isRtl) { center = Offset(
size.width - avatarSizePx * (overlapRatio - 0.5f) x = xOffset,
} else { y = size.height / 2,
0f + avatarSizePx * (overlapRatio - 0.5f) ),
} radius = avatarSizePx / 2,
if (index < lastItemIndex) { blendMode = BlendMode.Clear,
drawCircle( )
color = Color.Black,
center = Offset(
x = xOffset,
y = size.height / 2,
),
radius = avatarSizePx / 2,
blendMode = BlendMode.Clear,
)
}
} }
.size(size = avatarSize) }
// Keep internal padding, it has the advantage to not reduce the size of the Avatar image, .size(size = avatarSize)
// which is already small in our use case. // Keep internal padding, it has the advantage to not reduce the size of the Avatar image,
.padding(2.dp), // which is already small in our use case.
.padding(2.dp),
avatarData = avatarData, avatarData = avatarData,
avatarType = avatarType,
) )
} }
} }
@ -122,6 +126,7 @@ private fun ContentToPreview(overlapRatio: Float) {
size = AvatarSize.RoomListItem, size = AvatarSize.RoomListItem,
) )
}.toImmutableList(), }.toImmutableList(),
avatarType = AvatarType.User,
overlapRatio = overlapRatio, overlapRatio = overlapRatio,
) )
} }

View file

@ -39,6 +39,7 @@ import io.element.android.libraries.designsystem.components.async.AsyncIndicator
import io.element.android.libraries.designsystem.components.async.rememberAsyncIndicatorState import io.element.android.libraries.designsystem.components.async.rememberAsyncIndicatorState
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Button
@ -195,7 +196,10 @@ private fun KnockRequestAvatarView(
Box(modifier) { Box(modifier) {
when (knockRequests.size) { when (knockRequests.size) {
0 -> Unit 0 -> Unit
1 -> Avatar(knockRequests.first().getAvatarData(AvatarSize.KnockRequestBanner)) 1 -> Avatar(
avatarData = knockRequests.first().getAvatarData(AvatarSize.KnockRequestBanner),
avatarType = AvatarType.User,
)
else -> KnockRequestAvatarListView(knockRequests) else -> KnockRequestAvatarListView(knockRequests)
} }
} }
@ -214,6 +218,7 @@ private fun KnockRequestAvatarListView(
.toImmutableList() .toImmutableList()
AvatarRow( AvatarRow(
avatarDataList = avatars, avatarDataList = avatars,
avatarType = AvatarType.User,
modifier = modifier, modifier = modifier,
) )
} }

View file

@ -56,6 +56,7 @@ import io.element.android.libraries.designsystem.components.ProgressDialog
import io.element.android.libraries.designsystem.components.async.AsyncActionView import io.element.android.libraries.designsystem.components.async.AsyncActionView
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
@ -318,7 +319,10 @@ private fun KnockRequestItem(
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 12.dp) .padding(horizontal = 16.dp, vertical = 12.dp)
) { ) {
Avatar(knockRequest.getAvatarData(AvatarSize.KnockRequestItem)) Avatar(
avatarData = knockRequest.getAvatarData(AvatarSize.KnockRequestItem),
avatarType = AvatarType.User,
)
Spacer(modifier = Modifier.width(16.dp)) Spacer(modifier = Modifier.width(16.dp))
Column { Column {
// Name and date // Name and date

View file

@ -74,6 +74,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.utils.messagesummary.DefaultMessageSummaryFormatter import io.element.android.features.messages.impl.utils.messagesummary.DefaultMessageSummaryFormatter
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.components.list.ListItemContent
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
@ -256,7 +257,12 @@ private fun MessageSummary(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val content: @Composable () -> Unit val content: @Composable () -> Unit
val icon: @Composable () -> Unit = { Avatar(avatarData = event.senderAvatar.copy(size = AvatarSize.MessageActionSender)) } val icon: @Composable () -> Unit = {
Avatar(
avatarData = event.senderAvatar.copy(size = AvatarSize.MessageActionSender),
avatarType = AvatarType.User,
)
}
val contentStyle = ElementTheme.typography.fontBodyMdRegular.copy(color = ElementTheme.colors.textSecondary) val contentStyle = ElementTheme.typography.fontBodyMdRegular.copy(color = ElementTheme.colors.textSecondary)
@Composable @Composable

View file

@ -33,6 +33,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.roomcall.api.RoomCallState import io.element.android.features.roomcall.api.RoomCallState
import io.element.android.features.roomcall.api.RoomCallStateProvider import io.element.android.features.roomcall.api.RoomCallStateProvider
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.text.toDp import io.element.android.libraries.designsystem.text.toDp
@ -55,7 +56,10 @@ internal fun TimelineItemCallNotifyView(
horizontalArrangement = Arrangement.spacedBy(12.dp), horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
Avatar(avatarData = event.senderAvatar) Avatar(
avatarData = event.senderAvatar,
avatarType = AvatarType.User,
)
Column(modifier = Modifier.weight(1f)) { Column(modifier = Modifier.weight(1f)) {
Text( Text(
text = event.safeSenderName, text = event.safeSenderName,

View file

@ -76,6 +76,7 @@ import io.element.android.libraries.designsystem.colors.AvatarColorsProvider
import io.element.android.libraries.designsystem.components.EqualWidthColumn import io.element.android.libraries.designsystem.components.EqualWidthColumn
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.swipe.SwipeableActionsState import io.element.android.libraries.designsystem.swipe.SwipeableActionsState
@ -441,6 +442,7 @@ private fun MessageSenderInformation(
.clip(CircleShape) .clip(CircleShape)
.clickable(onClick = onClick), .clickable(onClick = onClick),
avatarData = senderAvatar, avatarData = senderAvatar,
avatarType = AvatarType.User,
) )
SenderName( SenderName(
modifier = Modifier modifier = Modifier

View file

@ -62,6 +62,7 @@ import io.element.android.features.messages.impl.timeline.model.AggregatedReacti
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.text.toDp import io.element.android.libraries.designsystem.text.toDp
@ -249,7 +250,10 @@ private fun SenderRow(
.semantics(mergeDescendants = true) {}, .semantics(mergeDescendants = true) {},
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Avatar(avatarData) Avatar(
avatarData = avatarData,
avatarType = AvatarType.User,
)
Column( Column(
modifier = Modifier.padding(start = 12.dp), modifier = Modifier.padding(start = 12.dp),
) { ) {

View file

@ -37,6 +37,7 @@ import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.features.messages.impl.timeline.model.ReadReceiptData import io.element.android.features.messages.impl.timeline.model.ReadReceiptData
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.components.avatar.getBestName import io.element.android.libraries.designsystem.components.avatar.getBestName
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
@ -65,11 +66,11 @@ fun TimelineItemReadReceiptView(
ReadReceiptsAvatars( ReadReceiptsAvatars(
receipts = state.receipts, receipts = state.receipts,
modifier = Modifier modifier = Modifier
.clip(RoundedCornerShape(4.dp)) .clip(RoundedCornerShape(4.dp))
.clickable { .clickable {
onReadReceiptsClick() onReadReceiptsClick()
} }
.padding(2.dp) .padding(2.dp)
) )
} }
} }
@ -112,9 +113,9 @@ private fun ReadReceiptsRow(
) { ) {
Row( Row(
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.height(AvatarSize.TimelineReadReceipt.dp + 8.dp) .height(AvatarSize.TimelineReadReceipt.dp + 8.dp)
.padding(horizontal = 18.dp), .padding(horizontal = 18.dp),
horizontalArrangement = Arrangement.End, horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
@ -154,15 +155,16 @@ private fun ReadReceiptsAvatars(
.forEachIndexed { index, readReceiptData -> .forEachIndexed { index, readReceiptData ->
Box( Box(
modifier = Modifier modifier = Modifier
.padding(end = (12.dp + avatarStrokeSize * 2) * index) .padding(end = (12.dp + avatarStrokeSize * 2) * index)
.size(size = avatarSize + avatarStrokeSize * 2) .size(size = avatarSize + avatarStrokeSize * 2)
.clip(CircleShape) .clip(CircleShape)
.background(avatarStrokeColor) .background(avatarStrokeColor)
.zIndex(index.toFloat()), .zIndex(index.toFloat()),
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
) { ) {
Avatar( Avatar(
avatarData = readReceiptData.avatarData, avatarData = readReceiptData.avatarData,
avatarType = AvatarType.User,
) )
} }
} }

View file

@ -7,7 +7,9 @@
package io.element.android.features.preferences.impl.user.editprofile package io.element.android.features.preferences.impl.user.editprofile
import android.net.Uri
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.core.net.toUri
import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.permissions.api.aPermissionsState import io.element.android.libraries.permissions.api.aPermissionsState
@ -17,14 +19,17 @@ open class EditUserProfileStateProvider : PreviewParameterProvider<EditUserProfi
override val values: Sequence<EditUserProfileState> override val values: Sequence<EditUserProfileState>
get() = sequenceOf( get() = sequenceOf(
aEditUserProfileState(), aEditUserProfileState(),
aEditUserProfileState(userAvatarUrl = "example://uri".toUri()),
// Add other states here // Add other states here
) )
} }
fun aEditUserProfileState() = EditUserProfileState( fun aEditUserProfileState(
userAvatarUrl: Uri? = null,
) = EditUserProfileState(
userId = UserId("@john.doe:matrix.org"), userId = UserId("@john.doe:matrix.org"),
displayName = "John Doe", displayName = "John Doe",
userAvatarUrl = null, userAvatarUrl = userAvatarUrl,
avatarActions = persistentListOf(), avatarActions = persistentListOf(),
saveAction = AsyncAction.Uninitialized, saveAction = AsyncAction.Uninitialized,
saveButtonEnabled = true, saveButtonEnabled = true,

View file

@ -50,6 +50,7 @@ import io.element.android.libraries.designsystem.components.async.rememberAsyncI
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
@ -341,7 +342,10 @@ private fun MemberRow(
.padding(start = 16.dp, top = 4.dp, end = 16.dp, bottom = 4.dp), .padding(start = 16.dp, top = 4.dp, end = 16.dp, bottom = 4.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Avatar(avatarData) Avatar(
avatarData = avatarData,
avatarType = AvatarType.User,
)
Column( Column(
modifier = Modifier modifier = Modifier
.padding(start = 12.dp) .padding(start = 12.dp)

View file

@ -48,6 +48,7 @@ import io.element.android.libraries.designsystem.atomic.atoms.RedIndicatorAtom
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.components.avatarBloom import io.element.android.libraries.designsystem.components.avatarBloom
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
@ -297,6 +298,7 @@ private fun NavigationIcon(
Box { Box {
Avatar( Avatar(
avatarData = avatarData, avatarData = avatarData,
avatarType = AvatarType.User,
contentDescription = stringResource(CommonStrings.common_settings), contentDescription = stringResource(CommonStrings.common_settings),
) )
if (showAvatarIndicator) { if (showAvatarIndicator) {

View file

@ -38,6 +38,7 @@ import io.element.android.libraries.designsystem.components.async.AsyncIndicator
import io.element.android.libraries.designsystem.components.async.rememberAsyncIndicatorState import io.element.android.libraries.designsystem.components.async.rememberAsyncIndicatorState
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.designsystem.components.dialogs.TextFieldDialog import io.element.android.libraries.designsystem.components.dialogs.TextFieldDialog
import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.components.list.ListItemContent
@ -223,6 +224,7 @@ private fun RoomMemberActionsBottomSheet(
) { ) {
Avatar( Avatar(
avatarData = user.getAvatarData(size = AvatarSize.RoomListManageUser), avatarData = user.getAvatarData(size = AvatarSize.RoomListManageUser),
avatarType = AvatarType.User,
modifier = Modifier modifier = Modifier
.padding(bottom = 28.dp) .padding(bottom = 28.dp)
.align(Alignment.CenterHorizontally) .align(Alignment.CenterHorizontally)

View file

@ -30,6 +30,7 @@ import io.element.android.libraries.designsystem.atomic.molecules.MatrixBadgeRow
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.modifiers.niceClickable import io.element.android.libraries.designsystem.modifiers.niceClickable
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
@ -61,6 +62,7 @@ fun UserProfileHeaderSection(
) { ) {
Avatar( Avatar(
avatarData = AvatarData(userId.value, userName, avatarUrl, AvatarSize.UserHeader), avatarData = AvatarData(userId.value, userName, avatarUrl, AvatarSize.UserHeader),
avatarType = AvatarType.User,
modifier = Modifier modifier = Modifier
.clip(CircleShape) .clip(CircleShape)
.clickable(enabled = avatarUrl != null) { openAvatarPreview(avatarUrl!!) } .clickable(enabled = avatarUrl != null) { openAvatarPreview(avatarUrl!!) }

View file

@ -25,6 +25,7 @@ import io.element.android.compound.theme.ElementTheme
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
@ -50,10 +51,11 @@ fun VerificationUserProfileContent(
.padding(12.dp), .padding(12.dp),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
Avatar(avatarData) Avatar(
avatarData = avatarData,
avatarType = AvatarType.User,
)
Spacer(modifier = Modifier.padding(12.dp)) Spacer(modifier = Modifier.padding(12.dp))
Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
Text(text = displayName ?: userId.value, style = ElementTheme.typography.fontBodyLgMedium, color = ElementTheme.colors.textPrimary) Text(text = displayName ?: userId.value, style = ElementTheme.typography.fontBodyLgMedium, color = ElementTheme.colors.textPrimary)

View file

@ -27,6 +27,7 @@ import io.element.android.compound.theme.ElementTheme
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.components.avatar.anAvatarData import io.element.android.libraries.designsystem.components.avatar.anAvatarData
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
@ -72,7 +73,10 @@ fun ComposerAlertMolecule(
horizontalArrangement = Arrangement.spacedBy(16.dp) horizontalArrangement = Arrangement.spacedBy(16.dp)
) { ) {
if (avatar != null) { if (avatar != null) {
Avatar(avatarData = avatar) Avatar(
avatarData = avatar,
avatarType = AvatarType.User,
)
} }
Text( Text(
text = content, text = content,

View file

@ -94,6 +94,7 @@ import io.element.android.libraries.designsystem.colors.AvatarColorsProvider
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
@ -498,6 +499,7 @@ internal fun BloomPreview() {
url = "aURL", url = "aURL",
size = AvatarSize.CurrentUserTopBar, size = AvatarSize.CurrentUserTopBar,
), ),
avatarType = AvatarType.User,
) )
}, },
actions = { actions = {

View file

@ -16,6 +16,9 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.components.avatar.internal.RoomAvatar
import io.element.android.libraries.designsystem.components.avatar.internal.SpaceAvatar
import io.element.android.libraries.designsystem.components.avatar.internal.UserAvatar
import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
@ -24,8 +27,8 @@ import io.element.android.libraries.designsystem.utils.CommonDrawables
@Composable @Composable
fun Avatar( fun Avatar(
avatarData: AvatarData, avatarData: AvatarData,
avatarType: AvatarType,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
avatarType: AvatarType = AvatarType.User,
contentDescription: String? = null, contentDescription: String? = null,
// If not null, will be used instead of the size from avatarData // If not null, will be used instead of the size from avatarData
forcedAvatarSize: Dp? = null, forcedAvatarSize: Dp? = null,
@ -38,6 +41,7 @@ fun Avatar(
avatarType = avatarType, avatarType = avatarType,
modifier = modifier, modifier = modifier,
hideAvatarImage = hideImage, hideAvatarImage = hideImage,
forcedAvatarSize = forcedAvatarSize,
contentDescription = contentDescription, contentDescription = contentDescription,
) )
AvatarType.User -> UserAvatar( AvatarType.User -> UserAvatar(
@ -52,33 +56,7 @@ fun Avatar(
avatarType = avatarType, avatarType = avatarType,
modifier = modifier, modifier = modifier,
hideAvatarImage = hideImage, hideAvatarImage = hideImage,
contentDescription = contentDescription,
)
}
}
@Composable
private fun UserAvatar(
avatarData: AvatarData,
modifier: Modifier = Modifier,
contentDescription: String? = null,
forcedAvatarSize: Dp? = null,
hideImage: Boolean = false,
) {
if (avatarData.url.isNullOrBlank() || hideImage) {
InitialLetterAvatar(
avatarData = avatarData,
avatarType = AvatarType.User,
forcedAvatarSize = forcedAvatarSize, forcedAvatarSize = forcedAvatarSize,
modifier = modifier,
contentDescription = contentDescription,
)
} else {
ImageAvatar(
avatarData = avatarData,
avatarType = AvatarType.User,
forcedAvatarSize = forcedAvatarSize,
modifier = modifier,
contentDescription = contentDescription, contentDescription = contentDescription,
) )
} }
@ -94,7 +72,10 @@ internal fun AvatarPreview(@PreviewParameter(AvatarDataProvider::class) avatarDa
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(16.dp) horizontalArrangement = Arrangement.spacedBy(16.dp)
) { ) {
Avatar(avatarData) Avatar(
avatarData = avatarData,
avatarType = AvatarType.User,
)
Text(text = avatarData.size.name + " " + avatarData.size.dp) Text(text = avatarData.size.name + " " + avatarData.size.dp)
} }
} }

View file

@ -11,7 +11,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
open class AvatarDataProvider : PreviewParameterProvider<AvatarData> { open class AvatarDataProvider : PreviewParameterProvider<AvatarData> {
override val values: Sequence<AvatarData> override val values: Sequence<AvatarData>
get() = AvatarSize.values() get() = AvatarSize.entries
.asSequence() .asSequence()
.map { .map {
sequenceOf( sequenceOf(

View file

@ -11,12 +11,22 @@ import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.Dp
@Composable @Composable
fun AvatarType.avatarShape(): Shape { fun AvatarType.User.avatarShape() = CircleShape
@Composable
fun AvatarType.Room.avatarShape() = CircleShape
@Composable
fun AvatarType.Space.avatarShape(avatarSize: Dp) = RoundedCornerShape(avatarSize * 0.25f)
@Composable
fun AvatarType.avatarShape(avatarSize: Dp): Shape {
return when (this) { return when (this) {
is AvatarType.Space -> RoundedCornerShape(cornerSize) is AvatarType.Space -> avatarShape(avatarSize)
is AvatarType.Room, is AvatarType.Room -> avatarShape()
AvatarType.User -> CircleShape is AvatarType.User -> avatarShape()
} }
} }

View file

@ -8,7 +8,6 @@
package io.element.android.libraries.designsystem.components.avatar package io.element.android.libraries.designsystem.components.avatar
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import androidx.compose.ui.unit.Dp
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
@ -22,7 +21,6 @@ sealed interface AvatarType {
) : AvatarType ) : AvatarType
data class Space( data class Space(
val cornerSize: Dp,
val isTombstoned: Boolean = false, val isTombstoned: Boolean = false,
) : AvatarType ) : AvatarType
} }

View file

@ -55,6 +55,7 @@ fun DmAvatars(
// Draw user avatar and cut top end corner // Draw user avatar and cut top end corner
Avatar( Avatar(
avatarData = userAvatarData, avatarData = userAvatarData,
avatarType = AvatarType.User,
modifier = Modifier modifier = Modifier
.align(Alignment.BottomStart) .align(Alignment.BottomStart)
.graphicsLayer { .graphicsLayer {
@ -85,6 +86,7 @@ fun DmAvatars(
// Draw other user avatar // Draw other user avatar
Avatar( Avatar(
avatarData = otherUserAvatarData, avatarData = otherUserAvatarData,
avatarType = AvatarType.User,
modifier = Modifier modifier = Modifier
.align(Alignment.TopEnd) .align(Alignment.TopEnd)
.clip(CircleShape) .clip(CircleShape)

View file

@ -5,10 +5,11 @@
* Please see LICENSE files in the repository root for full details. * Please see LICENSE files in the repository root for full details.
*/ */
package io.element.android.libraries.designsystem.components.avatar package io.element.android.libraries.designsystem.components.avatar.internal
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
@ -19,6 +20,10 @@ import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.components.avatar.anAvatarData
import io.element.android.libraries.designsystem.components.avatar.avatarShape
import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.preview.PreviewGroup
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
@ -33,8 +38,8 @@ private const val MAX_AVATAR_COUNT = 4
@Composable @Composable
internal fun AvatarCluster( internal fun AvatarCluster(
avatars: ImmutableList<AvatarData>, avatars: ImmutableList<AvatarData>,
avatarType: AvatarType,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
avatarType: AvatarType = AvatarType.User,
hideAvatarImages: Boolean = false, hideAvatarImages: Boolean = false,
contentDescription: String? = null, contentDescription: String? = null,
) { ) {
@ -49,12 +54,13 @@ internal fun AvatarCluster(
error("Unsupported number of avatars: 0") error("Unsupported number of avatars: 0")
} }
1 -> { 1 -> {
Avatar( InitialOrImageAvatar(
avatarData = limitedAvatars[0], avatarData = limitedAvatars[0],
avatarType = avatarType, hideAvatarImage = hideAvatarImages,
avatarShape = avatarType.avatarShape(limitedAvatars[0].size.dp),
forcedAvatarSize = null,
modifier = modifier, modifier = modifier,
contentDescription = contentDescription, contentDescription = contentDescription,
hideImage = hideAvatarImages
) )
} }
else -> { else -> {
@ -97,11 +103,13 @@ internal fun AvatarCluster(
y = yOffset, y = yOffset,
) )
) { ) {
Avatar( InitialOrImageAvatar(
avatarData = heroAvatar, avatarData = heroAvatar,
hideAvatarImage = hideAvatarImages,
avatarShape = avatarType.avatarShape(heroAvatarSize),
forcedAvatarSize = heroAvatarSize, forcedAvatarSize = heroAvatarSize,
avatarType = avatarType, modifier = Modifier,
hideImage = hideAvatarImages, contentDescription = contentDescription,
) )
} }
} }
@ -113,13 +121,24 @@ internal fun AvatarCluster(
@Preview(group = PreviewGroup.Avatars) @Preview(group = PreviewGroup.Avatars)
@Composable @Composable
internal fun AvatarClusterPreview() = ElementThemedPreview { internal fun AvatarClusterPreview() = ElementThemedPreview {
Row( Column(
horizontalArrangement = Arrangement.spacedBy(8.dp) verticalArrangement = Arrangement.spacedBy(8.dp),
) { ) {
for (ngOfAvatars in 1..5) { listOf(
AvatarCluster( AvatarType.User,
avatars = List(ngOfAvatars) { anAvatarData(it) }.toPersistentList(), AvatarType.Room(),
) AvatarType.Space(),
).forEach { avatarType ->
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
for (ngOfAvatars in 1..5) {
AvatarCluster(
avatars = List(ngOfAvatars) { anAvatarData(it) }.toPersistentList(),
avatarType = avatarType,
)
}
}
} }
} }
} }

View file

@ -5,7 +5,7 @@
* Please see LICENSE files in the repository root for full details. * Please see LICENSE files in the repository root for full details.
*/ */
package io.element.android.libraries.designsystem.components.avatar package io.element.android.libraries.designsystem.components.avatar.internal
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -14,19 +14,21 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import coil3.compose.AsyncImagePainter import coil3.compose.AsyncImagePainter
import coil3.compose.SubcomposeAsyncImage import coil3.compose.SubcomposeAsyncImage
import coil3.compose.SubcomposeAsyncImageContent import coil3.compose.SubcomposeAsyncImageContent
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import timber.log.Timber import timber.log.Timber
@Composable @Composable
internal fun ImageAvatar( internal fun ImageAvatar(
avatarData: AvatarData, avatarData: AvatarData,
avatarType: AvatarType, avatarShape: Shape,
forcedAvatarSize: Dp?, forcedAvatarSize: Dp?,
modifier: Modifier = Modifier.Companion, modifier: Modifier = Modifier,
contentDescription: String? = null, contentDescription: String? = null,
) { ) {
val size = forcedAvatarSize ?: avatarData.size.dp val size = forcedAvatarSize ?: avatarData.size.dp
@ -35,8 +37,8 @@ internal fun ImageAvatar(
contentDescription = contentDescription, contentDescription = contentDescription,
contentScale = ContentScale.Companion.Crop, contentScale = ContentScale.Companion.Crop,
modifier = modifier modifier = modifier
.size(size) .size(size)
.clip(avatarType.avatarShape()) .clip(avatarShape)
) { ) {
val collectedState by painter.state.collectAsState() val collectedState by painter.state.collectAsState()
when (val state = collectedState) { when (val state = collectedState) {
@ -50,14 +52,14 @@ internal fun ImageAvatar(
} }
InitialLetterAvatar( InitialLetterAvatar(
avatarData = avatarData, avatarData = avatarData,
avatarType = avatarType, avatarShape = avatarShape,
forcedAvatarSize = forcedAvatarSize, forcedAvatarSize = forcedAvatarSize,
contentDescription = contentDescription, contentDescription = contentDescription,
) )
} }
else -> InitialLetterAvatar( else -> InitialLetterAvatar(
avatarData = avatarData, avatarData = avatarData,
avatarType = avatarType, avatarShape = avatarShape,
forcedAvatarSize = forcedAvatarSize, forcedAvatarSize = forcedAvatarSize,
contentDescription = contentDescription, contentDescription = contentDescription,
) )

View file

@ -5,26 +5,28 @@
* Please see LICENSE files in the repository root for full details. * Please see LICENSE files in the repository root for full details.
*/ */
package io.element.android.libraries.designsystem.components.avatar package io.element.android.libraries.designsystem.components.avatar.internal
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import io.element.android.libraries.designsystem.colors.AvatarColorsProvider import io.element.android.libraries.designsystem.colors.AvatarColorsProvider
import io.element.android.libraries.designsystem.components.avatar.AvatarData
@Composable @Composable
internal fun InitialLetterAvatar( internal fun InitialLetterAvatar(
avatarData: AvatarData, avatarData: AvatarData,
avatarType: AvatarType, avatarShape: Shape,
forcedAvatarSize: Dp?, forcedAvatarSize: Dp?,
contentDescription: String?, contentDescription: String?,
modifier: Modifier = Modifier.Companion, modifier: Modifier = Modifier,
) { ) {
val avatarColors = AvatarColorsProvider.provide(avatarData.id) val avatarColors = AvatarColorsProvider.provide(avatarData.id)
TextAvatar( TextAvatar(
text = avatarData.initialLetter, text = avatarData.initialLetter,
size = forcedAvatarSize ?: avatarData.size.dp, size = forcedAvatarSize ?: avatarData.size.dp,
avatarType = avatarType, avatarShape = avatarShape,
colors = avatarColors, colors = avatarColors,
contentDescription = contentDescription, contentDescription = contentDescription,
modifier = modifier modifier = modifier

View file

@ -0,0 +1,41 @@
/*
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.designsystem.components.avatar.internal
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.Dp
import io.element.android.libraries.designsystem.components.avatar.AvatarData
@Composable
internal fun InitialOrImageAvatar(
avatarData: AvatarData,
hideAvatarImage: Boolean,
forcedAvatarSize: Dp?,
avatarShape: Shape,
contentDescription: String?,
modifier: Modifier = Modifier,
) {
when {
avatarData.url.isNullOrBlank() || hideAvatarImage -> InitialLetterAvatar(
avatarData = avatarData,
avatarShape = avatarShape,
forcedAvatarSize = forcedAvatarSize,
modifier = modifier,
contentDescription = contentDescription,
)
else -> ImageAvatar(
avatarData = avatarData,
avatarShape = avatarShape,
forcedAvatarSize = forcedAvatarSize,
modifier = modifier,
contentDescription = contentDescription,
)
}
}

View file

@ -5,10 +5,14 @@
* Please see LICENSE files in the repository root for full details. * Please see LICENSE files in the repository root for full details.
*/ */
package io.element.android.libraries.designsystem.components.avatar package io.element.android.libraries.designsystem.components.avatar.internal
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.components.avatar.avatarShape
@Composable @Composable
internal fun RoomAvatar( internal fun RoomAvatar(
@ -16,39 +20,33 @@ internal fun RoomAvatar(
avatarType: AvatarType.Room, avatarType: AvatarType.Room,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
hideAvatarImage: Boolean = false, hideAvatarImage: Boolean = false,
forcedAvatarSize: Dp? = null,
contentDescription: String? = null, contentDescription: String? = null,
) { ) {
when { when {
avatarType.isTombstoned -> { avatarType.isTombstoned -> {
TombstonedRoomAvatar( TombstonedRoomAvatar(
size = avatarData.size, size = forcedAvatarSize ?: avatarData.size.dp,
modifier = modifier, modifier = modifier,
avatarType = avatarType, avatarShape = avatarType.avatarShape(),
contentDescription = contentDescription contentDescription = contentDescription
) )
} }
avatarData.url != null || avatarType.heroes.isEmpty() -> { avatarData.url != null || avatarType.heroes.isEmpty() -> {
if (avatarData.url.isNullOrBlank() || hideAvatarImage) { InitialOrImageAvatar(
InitialLetterAvatar( avatarData = avatarData,
avatarData = avatarData, hideAvatarImage = hideAvatarImage,
avatarType = avatarType, avatarShape = avatarType.avatarShape(),
modifier = modifier, forcedAvatarSize = forcedAvatarSize,
contentDescription = contentDescription, modifier = modifier,
forcedAvatarSize = null, contentDescription = contentDescription,
) )
} else {
ImageAvatar(
avatarData = avatarData,
avatarType = avatarType,
forcedAvatarSize = null,
modifier = modifier,
contentDescription = contentDescription,
)
}
} }
else -> { else -> {
AvatarCluster( AvatarCluster(
avatars = avatarType.heroes, avatars = avatarType.heroes,
// Note: even for a room avatar, we use UserAvatarType here to display the avatar of heroes
avatarType = AvatarType.User,
modifier = modifier, modifier = modifier,
hideAvatarImages = hideAvatarImage, hideAvatarImages = hideAvatarImage,
contentDescription = contentDescription contentDescription = contentDescription

View file

@ -5,7 +5,7 @@
* Please see LICENSE files in the repository root for full details. * Please see LICENSE files in the repository root for full details.
*/ */
package io.element.android.libraries.designsystem.components.avatar package io.element.android.libraries.designsystem.components.avatar.internal
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@ -13,37 +13,38 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.components.avatar.anAvatarData
import io.element.android.libraries.designsystem.components.avatar.avatarShape
import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.designsystem.utils.CommonDrawables
@Composable @Composable
fun SpaceAvatar( internal fun SpaceAvatar(
avatarData: AvatarData, avatarData: AvatarData,
avatarType: AvatarType.Space, avatarType: AvatarType.Space,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
forcedAvatarSize: Dp? = null,
hideAvatarImage: Boolean = false, hideAvatarImage: Boolean = false,
contentDescription: String? = null, contentDescription: String? = null,
) { ) {
val size = forcedAvatarSize ?: avatarData.size.dp
when { when {
avatarType.isTombstoned -> TombstonedRoomAvatar( avatarType.isTombstoned -> TombstonedRoomAvatar(
size = avatarData.size, size = size,
avatarType = avatarType, avatarShape = avatarType.avatarShape(size),
modifier = modifier, modifier = modifier,
contentDescription = contentDescription, contentDescription = contentDescription,
) )
avatarData.url.isNullOrBlank() || hideAvatarImage -> InitialLetterAvatar( else -> InitialOrImageAvatar(
avatarData = avatarData, avatarData = avatarData,
avatarType = avatarType, hideAvatarImage = hideAvatarImage,
modifier = modifier, avatarShape = avatarType.avatarShape(size),
contentDescription = contentDescription, forcedAvatarSize = forcedAvatarSize,
forcedAvatarSize = null,
)
else -> ImageAvatar(
avatarData = avatarData,
avatarType = avatarType,
forcedAvatarSize = null,
modifier = modifier, modifier = modifier,
contentDescription = contentDescription, contentDescription = contentDescription,
) )
@ -62,12 +63,11 @@ internal fun SpaceAvatarPreview() =
) { ) {
SpaceAvatar( SpaceAvatar(
avatarData = anAvatarData(), avatarData = anAvatarData(),
avatarType = AvatarType.Space(cornerSize = 16.dp), avatarType = AvatarType.Space(),
) )
SpaceAvatar( SpaceAvatar(
avatarData = anAvatarData(), avatarData = anAvatarData(),
avatarType = AvatarType.Space( avatarType = AvatarType.Space(
cornerSize = 16.dp,
isTombstoned = true, isTombstoned = true,
), ),
) )

View file

@ -5,7 +5,7 @@
* Please see LICENSE files in the repository root for full details. * Please see LICENSE files in the repository root for full details.
*/ */
package io.element.android.libraries.designsystem.components.avatar package io.element.android.libraries.designsystem.components.avatar.internal
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@ -17,6 +17,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
@ -25,6 +26,8 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import io.element.android.compound.theme.AvatarColors import io.element.android.compound.theme.AvatarColors
import io.element.android.compound.theme.ElementTheme import io.element.android.compound.theme.ElementTheme
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.components.avatar.avatarShape
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.text.toSp import io.element.android.libraries.designsystem.text.toSp
@ -36,13 +39,13 @@ internal fun TextAvatar(
size: Dp, size: Dp,
colors: AvatarColors, colors: AvatarColors,
contentDescription: String?, contentDescription: String?,
avatarType: AvatarType, avatarShape: Shape,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
Box( Box(
modifier modifier
.size(size) .size(size)
.clip(avatarType.avatarShape()) .clip(avatarShape)
.background(color = colors.background) .background(color = colors.background)
) { ) {
val fontSize = size.toSp() / 2 val fontSize = size.toSp() / 2
@ -74,7 +77,7 @@ internal fun TextAvatarPreview() = ElementPreview {
listOf( listOf(
AvatarType.User, AvatarType.User,
AvatarType.Room(), AvatarType.Room(),
AvatarType.Space(8.dp), AvatarType.Space(),
).forEach { avatarType -> ).forEach { avatarType ->
TextAvatar( TextAvatar(
text = "AB", text = "AB",
@ -83,7 +86,7 @@ internal fun TextAvatarPreview() = ElementPreview {
background = ElementTheme.colors.bgSubtlePrimary, background = ElementTheme.colors.bgSubtlePrimary,
foreground = ElementTheme.colors.iconPrimary, foreground = ElementTheme.colors.iconPrimary,
), ),
avatarType = avatarType, avatarShape = avatarType.avatarShape(40.dp),
contentDescription = null, contentDescription = null,
) )
} }

View file

@ -5,11 +5,15 @@
* Please see LICENSE files in the repository root for full details. * Please see LICENSE files in the repository root for full details.
*/ */
package io.element.android.libraries.designsystem.components.avatar package io.element.android.libraries.designsystem.components.avatar.internal
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.AvatarColors import io.element.android.compound.theme.AvatarColors
import io.element.android.compound.theme.ElementTheme import io.element.android.compound.theme.ElementTheme
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
@ -17,20 +21,20 @@ import io.element.android.libraries.designsystem.preview.PreviewGroup
@Composable @Composable
internal fun TombstonedRoomAvatar( internal fun TombstonedRoomAvatar(
size: AvatarSize, size: Dp,
avatarType: AvatarType, avatarShape: Shape,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
contentDescription: String? = null, contentDescription: String? = null,
) { ) {
TextAvatar( TextAvatar(
text = "!", text = "!",
size = size.dp, size = size,
colors = AvatarColors( colors = AvatarColors(
background = ElementTheme.colors.bgSubtlePrimary, background = ElementTheme.colors.bgSubtlePrimary,
foreground = ElementTheme.colors.iconTertiary foreground = ElementTheme.colors.iconTertiary
), ),
modifier = modifier, modifier = modifier,
avatarType = avatarType, avatarShape = avatarShape,
contentDescription = contentDescription, contentDescription = contentDescription,
) )
} }
@ -39,8 +43,8 @@ internal fun TombstonedRoomAvatar(
@Composable @Composable
internal fun TombstonedRoomAvatarPreview() = ElementPreview { internal fun TombstonedRoomAvatarPreview() = ElementPreview {
TombstonedRoomAvatar( TombstonedRoomAvatar(
size = AvatarSize.RoomListItem, size = 52.dp,
avatarType = AvatarType.Room(), avatarShape = CircleShape,
contentDescription = null, contentDescription = null,
) )
} }

View file

@ -0,0 +1,33 @@
/*
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.designsystem.components.avatar.internal
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.components.avatar.avatarShape
@Composable
internal fun UserAvatar(
avatarData: AvatarData,
modifier: Modifier = Modifier,
contentDescription: String? = null,
forcedAvatarSize: Dp? = null,
hideImage: Boolean = false,
) {
InitialOrImageAvatar(
avatarData = avatarData,
hideAvatarImage = hideImage,
avatarShape = AvatarType.User.avatarShape(),
modifier = modifier,
contentDescription = contentDescription,
forcedAvatarSize = forcedAvatarSize,
)
}

View file

@ -5,7 +5,7 @@
* Please see LICENSE files in the repository root for full details. * Please see LICENSE files in the repository root for full details.
*/ */
package io.element.android.libraries.designsystem.components.avatar package io.element.android.libraries.designsystem.components.avatar.internal
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@ -16,6 +16,9 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.avatarColors import io.element.android.compound.theme.avatarColors
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.components.avatar.anAvatarData
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
@ -33,7 +36,10 @@ internal fun UserAvatarColorsPreview() = ElementPreview {
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
// Note: it's OK, since the hash of "0" is 0, the hash of "1" is 1, etc. // Note: it's OK, since the hash of "0" is 0, the hash of "1" is 1, etc.
Avatar(anAvatarData(id = "$it")) Avatar(
avatarData = anAvatarData(id = "$it"),
avatarType = AvatarType.User,
)
Text(text = "Color index $it") Text(text = "Color index $it")
} }
} }

View file

@ -25,6 +25,7 @@ import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Button
@ -62,6 +63,7 @@ fun CreateDmConfirmationBottomSheet(
Spacer(modifier = Modifier.height(24.dp)) Spacer(modifier = Modifier.height(24.dp))
Avatar( Avatar(
avatarData = matrixUser.getAvatarData(AvatarSize.DmCreationConfirmation), avatarData = matrixUser.getAvatarData(AvatarSize.DmCreationConfirmation),
avatarType = AvatarType.User,
) )
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
Text( Text(

View file

@ -13,7 +13,6 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
@ -61,7 +60,6 @@ fun EditableAvatarView(
val a11yAvatar = stringResource(CommonStrings.a11y_avatar) val a11yAvatar = stringResource(CommonStrings.a11y_avatar)
Box( Box(
modifier = Modifier modifier = Modifier
.size(avatarSize.dp)
.clickable( .clickable(
interactionSource = remember { MutableInteractionSource() }, interactionSource = remember { MutableInteractionSource() },
onClick = onAvatarClick, onClick = onAvatarClick,
@ -75,16 +73,20 @@ fun EditableAvatarView(
when (avatarUrl?.scheme) { when (avatarUrl?.scheme) {
null, "mxc" -> { null, "mxc" -> {
Avatar( Avatar(
avatarData = AvatarData(matrixId, displayName, avatarUrl?.toString(), size = avatarSize), avatarData = AvatarData(
id = matrixId,
name = displayName,
url = avatarUrl?.toString(),
size = avatarSize,
),
avatarType = avatarType, avatarType = avatarType,
modifier = Modifier.fillMaxSize(),
) )
} }
else -> { else -> {
UnsavedAvatar( UnsavedAvatar(
avatarUri = avatarUrl, avatarUri = avatarUrl,
avatarSize = avatarSize,
avatarType = avatarType, avatarType = avatarType,
modifier = Modifier.fillMaxSize(),
) )
} }
} }

View file

@ -18,6 +18,7 @@ import io.element.android.compound.theme.ElementTheme
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
@ -35,7 +36,11 @@ fun InviteSenderView(
modifier = modifier, modifier = modifier,
) { ) {
Box(modifier = Modifier.padding(vertical = 2.dp)) { Box(modifier = Modifier.padding(vertical = 2.dp)) {
Avatar(avatarData = inviteSender.avatarData, hideImage = hideAvatarImage) Avatar(
avatarData = inviteSender.avatarData,
avatarType = AvatarType.User,
hideImage = hideAvatarImage,
)
} }
Text( Text(
text = inviteSender.annotatedString(), text = inviteSender.annotatedString(),

View file

@ -23,6 +23,7 @@ import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme import io.element.android.compound.theme.ElementTheme
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
@ -65,6 +66,7 @@ private fun MatrixUserHeaderContent(
modifier = Modifier modifier = Modifier
.padding(vertical = 12.dp), .padding(vertical = 12.dp),
avatarData = matrixUser.getAvatarData(size = AvatarSize.UserPreference), avatarData = matrixUser.getAvatarData(size = AvatarSize.UserPreference),
avatarType = AvatarType.User,
) )
Spacer(modifier = Modifier.width(16.dp)) Spacer(modifier = Modifier.width(16.dp))
Column( Column(

View file

@ -30,6 +30,7 @@ import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Icon
@ -54,7 +55,10 @@ fun SelectedUser(
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
Avatar(matrixUser.getAvatarData(size = AvatarSize.SelectedUser)) Avatar(
avatarData = matrixUser.getAvatarData(size = AvatarSize.SelectedUser),
avatarType = AvatarType.User,
)
Text( Text(
modifier = Modifier.clipToBounds(), modifier = Modifier.clipToBounds(),
text = matrixUser.getBestName(), text = matrixUser.getBestName(),

View file

@ -27,6 +27,7 @@ import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
@ -46,7 +47,10 @@ fun UnresolvedUserRow(
.padding(start = 16.dp, top = 8.dp, end = 16.dp, bottom = 8.dp), .padding(start = 16.dp, top = 8.dp, end = 16.dp, bottom = 8.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Avatar(avatarData) Avatar(
avatarData = avatarData,
avatarType = AvatarType.User,
)
Column( Column(
modifier = Modifier modifier = Modifier
.padding(start = 12.dp), .padding(start = 12.dp),

View file

@ -28,6 +28,7 @@ import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage import coil3.compose.AsyncImage
import coil3.request.ImageRequest import coil3.request.ImageRequest
import io.element.android.compound.theme.ElementTheme import io.element.android.compound.theme.ElementTheme
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.components.avatar.avatarShape import io.element.android.libraries.designsystem.components.avatar.avatarShape
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
@ -43,12 +44,13 @@ import io.element.android.libraries.designsystem.theme.temporaryColorBgSpecial
@Composable @Composable
fun UnsavedAvatar( fun UnsavedAvatar(
avatarUri: Uri?, avatarUri: Uri?,
avatarSize: AvatarSize,
avatarType: AvatarType, avatarType: AvatarType,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val commonModifier = modifier val commonModifier = modifier
.size(70.dp) .size(avatarSize.dp)
.clip(avatarType.avatarShape()) .clip(avatarType.avatarShape(avatarSize.dp))
if (avatarUri != null) { if (avatarUri != null) {
val context = LocalContext.current val context = LocalContext.current
@ -69,7 +71,7 @@ fun UnsavedAvatar(
contentDescription = null, contentDescription = null,
modifier = Modifier modifier = Modifier
.align(Alignment.Center) .align(Alignment.Center)
.size(40.dp), .size(avatarSize.dp * 4 / 7),
tint = ElementTheme.colors.iconSecondary, tint = ElementTheme.colors.iconSecondary,
) )
} }
@ -83,9 +85,9 @@ internal fun UnsavedAvatarPreview() = ElementPreview {
modifier = Modifier.padding(8.dp), modifier = Modifier.padding(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp),
) { ) {
UnsavedAvatar(null, AvatarType.User) UnsavedAvatar(null, AvatarSize.EditRoomDetails, AvatarType.User)
UnsavedAvatar(Uri.EMPTY, AvatarType.User) UnsavedAvatar(Uri.EMPTY, AvatarSize.EditRoomDetails, AvatarType.User)
UnsavedAvatar(null, AvatarType.Space(8.dp)) UnsavedAvatar(null, AvatarSize.EditRoomDetails, AvatarType.Space())
UnsavedAvatar(Uri.EMPTY, AvatarType.Space(8.dp)) UnsavedAvatar(Uri.EMPTY, AvatarSize.EditRoomDetails, AvatarType.Space())
} }
} }

View file

@ -21,6 +21,7 @@ import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme import io.element.android.compound.theme.ElementTheme
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
@Composable @Composable
@ -38,7 +39,10 @@ internal fun UserRow(
.padding(start = 16.dp, top = 4.dp, end = 16.dp, bottom = 4.dp), .padding(start = 16.dp, top = 4.dp, end = 16.dp, bottom = 4.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Avatar(avatarData) Avatar(
avatarData = avatarData,
avatarType = AvatarType.User,
)
Column( Column(
modifier = Modifier modifier = Modifier
.padding(start = 12.dp) .padding(start = 12.dp)

View file

@ -29,6 +29,7 @@ import io.element.android.libraries.designsystem.colors.AvatarColorsProvider
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.components.list.ListItemContent
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
@ -139,12 +140,13 @@ private fun SenderRow(
) { ) {
val id = mediaInfo.senderId?.value ?: "@Alice:domain" val id = mediaInfo.senderId?.value ?: "@Alice:domain"
Avatar( Avatar(
AvatarData( avatarData = AvatarData(
id = id, id = id,
name = mediaInfo.senderName, name = mediaInfo.senderName,
url = mediaInfo.senderAvatar, url = mediaInfo.senderAvatar,
size = AvatarSize.MediaSender, size = AvatarSize.MediaSender,
) ),
avatarType = AvatarType.User,
) )
Column( Column(
modifier = Modifier modifier = Modifier

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:69fdae3edcd02635868f209a2c1ff21ce807ffb4c6d44dcf89fb0f54e1b9c1f3
size 68396

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6da7fb8d713d5d8255593f2007edf068870dbd05225fe2c0562d197a1739b876
size 67313

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3c7a7b64a83f68670c0c38eac2782ad5121fe18638c6a21f3b868d0cb4820c7c
size 59391

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:57545d6b9690b892859f7bf2d2aa92d3fdc1a3ae7b4849e39cf6df10bb0dbbb4
size 8719

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:def7dc550efea75745153ad0cf71c7d92905205549965ba1f192ee3612cec9c4
size 8041

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c6afcae51ee69dcdb9dbf60c8f72e55b9b07e4feb72dfba0a40ffeebed6ab681
size 25730

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d61143149a23cbb1549ee11b1ad00fd4eee16d173407bc847bde91c39cd1b5bc
size 9178

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0b632082558328aa485712b8684016a84a9d1eaba7f96cd92b47d364e6c9169f
size 7981

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:e20e9a7264a6784fbaf6525e73a07318abd27bcf025541b880fd27ac0cca4ba4 oid sha256:82c54bb785e7c2c301109f25b59e527193e1c786fdfda1252b82702c685cfd7d
size 69466 size 68516

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:991318dc36c1e6b86a753241638c36adafb896f852f4d5d0e15dd251fa025311 oid sha256:ca5be5bfe66000d7730a9405ffb2992644f638fb58b28ac3f647c0c242ed8c4d
size 68974 size 68007