design(space): match figma for Space views

This commit is contained in:
ganfra 2025-10-15 11:28:55 +02:00
parent 35cf3aeb0b
commit 6b2e4ffbbd
3 changed files with 72 additions and 49 deletions

View file

@ -14,6 +14,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
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.HorizontalDivider
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.CurrentUserMembership
import io.element.android.libraries.matrix.ui.components.SpaceHeaderRootView import io.element.android.libraries.matrix.ui.components.SpaceHeaderRootView
@ -47,6 +48,9 @@ fun HomeSpacesView(
) )
} }
} }
item {
HorizontalDivider()
}
state.spaceRooms.forEach { spaceRoom -> state.spaceRooms.forEach { spaceRoom ->
item(spaceRoom.roomId) { item(spaceRoom.roomId) {
val isInvitation = spaceRoom.state == CurrentUserMembership.INVITED val isInvitation = spaceRoom.state == CurrentUserMembership.INVITED

View file

@ -49,6 +49,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
import io.element.android.libraries.designsystem.theme.components.DropdownMenu import io.element.android.libraries.designsystem.theme.components.DropdownMenu
import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
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.IconButton import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Scaffold
@ -177,6 +178,9 @@ private fun SpaceViewContent(
onTopicClick = onTopicClick onTopicClick = onTopicClick
) )
} }
item {
HorizontalDivider()
}
} }
state.children.forEach { spaceRoom -> state.children.forEach { spaceRoom ->
item { item {

View file

@ -10,6 +10,7 @@ package io.element.android.libraries.matrix.ui.components
import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.IntrinsicSize
@ -42,6 +43,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.modifiers.onKeyboardContextMenuAction import io.element.android.libraries.designsystem.modifiers.onKeyboardContextMenuAction
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.HorizontalDivider
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
import io.element.android.libraries.designsystem.theme.unreadIndicator import io.element.android.libraries.designsystem.theme.unreadIndicator
@ -56,6 +58,9 @@ import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
/**
* Figma reference: https://www.figma.com/design/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?node-id=3643-2079&m=dev
*/
@Composable @Composable
fun SpaceRoomItemView( fun SpaceRoomItemView(
spaceRoom: SpaceRoom, spaceRoom: SpaceRoom,
@ -67,43 +72,65 @@ fun SpaceRoomItemView(
trailingAction: @Composable (() -> Unit)? = null, trailingAction: @Composable (() -> Unit)? = null,
bottomAction: @Composable (() -> Unit)? = null, bottomAction: @Composable (() -> Unit)? = null,
) { ) {
SpaceRoomItemScaffold( val clickModifier = Modifier
modifier = modifier, .combinedClickable(
avatarData = spaceRoom.getAvatarData(AvatarSize.SpaceListItem), onClick = onClick,
isSpace = spaceRoom.isSpace, onLongClick = onLongClick,
hideAvatars = hideAvatars, onLongClickLabel = stringResource(CommonStrings.action_open_context_menu),
heroes = spaceRoom.heroes indication = ripple(),
.map { hero -> hero.getAvatarData(AvatarSize.SpaceListItem) } interactionSource = remember { MutableInteractionSource() }
.toImmutableList(),
onClick = onClick,
onLongClick = onLongClick,
trailingAction = trailingAction,
) {
NameAndIndicatorRow(
name = spaceRoom.displayName,
showIndicator = showUnreadIndicator
) )
Spacer(modifier = Modifier.height(1.dp)) .onKeyboardContextMenuAction { onLongClick }
SubtitleRow( Box(modifier = modifier.then(clickModifier)) {
visibilityIcon = spaceRoom.visibilityIcon(), Column(
subtitle = spaceRoom.subtitle() modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp),
) {
SpaceRoomItemScaffold(
avatarData = spaceRoom.getAvatarData(AvatarSize.SpaceListItem),
isSpace = spaceRoom.isSpace,
hideAvatars = hideAvatars,
heroes = spaceRoom.heroes
.map { hero -> hero.getAvatarData(AvatarSize.SpaceListItem) }
.toImmutableList(),
trailingAction = trailingAction,
) {
NameAndIndicatorRow(
name = spaceRoom.displayName,
showIndicator = showUnreadIndicator
)
Spacer(modifier = Modifier.height(1.dp))
SubtitleRow(
visibilityIcon = spaceRoom.visibilityIcon(),
subtitle = spaceRoom.subtitle()
)
Spacer(modifier = Modifier.height(1.dp))
val info = spaceRoom.info()
if (info.isNotBlank()) {
Text(
modifier = Modifier.weight(1f),
style = ElementTheme.typography.fontBodyMdRegular,
text = info,
color = ElementTheme.colors.textSecondary,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
if (bottomAction != null) {
Spacer(modifier = Modifier.height(12.dp))
// Match the padding of the text content (avatar + spacer)
Box(modifier = Modifier.padding(start = AvatarSize.SpaceListItem.dp + 16.dp)) {
bottomAction()
}
Spacer(modifier = Modifier.height(4.dp))
}
}
HorizontalDivider(
modifier = Modifier
// Match the padding of the text content (padding + avatar + spacer)
.padding(start = AvatarSize.SpaceListItem.dp + 16.dp + 16.dp)
.align(Alignment.BottomCenter)
) )
Spacer(modifier = Modifier.height(1.dp))
val info = spaceRoom.info()
if (info.isNotBlank()) {
Text(
modifier = Modifier.weight(1f),
style = ElementTheme.typography.fontBodyMdRegular,
text = info,
color = ElementTheme.colors.textSecondary,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
if (bottomAction != null) {
Spacer(modifier = Modifier.height(12.dp))
bottomAction()
}
} }
} }
@ -170,28 +197,16 @@ private fun SpaceRoomItemScaffold(
avatarData: AvatarData, avatarData: AvatarData,
isSpace: Boolean, isSpace: Boolean,
heroes: ImmutableList<AvatarData>, heroes: ImmutableList<AvatarData>,
onClick: () -> Unit,
onLongClick: () -> Unit,
hideAvatars: Boolean, hideAvatars: Boolean,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
trailingAction: @Composable (() -> Unit)? = null, trailingAction: @Composable (() -> Unit)? = null,
content: @Composable ColumnScope.() -> Unit, content: @Composable ColumnScope.() -> Unit,
) { ) {
val clickModifier = Modifier
.combinedClickable(
onClick = onClick,
onLongClick = onLongClick,
onLongClickLabel = stringResource(CommonStrings.action_open_context_menu),
indication = ripple(),
interactionSource = remember { MutableInteractionSource() }
)
.onKeyboardContextMenuAction { onLongClick }
Row( Row(
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.then(clickModifier)
.padding(horizontal = 16.dp, vertical = 8.dp)
.height(IntrinsicSize.Min), .height(IntrinsicSize.Min),
verticalAlignment = Alignment.CenterVertically,
) { ) {
Avatar( Avatar(
avatarData = avatarData, avatarData = avatarData,
@ -249,7 +264,7 @@ internal fun SpaceRoomItemViewPreview(@PreviewParameter(SpaceRoomProvider::class
hideAvatars = false, hideAvatars = false,
onClick = {}, onClick = {},
onLongClick = {}, onLongClick = {},
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth().padding(8.dp),
bottomAction = if (spaceRoom.state == CurrentUserMembership.INVITED) { bottomAction = if (spaceRoom.state == CurrentUserMembership.INVITED) {
{ InviteButtonsRowMolecule({}, {}) } { InviteButtonsRowMolecule({}, {}) }
} else { } else {