Merge pull request #5431 from element-hq/feature/fga/space_list_join_action

Feature : space list join action
This commit is contained in:
Benoit Marty 2025-10-01 14:53:30 +02:00 committed by GitHub
commit 83f59c2de3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
42 changed files with 829 additions and 80 deletions

View file

@ -37,6 +37,7 @@ dependencies {
implementation(libs.coil.gif)
implementation(libs.coil.network.okhttp)
implementation(libs.jsoup)
implementation(projects.libraries.previewutils)
testCommonDependencies(libs, true)
testImplementation(projects.libraries.matrix.test)

View file

@ -0,0 +1,35 @@
/*
* 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.material3.LocalContentColor
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import io.element.android.compound.theme.ElementTheme
import io.element.android.libraries.designsystem.theme.components.ButtonSize
import io.element.android.libraries.designsystem.theme.components.TextButton
import io.element.android.libraries.ui.strings.CommonStrings
@Composable
fun JoinButton(
showProgress: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
CompositionLocalProvider(LocalContentColor provides ElementTheme.colors.textActionAccent) {
TextButton(
modifier = modifier,
text = stringResource(CommonStrings.action_join),
onClick = onClick,
size = ButtonSize.LargeLowPadding,
showProgress = showProgress,
)
}
}

View file

@ -31,6 +31,7 @@ import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontStyle
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
@ -41,6 +42,8 @@ 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.modifiers.onKeyboardContextMenuAction
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.Text
import io.element.android.libraries.designsystem.theme.unreadIndicator
@ -59,6 +62,8 @@ fun SpaceRoomItemView(
onClick: () -> Unit,
onLongClick: () -> Unit,
modifier: Modifier = Modifier,
trailingAction: @Composable (() -> Unit)? = null,
bottomAction: @Composable (() -> Unit)? = null,
) {
SpaceRoomItemScaffold(
modifier = modifier,
@ -67,6 +72,7 @@ fun SpaceRoomItemView(
hideAvatars = hideAvatars,
onClick = onClick,
onLongClick = onLongClick,
trailingAction = trailingAction,
) {
NameAndIndicatorRow(
isSpace = spaceRoom.isSpace,
@ -79,22 +85,22 @@ fun SpaceRoomItemView(
subtitle = spaceRoom.subtitle()
)
Spacer(modifier = Modifier.height(1.dp))
Text(
modifier = Modifier.weight(1f),
style = ElementTheme.typography.fontBodyMdRegular,
text = spaceRoom.info(),
fontStyle = FontStyle.Italic.takeIf { spaceRoom.name == null },
color = ElementTheme.colors.textSecondary,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
if (spaceRoom.state == CurrentUserMembership.INVITED) {
Spacer(modifier = Modifier.height(12.dp))
InviteButtonsRowMolecule(
onAcceptClick = {},
onDeclineClick = {},
val info = spaceRoom.info()
if (info.isNotBlank()) {
Text(
modifier = Modifier.weight(1f),
style = ElementTheme.typography.fontBodyMdRegular,
text = info,
fontStyle = FontStyle.Italic.takeIf { spaceRoom.name == null },
color = ElementTheme.colors.textSecondary,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
if (bottomAction != null) {
Spacer(modifier = Modifier.height(12.dp))
bottomAction()
}
}
}
@ -166,7 +172,8 @@ private fun SpaceRoomItemScaffold(
onLongClick: () -> Unit,
hideAvatars: Boolean,
modifier: Modifier = Modifier,
content: @Composable ColumnScope.() -> Unit
trailingAction: @Composable (() -> Unit)? = null,
content: @Composable ColumnScope.() -> Unit,
) {
val clickModifier = Modifier
.combinedClickable(
@ -194,6 +201,10 @@ private fun SpaceRoomItemScaffold(
modifier = Modifier.weight(1f),
content = content,
)
if (trailingAction != null) {
Spacer(modifier = Modifier.width(16.dp))
trailingAction()
}
}
}
@ -233,3 +244,32 @@ private fun SpaceRoom.visibilityIcon(): ImageVector? {
CompoundIcons.LockSolid()
}
}
@Composable
@PreviewsDayNight
internal fun SpaceRoomItemViewPreview(@PreviewParameter(SpaceRoomProvider::class) spaceRoom: SpaceRoom) = ElementPreview {
SpaceRoomItemView(
spaceRoom = spaceRoom,
showUnreadIndicator = spaceRoom.state == CurrentUserMembership.INVITED,
hideAvatars = false,
onClick = {},
onLongClick = {},
modifier = Modifier.fillMaxWidth(),
bottomAction = if (spaceRoom.state == CurrentUserMembership.INVITED) {
{ InviteButtonsRowMolecule({}, {}) }
} else {
null
},
trailingAction = when (spaceRoom.state) {
null, CurrentUserMembership.LEFT -> {
{
JoinButton(
showProgress = spaceRoom.state == CurrentUserMembership.LEFT,
onClick = { },
)
}
}
else -> null
}
)
}

View file

@ -0,0 +1,73 @@
/*
* 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.ui.tooling.preview.PreviewParameterProvider
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.RoomType
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
import io.element.android.libraries.previewutils.room.aSpaceRoom
class SpaceRoomProvider : PreviewParameterProvider<SpaceRoom> {
override val values: Sequence<SpaceRoom> = sequenceOf(
aSpaceRoom(
roomType = RoomType.Room,
name = "Room name with topic",
topic = "Room topic that is quite long and might be truncated"
),
aSpaceRoom(
roomType = RoomType.Room,
name = "Room name no topic",
state = CurrentUserMembership.LEFT,
),
aSpaceRoom(
roomType = RoomType.Room,
name = "Room name with topic",
topic = "Room topic that is quite long and might be truncated",
state = CurrentUserMembership.INVITED,
),
aSpaceRoom(
roomType = RoomType.Room,
name = "Room name no topic",
state = CurrentUserMembership.INVITED,
),
aSpaceRoom(
numJoinedMembers = 5,
childrenCount = 10,
worldReadable = true,
roomId = RoomId("!spaceId0:example.com"),
),
aSpaceRoom(
numJoinedMembers = 5,
childrenCount = 10,
worldReadable = true,
avatarUrl = "anUrl",
roomId = RoomId("!spaceId1:example.com"),
state = CurrentUserMembership.LEFT,
),
aSpaceRoom(
name = null,
numJoinedMembers = 5,
childrenCount = 10,
worldReadable = true,
avatarUrl = "anUrl",
roomId = RoomId("!spaceId2:example.com"),
state = CurrentUserMembership.INVITED,
),
aSpaceRoom(
name = null,
numJoinedMembers = 5,
childrenCount = 10,
worldReadable = true,
avatarUrl = "anUrl",
roomId = RoomId("!spaceId3:example.com"),
state = CurrentUserMembership.INVITED,
),
)
}