Merge pull request #5185 from element-hq/feature/bma/invitePoepleUi
Iterate on invite people UI
This commit is contained in:
@@ -30,7 +30,6 @@ import io.element.android.libraries.designsystem.components.async.AsyncLoading
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
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.SearchBar
|
||||
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
@@ -192,10 +191,6 @@ private fun InvitePeopleSearchBar(
|
||||
onCheckedChange = { onToggleUser(invitableUser.matrixUser) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
if (index < results.lastIndex) {
|
||||
HorizontalDivider()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.atomic.atoms
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.selection.toggleable
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.semantics.Role
|
||||
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.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
|
||||
@Composable
|
||||
fun SelectedIndicatorAtom(
|
||||
checked: Boolean,
|
||||
enabled: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (checked) {
|
||||
Icon(
|
||||
modifier = modifier.toggleable(
|
||||
value = true,
|
||||
role = Role.Companion.Checkbox,
|
||||
enabled = enabled,
|
||||
onValueChange = {},
|
||||
),
|
||||
imageVector = CompoundIcons.CheckCircleSolid(),
|
||||
contentDescription = null,
|
||||
tint = if (enabled) {
|
||||
ElementTheme.colors.iconAccentPrimary
|
||||
} else {
|
||||
ElementTheme.colors.iconDisabled
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Box(modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@PreviewsDayNight
|
||||
internal fun SelectedIndicatorAtomPreview() = ElementPreview {
|
||||
Column(
|
||||
modifier = Modifier.padding(8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
SelectedIndicatorAtom(
|
||||
modifier = Modifier.size(24.dp),
|
||||
checked = false,
|
||||
enabled = false,
|
||||
)
|
||||
SelectedIndicatorAtom(
|
||||
modifier = Modifier.size(24.dp),
|
||||
checked = true,
|
||||
enabled = false,
|
||||
)
|
||||
SelectedIndicatorAtom(
|
||||
modifier = Modifier.size(24.dp),
|
||||
checked = false,
|
||||
enabled = true,
|
||||
)
|
||||
SelectedIndicatorAtom(
|
||||
modifier = Modifier.size(24.dp),
|
||||
checked = true,
|
||||
enabled = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ enum class AvatarSize(val dp: Dp) {
|
||||
UserHeader(96.dp),
|
||||
UserListItem(36.dp),
|
||||
|
||||
SelectedUser(56.dp),
|
||||
SelectedUser(52.dp),
|
||||
SelectedRoom(56.dp),
|
||||
|
||||
DmCluster(75.dp),
|
||||
|
||||
@@ -19,10 +19,10 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.libraries.designsystem.atomic.atoms.SelectedIndicatorAtom
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
||||
import io.element.android.libraries.designsystem.theme.components.Checkbox
|
||||
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
|
||||
import io.element.android.libraries.matrix.ui.model.getAvatarData
|
||||
|
||||
@@ -50,6 +50,7 @@ fun CheckableUserRow(
|
||||
avatarData = data.avatarData,
|
||||
name = data.name,
|
||||
subtext = data.subtext,
|
||||
enabled = enabled,
|
||||
)
|
||||
}
|
||||
is CheckableUserRowData.Unresolved -> {
|
||||
@@ -57,14 +58,13 @@ fun CheckableUserRow(
|
||||
modifier = rowModifier,
|
||||
avatarData = data.avatarData,
|
||||
id = data.id,
|
||||
enabled = enabled,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Checkbox(
|
||||
modifier = Modifier.padding(end = 4.dp),
|
||||
SelectedIndicatorAtom(
|
||||
modifier = Modifier.padding(end = 24.dp),
|
||||
checked = checked,
|
||||
onCheckedChange = null,
|
||||
enabled = enabled,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,24 @@ open class MatrixUserProvider : PreviewParameterProvider<MatrixUser> {
|
||||
override val values: Sequence<MatrixUser>
|
||||
get() = sequenceOf(
|
||||
aMatrixUser(),
|
||||
aMatrixUser().copy(displayName = null),
|
||||
aMatrixUser(displayName = null),
|
||||
)
|
||||
}
|
||||
|
||||
open class MatrixUserWithNullProvider : PreviewParameterProvider<MatrixUser?> {
|
||||
override val values: Sequence<MatrixUser?>
|
||||
get() = sequenceOf(
|
||||
aMatrixUser(),
|
||||
aMatrixUser(displayName = null),
|
||||
null,
|
||||
)
|
||||
}
|
||||
|
||||
open class MatrixUserWithAvatarProvider : PreviewParameterProvider<MatrixUser?> {
|
||||
override val values: Sequence<MatrixUser?>
|
||||
get() = sequenceOf(
|
||||
aMatrixUser(displayName = "John Doe"),
|
||||
aMatrixUser(displayName = "John Doe", avatarUrl = "anUrl"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -41,12 +58,3 @@ fun aMatrixUserList() = listOf(
|
||||
aMatrixUser("@victor:server.org", "Victor"),
|
||||
aMatrixUser("@walter:server.org", "Walter"),
|
||||
)
|
||||
|
||||
open class MatrixUserWithNullProvider : PreviewParameterProvider<MatrixUser?> {
|
||||
override val values: Sequence<MatrixUser?>
|
||||
get() = sequenceOf(
|
||||
aMatrixUser(),
|
||||
aMatrixUser().copy(displayName = null),
|
||||
null,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ fun MatrixUserRow(
|
||||
name = matrixUser.getBestName(),
|
||||
subtext = if (matrixUser.displayName.isNullOrEmpty()) null else matrixUser.userId.value,
|
||||
modifier = modifier,
|
||||
trailingContent,
|
||||
trailingContent = trailingContent,
|
||||
)
|
||||
|
||||
@PreviewsDayNight
|
||||
|
||||
@@ -20,7 +20,7 @@ class SelectRoomInfoProvider : PreviewParameterProvider<SelectRoomInfo> {
|
||||
get() = sequenceOf(
|
||||
aSelectRoomInfo(roomId = RoomId("!room1:domain")),
|
||||
aSelectRoomInfo(roomId = RoomId("!room2:domain"), name = "Room with a name"),
|
||||
aSelectRoomInfo(roomId = RoomId("!room3:domain"), name = "Room with a name and alias", canonicalAlias = RoomAlias("#alias:domain")),
|
||||
aSelectRoomInfo(roomId = RoomId("!room3:domain"), name = "Room with a name and avatar", avatarUrl = "anUrl"),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* 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.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
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.MaterialTheme
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
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.clipToBounds
|
||||
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.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
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.AvatarType
|
||||
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.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
fun SelectedItem(
|
||||
avatarData: AvatarData,
|
||||
avatarType: AvatarType,
|
||||
text: String,
|
||||
maxLines: Int,
|
||||
a11yContentDescription: String,
|
||||
canRemove: Boolean,
|
||||
onRemoveClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val actionRemove = stringResource(id = CommonStrings.action_remove)
|
||||
Box(
|
||||
modifier = modifier
|
||||
.width(avatarData.size.dp)
|
||||
.clearAndSetSemantics {
|
||||
contentDescription = a11yContentDescription
|
||||
if (canRemove) {
|
||||
// Note: this does not set the click effect to the whole Box
|
||||
// when talkback is not enabled
|
||||
onClick(
|
||||
label = actionRemove,
|
||||
action = {
|
||||
onRemoveClick()
|
||||
true
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
|
||||
val closeIconRadius = 12.dp.toPx()
|
||||
val closeIconOffset = 10.dp.toPx()
|
||||
Avatar(
|
||||
avatarData = avatarData,
|
||||
avatarType = avatarType,
|
||||
modifier = Modifier
|
||||
.graphicsLayer {
|
||||
compositingStrategy = CompositingStrategy.Offscreen
|
||||
}
|
||||
.drawWithContent {
|
||||
drawContent()
|
||||
if (canRemove) {
|
||||
val xOffset = if (isRtl) {
|
||||
closeIconOffset
|
||||
} else {
|
||||
size.width - closeIconOffset
|
||||
}
|
||||
drawCircle(
|
||||
color = Color.Black,
|
||||
center = Offset(
|
||||
x = xOffset,
|
||||
y = closeIconOffset,
|
||||
),
|
||||
radius = closeIconRadius,
|
||||
blendMode = BlendMode.Clear,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.clipToBounds(),
|
||||
text = text,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = maxLines,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
if (canRemove) {
|
||||
Surface(
|
||||
color = ElementTheme.colors.bgActionPrimaryRest,
|
||||
modifier = Modifier
|
||||
.clip(CircleShape)
|
||||
.size(20.dp)
|
||||
.align(Alignment.TopEnd)
|
||||
.clickable(
|
||||
indication = ripple(),
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = onRemoveClick,
|
||||
),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.Close(),
|
||||
// Note: keep the context description for the test
|
||||
contentDescription = stringResource(id = CommonStrings.action_remove),
|
||||
tint = ElementTheme.colors.iconOnSolidPrimary,
|
||||
modifier = Modifier.padding(2.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,35 +7,17 @@
|
||||
|
||||
package io.element.android.libraries.matrix.ui.components
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
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.MaterialTheme
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
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 androidx.compose.ui.unit.LayoutDirection
|
||||
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.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.Surface
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.matrix.ui.model.SelectRoomInfo
|
||||
import io.element.android.libraries.matrix.ui.model.getAvatarData
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
@@ -47,48 +29,22 @@ fun SelectedRoom(
|
||||
onRemoveRoom: (SelectRoomInfo) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.width(56.dp)
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Avatar(
|
||||
avatarData = roomInfo.getAvatarData(AvatarSize.SelectedRoom),
|
||||
avatarType = AvatarType.Room(
|
||||
heroes = roomInfo.heroes.map { it.getAvatarData(AvatarSize.SelectedRoom) }.toImmutableList(),
|
||||
isTombstoned = roomInfo.isTombstoned,
|
||||
),
|
||||
)
|
||||
Text(
|
||||
// If name is null, we do not have space to render "No room name", so just use `#` here.
|
||||
text = roomInfo.name ?: "#",
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
)
|
||||
}
|
||||
Surface(
|
||||
color = ElementTheme.colors.iconPrimary,
|
||||
modifier = Modifier
|
||||
.clip(CircleShape)
|
||||
.size(20.dp)
|
||||
.align(Alignment.TopEnd)
|
||||
.clickable(
|
||||
indication = ripple(),
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = { onRemoveRoom(roomInfo) }
|
||||
),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.Close(),
|
||||
contentDescription = stringResource(id = CommonStrings.action_remove),
|
||||
tint = ElementTheme.colors.iconOnSolidPrimary,
|
||||
modifier = Modifier.padding(2.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
SelectedItem(
|
||||
avatarData = roomInfo.getAvatarData(AvatarSize.SelectedRoom),
|
||||
avatarType = AvatarType.Room(
|
||||
heroes = roomInfo.heroes.map { it.getAvatarData(AvatarSize.SelectedRoom) }.toImmutableList(),
|
||||
isTombstoned = roomInfo.isTombstoned,
|
||||
),
|
||||
// If name is null, we do not have space to render "No room name", so just use `#` here.
|
||||
text = roomInfo.name ?: "#",
|
||||
maxLines = 1,
|
||||
a11yContentDescription = roomInfo.name
|
||||
?: roomInfo.canonicalAlias?.value
|
||||
?: stringResource(id = CommonStrings.common_room_name),
|
||||
canRemove = true,
|
||||
onRemoveClick = { onRemoveRoom(roomInfo) },
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@@ -101,3 +57,18 @@ internal fun SelectedRoomPreview(
|
||||
onRemoveRoom = {},
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun SelectedRoomRtlPreview(
|
||||
@PreviewParameter(SelectRoomInfoProvider::class) roomInfo: SelectRoomInfo
|
||||
) = CompositionLocalProvider(
|
||||
LocalLayoutDirection provides LayoutDirection.Rtl,
|
||||
) {
|
||||
ElementPreview {
|
||||
SelectedRoom(
|
||||
roomInfo = roomInfo,
|
||||
onRemoveRoom = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,42 +7,19 @@
|
||||
|
||||
package io.element.android.libraries.matrix.ui.components
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
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.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.clipToBounds
|
||||
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.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
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 androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
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.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.Surface
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.ui.model.getAvatarData
|
||||
import io.element.android.libraries.matrix.ui.model.getBestName
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
fun SelectedUser(
|
||||
@@ -51,81 +28,47 @@ fun SelectedUser(
|
||||
onUserRemove: (MatrixUser) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val actionRemove = stringResource(id = CommonStrings.action_remove)
|
||||
Box(
|
||||
modifier = modifier
|
||||
.width(AvatarSize.SelectedUser.dp)
|
||||
.clearAndSetSemantics {
|
||||
contentDescription = matrixUser.getBestName()
|
||||
if (canRemove) {
|
||||
// Note: this does not set the click effect to the whole Box
|
||||
// when talkback is not enabled
|
||||
onClick(
|
||||
label = actionRemove,
|
||||
action = {
|
||||
onUserRemove(matrixUser)
|
||||
true
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Avatar(
|
||||
avatarData = matrixUser.getAvatarData(size = AvatarSize.SelectedUser),
|
||||
avatarType = AvatarType.User,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.clipToBounds(),
|
||||
text = matrixUser.getBestName(),
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 2,
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
if (canRemove) {
|
||||
Surface(
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
modifier = Modifier
|
||||
.clip(CircleShape)
|
||||
.size(20.dp)
|
||||
.align(Alignment.TopEnd)
|
||||
.clickable(
|
||||
indication = ripple(),
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = { onUserRemove(matrixUser) }
|
||||
),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.Close(),
|
||||
// Note: keep the context description for the test
|
||||
contentDescription = stringResource(id = CommonStrings.action_remove),
|
||||
tint = ElementTheme.colors.iconOnSolidPrimary,
|
||||
modifier = Modifier.padding(2.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
SelectedItem(
|
||||
avatarData = matrixUser.getAvatarData(size = AvatarSize.SelectedUser),
|
||||
avatarType = AvatarType.User,
|
||||
text = matrixUser.getBestName(),
|
||||
maxLines = 2,
|
||||
a11yContentDescription = matrixUser.getBestName(),
|
||||
canRemove = canRemove,
|
||||
onRemoveClick = { onUserRemove(matrixUser) },
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun SelectedUserPreview() = ElementPreview {
|
||||
internal fun SelectedUserPreview(@PreviewParameter(MatrixUserWithAvatarProvider::class) user: MatrixUser) = ElementPreview {
|
||||
SelectedUser(
|
||||
aMatrixUser(displayName = "John Doe"),
|
||||
matrixUser = user,
|
||||
canRemove = true,
|
||||
onUserRemove = {},
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun SelectedUserRtlPreview() = CompositionLocalProvider(
|
||||
LocalLayoutDirection provides LayoutDirection.Rtl,
|
||||
) {
|
||||
ElementPreview {
|
||||
SelectedUser(
|
||||
matrixUser = aMatrixUser(displayName = "John Doe"),
|
||||
canRemove = true,
|
||||
onUserRemove = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun SelectedUserCannotRemovePreview() = ElementPreview {
|
||||
SelectedUser(
|
||||
aMatrixUser(),
|
||||
matrixUser = aMatrixUser(),
|
||||
canRemove = false,
|
||||
onUserRemove = {},
|
||||
)
|
||||
|
||||
@@ -39,6 +39,7 @@ fun UnresolvedUserRow(
|
||||
avatarData: AvatarData,
|
||||
id: String,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
@@ -61,7 +62,7 @@ fun UnresolvedUserRow(
|
||||
text = id,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
color = if (enabled) ElementTheme.colors.textPrimary else ElementTheme.colors.textDisabled,
|
||||
style = ElementTheme.typography.fontBodyLgMedium,
|
||||
)
|
||||
|
||||
@@ -78,11 +79,11 @@ fun UnresolvedUserRow(
|
||||
.size(18.dp)
|
||||
.align(Alignment.Top)
|
||||
.padding(2.dp),
|
||||
tint = ElementTheme.colors.iconCriticalPrimary,
|
||||
tint = if (enabled) ElementTheme.colors.iconCriticalPrimary else ElementTheme.colors.iconDisabled,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(CommonStrings.common_invite_unknown_profile),
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
color = if (enabled) ElementTheme.colors.textSecondary else ElementTheme.colors.textDisabled,
|
||||
style = ElementTheme.typography.fontBodySmRegular.copy(lineHeight = 16.sp),
|
||||
)
|
||||
}
|
||||
@@ -94,5 +95,8 @@ fun UnresolvedUserRow(
|
||||
@Composable
|
||||
internal fun UnresolvedUserRowPreview() = ElementThemedPreview {
|
||||
val matrixUser = aMatrixUser()
|
||||
UnresolvedUserRow(matrixUser.getAvatarData(size = AvatarSize.UserListItem), matrixUser.userId.value)
|
||||
Column {
|
||||
UnresolvedUserRow(matrixUser.getAvatarData(size = AvatarSize.UserListItem), matrixUser.userId.value)
|
||||
UnresolvedUserRow(matrixUser.getAvatarData(size = AvatarSize.UserListItem), matrixUser.userId.value, enabled = false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ internal fun UserRow(
|
||||
name: String,
|
||||
subtext: String?,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
trailingContent: @Composable (() -> Unit)? = null,
|
||||
) {
|
||||
Row(
|
||||
@@ -54,14 +55,14 @@ internal fun UserRow(
|
||||
text = name,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
color = if (enabled) ElementTheme.colors.textPrimary else ElementTheme.colors.textDisabled,
|
||||
style = ElementTheme.typography.fontBodyLgRegular,
|
||||
)
|
||||
// Id
|
||||
subtext?.let {
|
||||
Text(
|
||||
text = subtext,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
color = if (enabled) ElementTheme.colors.textSecondary else ElementTheme.colors.textDisabled,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = ElementTheme.typography.fontBodySmRegular,
|
||||
|
||||
@@ -166,10 +166,15 @@ class KonsistPreviewTest {
|
||||
additionalMessage = "Functions for Preview should be named like this: <ViewUnderPreview>Preview. " +
|
||||
"Exception can be added to the test, for multiple Previews of the same view",
|
||||
) {
|
||||
val testedView = it.name.removeSuffix("Preview")
|
||||
it.text.contains("$testedView(") ||
|
||||
it.text.contains("$testedView {") ||
|
||||
it.text.contains("ContentToPreview(")
|
||||
val testedView = if (it.name.endsWith("RtlPreview")) {
|
||||
it.name.removeSuffix("RtlPreview")
|
||||
} else {
|
||||
it.name.removeSuffix("Preview")
|
||||
}
|
||||
it.name.endsWith("Preview") &&
|
||||
(it.text.contains("$testedView(") ||
|
||||
it.text.contains("$testedView {") ||
|
||||
it.text.contains("ContentToPreview("))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user