Merge pull request #5197 from element-hq/feature/bma/spaceUiComponent
Space UI component
This commit is contained in:
commit
0796cf993d
69 changed files with 537 additions and 25 deletions
|
|
@ -46,7 +46,7 @@ import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewTitleAt
|
|||
import io.element.android.libraries.designsystem.atomic.molecules.ButtonRowMolecule
|
||||
import io.element.android.libraries.designsystem.atomic.molecules.IconTitlePlaceholdersRowMolecule
|
||||
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
|
||||
import io.element.android.libraries.designsystem.atomic.molecules.RoomPreviewMembersCountMolecule
|
||||
import io.element.android.libraries.designsystem.atomic.molecules.MembersCountMolecule
|
||||
import io.element.android.libraries.designsystem.atomic.organisms.RoomPreviewOrganism
|
||||
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
|
||||
import io.element.android.libraries.designsystem.components.Announcement
|
||||
|
|
@ -546,7 +546,7 @@ private fun DefaultLoadedContent(
|
|||
},
|
||||
memberCount = {
|
||||
if (contentState.showMemberCount) {
|
||||
RoomPreviewMembersCountMolecule(memberCount = contentState.numberOfMembers ?: 0)
|
||||
MembersCountMolecule(memberCount = contentState.numberOfMembers ?: 0)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import io.element.android.libraries.designsystem.components.async.AsyncIndicator
|
|||
import io.element.android.libraries.designsystem.components.async.AsyncIndicatorHost
|
||||
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.AvatarRow
|
||||
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
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import io.element.android.libraries.designsystem.theme.components.Icon
|
|||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
|
||||
@Composable
|
||||
fun RoomPreviewMembersCountMolecule(
|
||||
fun MembersCountMolecule(
|
||||
memberCount: Long,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
|
@ -51,13 +51,13 @@ fun RoomPreviewMembersCountMolecule(
|
|||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun RoomPreviewMembersCountMoleculePreview() = ElementPreview {
|
||||
internal fun MembersCountMoleculePreview() = ElementPreview {
|
||||
Column(
|
||||
modifier = Modifier.padding(8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
RoomPreviewMembersCountMolecule(memberCount = 1)
|
||||
RoomPreviewMembersCountMolecule(memberCount = 888)
|
||||
RoomPreviewMembersCountMolecule(memberCount = 123_456)
|
||||
MembersCountMolecule(memberCount = 1)
|
||||
MembersCountMolecule(memberCount = 888)
|
||||
MembersCountMolecule(memberCount = 123_456)
|
||||
}
|
||||
}
|
||||
|
|
@ -16,8 +16,8 @@ open class AvatarDataProvider : PreviewParameterProvider<AvatarData> {
|
|||
.map {
|
||||
sequenceOf(
|
||||
anAvatarData(size = it),
|
||||
anAvatarData(size = it).copy(name = null),
|
||||
anAvatarData(size = it).copy(url = "aUrl"),
|
||||
anAvatarData(size = it, name = null),
|
||||
anAvatarData(size = it, url = "aUrl"),
|
||||
)
|
||||
}
|
||||
.flatten()
|
||||
|
|
@ -26,10 +26,12 @@ open class AvatarDataProvider : PreviewParameterProvider<AvatarData> {
|
|||
fun anAvatarData(
|
||||
// Let's the id not start with a 'a'.
|
||||
id: String = "@id_of_alice:server.org",
|
||||
name: String = "Alice",
|
||||
name: String? = "Alice",
|
||||
url: String? = null,
|
||||
size: AvatarSize = AvatarSize.RoomListItem,
|
||||
) = AvatarData(
|
||||
id = id,
|
||||
name = name,
|
||||
url = url,
|
||||
size = size,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.knockrequests.impl.banner
|
||||
package io.element.android.libraries.designsystem.components.avatar
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.padding
|
||||
|
|
@ -23,10 +23,7 @@ import androidx.compose.ui.platform.LocalLayoutDirection
|
|||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
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.AvatarSize
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarType
|
||||
import io.element.android.libraries.designsystem.components.avatar.internal.OverlapRatioProvider
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.text.toPx
|
||||
|
|
@ -41,6 +38,7 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
* @param modifier Jetpack Compose modifier
|
||||
* @param overlapRatio the overlap ration. When 0f, avatars will render without overlap, when 1f
|
||||
* only the first avatar will be visible
|
||||
* @param lastOnTop if true, the last visible avatar will be rendered on top.
|
||||
*/
|
||||
@Composable
|
||||
fun AvatarRow(
|
||||
|
|
@ -48,6 +46,7 @@ fun AvatarRow(
|
|||
avatarType: AvatarType,
|
||||
modifier: Modifier = Modifier,
|
||||
overlapRatio: Float = 0.5f,
|
||||
lastOnTop: Boolean = false,
|
||||
) {
|
||||
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
|
||||
Box(
|
||||
|
|
@ -57,23 +56,35 @@ fun AvatarRow(
|
|||
val avatarSize = avatarDataList.firstOrNull()?.size?.dp ?: return
|
||||
val avatarSizePx = avatarSize.toPx()
|
||||
avatarDataList
|
||||
.reversed()
|
||||
.let {
|
||||
if (lastOnTop) {
|
||||
it
|
||||
} else {
|
||||
it.reversed()
|
||||
}
|
||||
}
|
||||
.forEachIndexed { index, avatarData ->
|
||||
val startPadding = if (lastOnTop) {
|
||||
avatarSize * (1 - overlapRatio) * index
|
||||
} else {
|
||||
avatarSize * (1 - overlapRatio) * (lastItemIndex - index)
|
||||
}
|
||||
Avatar(
|
||||
modifier = Modifier
|
||||
.padding(start = avatarSize * (1 - overlapRatio) * (lastItemIndex - index))
|
||||
.padding(start = startPadding)
|
||||
.graphicsLayer {
|
||||
compositingStrategy = CompositingStrategy.Offscreen
|
||||
}
|
||||
.drawWithContent {
|
||||
// Draw content and clear the pixels for the avatar on the left (right in RTL).
|
||||
// Draw content and clear the pixels for the avatar on the left (right in RTL) or when lastOnTop is true on
|
||||
// the right (left in RTL).
|
||||
drawContent()
|
||||
val xOffset = if (isRtl) {
|
||||
size.width - avatarSizePx * (overlapRatio - 0.5f)
|
||||
} else {
|
||||
0f + avatarSizePx * (overlapRatio - 0.5f)
|
||||
}
|
||||
if (index < lastItemIndex) {
|
||||
val xOffset = if (isRtl == lastOnTop) {
|
||||
avatarSizePx * (overlapRatio - 0.5f)
|
||||
} else {
|
||||
size.width - avatarSizePx * (overlapRatio - 0.5f)
|
||||
}
|
||||
drawCircle(
|
||||
color = Color.Black,
|
||||
center = Offset(
|
||||
|
|
@ -104,6 +115,17 @@ internal fun AvatarRowPreview(@PreviewParameter(OverlapRatioProvider::class) ove
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@PreviewsDayNight
|
||||
internal fun AvatarRowLastOnTopPreview(@PreviewParameter(OverlapRatioProvider::class) overlapRatio: Float) {
|
||||
ElementPreview {
|
||||
ContentToPreview(
|
||||
overlapRatio = overlapRatio,
|
||||
lastOnTop = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@PreviewsDayNight
|
||||
internal fun AvatarRowRtlPreview(@PreviewParameter(OverlapRatioProvider::class) overlapRatio: Float) {
|
||||
|
|
@ -117,7 +139,25 @@ internal fun AvatarRowRtlPreview(@PreviewParameter(OverlapRatioProvider::class)
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun ContentToPreview(overlapRatio: Float) {
|
||||
@PreviewsDayNight
|
||||
internal fun AvatarRowLastOnTopRtlPreview(@PreviewParameter(OverlapRatioProvider::class) overlapRatio: Float) {
|
||||
CompositionLocalProvider(
|
||||
LocalLayoutDirection provides LayoutDirection.Rtl,
|
||||
) {
|
||||
ElementPreview {
|
||||
ContentToPreview(
|
||||
overlapRatio = overlapRatio,
|
||||
lastOnTop = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ContentToPreview(
|
||||
overlapRatio: Float,
|
||||
lastOnTop: Boolean = false,
|
||||
) {
|
||||
AvatarRow(
|
||||
avatarDataList = listOf("A", "B", "C").map {
|
||||
AvatarData(
|
||||
|
|
@ -128,5 +168,6 @@ private fun ContentToPreview(overlapRatio: Float) {
|
|||
}.toImmutableList(),
|
||||
avatarType = AvatarType.User,
|
||||
overlapRatio = overlapRatio,
|
||||
lastOnTop = lastOnTop,
|
||||
)
|
||||
}
|
||||
|
|
@ -63,4 +63,7 @@ enum class AvatarSize(val dp: Dp) {
|
|||
DmCreationConfirmation(64.dp),
|
||||
|
||||
UserVerification(52.dp),
|
||||
|
||||
OrganizationHeader(64.dp),
|
||||
SpaceMember(24.dp),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.knockrequests.impl.banner
|
||||
package io.element.android.libraries.designsystem.components.avatar.internal
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* 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.matrix.ui.components
|
||||
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.drawWithContent
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.BlendMode
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.CompositingStrategy
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.clearAndSetSemantics
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.onClick
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
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.AvatarData
|
||||
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.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.text.toPx
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.Surface
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
/**
|
||||
* Ref: https://www.figma.com/design/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?node-id=3643-2678&m=dev
|
||||
*/
|
||||
@Composable
|
||||
fun EditableOrgAvatar(
|
||||
avatarData: AvatarData,
|
||||
onEdit: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val actionEdit = stringResource(id = CommonStrings.action_edit)
|
||||
val description = stringResource(CommonStrings.a11y_avatar)
|
||||
Box(
|
||||
modifier = modifier
|
||||
.width(avatarData.size.dp + 16.dp)
|
||||
.clearAndSetSemantics {
|
||||
contentDescription = description
|
||||
// Note: this does not set the click effect to the whole Box
|
||||
// when talkback is not enabled
|
||||
onClick(
|
||||
label = actionEdit,
|
||||
action = {
|
||||
onEdit()
|
||||
true
|
||||
}
|
||||
)
|
||||
}
|
||||
) {
|
||||
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
|
||||
val editIconRadius = 17.dp.toPx()
|
||||
val editIconXOffset = 7.dp.toPx()
|
||||
val editIconYOffset = 15.dp.toPx()
|
||||
Avatar(
|
||||
avatarData = avatarData,
|
||||
avatarType = AvatarType.Space(false),
|
||||
modifier = Modifier
|
||||
.align(Alignment.Center)
|
||||
.graphicsLayer {
|
||||
compositingStrategy = CompositingStrategy.Offscreen
|
||||
}
|
||||
.drawWithContent {
|
||||
drawContent()
|
||||
val xOffset = if (isRtl) {
|
||||
editIconXOffset
|
||||
} else {
|
||||
size.width - editIconXOffset
|
||||
}
|
||||
drawCircle(
|
||||
color = Color.Black,
|
||||
center = Offset(
|
||||
x = xOffset,
|
||||
y = size.height - editIconYOffset,
|
||||
),
|
||||
radius = editIconRadius,
|
||||
blendMode = BlendMode.Clear,
|
||||
)
|
||||
},
|
||||
)
|
||||
Surface(
|
||||
color = ElementTheme.colors.bgCanvasDefault,
|
||||
shape = CircleShape,
|
||||
border = BorderStroke(1.dp, color = ElementTheme.colors.borderInteractiveSecondary),
|
||||
modifier = Modifier
|
||||
.clip(CircleShape)
|
||||
.size(30.dp)
|
||||
.align(Alignment.BottomEnd)
|
||||
.clickable(
|
||||
indication = ripple(),
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = onEdit,
|
||||
),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.Edit(),
|
||||
// Note: keep the context description for the test
|
||||
contentDescription = stringResource(id = CommonStrings.action_edit),
|
||||
tint = ElementTheme.colors.iconPrimary,
|
||||
modifier = Modifier.padding(6.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun EditableOrgAvatarPreview() = ElementPreview {
|
||||
EditableOrgAvatar(
|
||||
avatarData = anAvatarData(
|
||||
url = "anUrl",
|
||||
size = AvatarSize.OrganizationHeader,
|
||||
),
|
||||
onEdit = {},
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun EditableOrgAvatarRtlPreview() = CompositionLocalProvider(
|
||||
LocalLayoutDirection provides LayoutDirection.Rtl,
|
||||
) {
|
||||
ElementPreview {
|
||||
EditableOrgAvatar(
|
||||
avatarData = anAvatarData(
|
||||
url = "anUrl",
|
||||
size = AvatarSize.OrganizationHeader,
|
||||
),
|
||||
onEdit = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* 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.matrix.ui.components
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
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.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.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.ui.strings.CommonPlurals
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
/**
|
||||
* Ref: https://www.figma.com/design/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?node-id=3643-2048&m=dev
|
||||
*/
|
||||
@Composable
|
||||
fun OrganizationHeader(
|
||||
avatarData: AvatarData,
|
||||
name: String,
|
||||
numberOfSpaces: Int,
|
||||
numberOfRooms: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 16.dp, bottom = 24.dp, start = 16.dp, end = 16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Avatar(
|
||||
avatarData = avatarData,
|
||||
avatarType = AvatarType.Space(false),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Text(
|
||||
text = name,
|
||||
style = ElementTheme.typography.fontHeadingLgBold,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
val subtitle = stringResource(
|
||||
id = CommonStrings.screen_space_list_details,
|
||||
pluralStringResource(CommonPlurals.common_spaces, numberOfSpaces, numberOfSpaces),
|
||||
pluralStringResource(CommonPlurals.common_rooms, numberOfRooms, numberOfRooms),
|
||||
)
|
||||
Text(
|
||||
text = subtitle,
|
||||
style = ElementTheme.typography.fontBodyLgRegular,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun OrganizationHeaderPreview() = ElementPreview {
|
||||
OrganizationHeader(
|
||||
avatarData = anAvatarData(
|
||||
url = "anUrl",
|
||||
size = AvatarSize.OrganizationHeader,
|
||||
),
|
||||
name = "Space name",
|
||||
numberOfSpaces = 9,
|
||||
numberOfRooms = 88,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* 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.matrix.ui.components
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.libraries.designsystem.atomic.molecules.MembersCountMolecule
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarRow
|
||||
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.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.utils.CommonDrawables
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.ui.model.getAvatarData
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
/**
|
||||
* Ref: https://www.figma.com/design/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?node-id=3729-605&m=dev
|
||||
*/
|
||||
@Composable
|
||||
fun SpaceMembersView(
|
||||
heroes: ImmutableList<MatrixUser>,
|
||||
numberOfMembers: Long,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (heroes.isEmpty()) {
|
||||
MembersCountMolecule(
|
||||
memberCount = numberOfMembers,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
SpaceMembersWithAvatar(
|
||||
heroes = heroes
|
||||
.take(3)
|
||||
.map {
|
||||
it.getAvatarData(AvatarSize.SpaceMember)
|
||||
}
|
||||
.toImmutableList(),
|
||||
numberOfMembers = numberOfMembers,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SpaceMembersWithAvatar(
|
||||
heroes: ImmutableList<AvatarData>,
|
||||
numberOfMembers: Long,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
AvatarRow(
|
||||
avatarDataList = heroes,
|
||||
avatarType = AvatarType.User,
|
||||
lastOnTop = true,
|
||||
)
|
||||
Text(
|
||||
text = "$numberOfMembers",
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@PreviewsDayNight
|
||||
internal fun SpaceMembersViewNoHeroesPreview() = ElementPreview {
|
||||
SpaceMembersView(
|
||||
heroes = persistentListOf(),
|
||||
numberOfMembers = 123,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@PreviewsDayNight
|
||||
internal fun SpaceMembersViewPreview() = ElementPreview(
|
||||
drawableFallbackForImages = CommonDrawables.sample_avatar,
|
||||
) {
|
||||
SpaceMembersView(
|
||||
heroes = persistentListOf(
|
||||
aMatrixUser(id = "@1:d", displayName = "Alice", avatarUrl = "aUrl"),
|
||||
aMatrixUser(id = "@2:d", displayName = "Bob"),
|
||||
aMatrixUser(id = "@3:d", displayName = "Charlie", avatarUrl = "aUrl"),
|
||||
aMatrixUser(id = "@4:d", displayName = "Dave"),
|
||||
),
|
||||
numberOfMembers = 123,
|
||||
)
|
||||
}
|
||||
|
|
@ -98,6 +98,7 @@ class KonsistPreviewTest {
|
|||
"SasEmojisPreview",
|
||||
"SecureBackupSetupViewChangePreview",
|
||||
"SelectedUserCannotRemovePreview",
|
||||
"SpaceMembersViewNoHeroesPreview",
|
||||
"TextComposerAddCaptionPreview",
|
||||
"TextComposerCaptionPreview",
|
||||
"TextComposerEditCaptionPreview",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d510dce684941c35ef1b6ea670156e2e5d5ed0eee3577c433e02ca073e0e84c5
|
||||
size 8615
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:577579f2ed5d7ffb0d289f91d98b13b1df2f9846fefd9c494522809b18de1854
|
||||
size 8225
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f053948660fad8b021a4b22d9cd234cde1cce72c3165c812000bd97b0c2da440
|
||||
size 7156
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:66c4f53b8a2b81285243a162c56521d2cb011ece4e725b9c2152d498b9f5a382
|
||||
size 5563
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:400a14cd9e24e55351cace9c44432d94d5a81b827a19dff8e61154193e13393a
|
||||
size 8879
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:31a781b781a1dccbf15012c2171f0467e54675c3b6c79740b63b9dc878324754
|
||||
size 8500
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:65cd68f9efcac21733ad327afa3308c09b8e1eece9c0d4323e7c7261a4cd3370
|
||||
size 7502
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a9f18b956d5280c31a4fae4754ddd8c2300e37580dd477b06ed043e198b08fa3
|
||||
size 5655
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:cf2ad9276a0aed1b9a97067a4ce60938b0fd556050db36cf28631ce0d69a8c36
|
||||
size 8588
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:223be8c6d34b8a19758575c7798ca6d925efb391c1ceabd0a1ed4c472364dcbb
|
||||
size 7823
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b6e439c9cd3b611cb8beac629d4d6e4da8927d05199aefec9eabe3e77ee67fe4
|
||||
size 7187
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:66c4f53b8a2b81285243a162c56521d2cb011ece4e725b9c2152d498b9f5a382
|
||||
size 5563
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:fb3cec5cf746f7faf350c3bc8cb919e5ba0e207ff87959e817e86d82e8531d72
|
||||
size 8831
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:489cf3807e915dff5ff19458166ab12379002edc42f745e28ae40d151316ed72
|
||||
size 8143
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:dc8bd17e8101352a0bdf542b1fe2ef0de85aa44f78afeb582d7ee18af4d9af75
|
||||
size 7491
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a9f18b956d5280c31a4fae4754ddd8c2300e37580dd477b06ed043e198b08fa3
|
||||
size 5655
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:781862888b73f9bb0948758783a3987d2e04ac2e0690996cdc913b09e4d7283c
|
||||
size 8645
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:21edc3161193318e2b1ca0a6b1136b76616011b3eb3bf0a3308f81b9c9038303
|
||||
size 8957
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7dc701122587804c0e8fe5e238dbfdddd0c93cb3a15c50eab2b0439a8b0a8f86
|
||||
size 8638
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:25262fd5004a941229d6404d73cbacb8c10b4c009b519acb07584eada553bf22
|
||||
size 8946
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:90982bf0b723b89c9b070c1dc94eb5a9fcf66623c382d496174f026e053929fb
|
||||
size 19493
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9640e8e6d758a03f995c7fbbb6c3c123109594138a7cc53d5416ef5e3e157693
|
||||
size 18006
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:fbe699fb17947981c53d47a9570da71e2388ec493a3a9471aaa13a5405075069
|
||||
size 23421
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ce1aba18ea8a6b45d9c40de9af961952e261c7c6200ab13cdfa67906d995208b
|
||||
size 14888
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6a4d295ae71ba2709845312b84983b79348069ded46b30091b9fa769a587cbb7
|
||||
size 14337
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:da8f54591a52475c6f47fa083a1bb397e1164dd3e735562388fde4ff45644150
|
||||
size 16275
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2b9bd735000eda1054294863bdff79d32b391e1511f025bdc05ba4374be3f27c
|
||||
size 31769
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6e9f6f4fe633ca50c2a27cc2e2a0157880f698f91cf759a35fa3f4d49b805905
|
||||
size 31332
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9a7773c27cc488967a4c8c526f4d19ca3a78e15c1efa2f7267881c2e9798d5c4
|
||||
size 31223
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3f837b094f91617dba15cbaab3336aa501b0a834299a07fc6673c9b6b89b8905
|
||||
size 30702
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:862a2a93700999b27dbdf461e70d2e97441550795d03ccb3e4ec4f964d3d6d99
|
||||
size 41995
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:33f802e03011231b8ef81574b12e9622cf9d3901e3faf0225b8e745e88f50623
|
||||
size 41324
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9df673a4073b6806332b3181987f7ff0594eca818ef4a2b760136582d16de476
|
||||
size 6191
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c6a4bf38d356974df822f69f6de85b5b83b4622395c98b1c72d2d352b4e820ce
|
||||
size 6093
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0df7c79b75dabc28fbae243d83547bb3786cb36eca0d33cabc38db0c40ffeb90
|
||||
size 6993
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f1c7cd3392fa7550edf07859f3e5c9a8e69c3e20ea784ac5acffcee5876a4dca
|
||||
size 7312
|
||||
Loading…
Add table
Add a link
Reference in a new issue