From 6f4af1dda4656dbef72534e8095354f25483419e Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 8 Oct 2025 21:59:12 +0200 Subject: [PATCH 1/9] feature(space): make sure to handle topic properly --- .../features/space/impl/root/SpaceEvents.kt | 3 + .../space/impl/root/SpacePresenter.kt | 5 ++ .../features/space/impl/root/SpaceState.kt | 8 +++ .../space/impl/root/SpaceStateProvider.kt | 14 ++++- .../features/space/impl/root/SpaceView.kt | 57 ++++++++++++++++++- .../components/ClickableLinkText.kt | 5 ++ .../matrix/ui/components/SpaceHeaderView.kt | 13 ++++- 7 files changed, 102 insertions(+), 3 deletions(-) diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceEvents.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceEvents.kt index ab94ef719b..8c8eb8dbb7 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceEvents.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceEvents.kt @@ -15,4 +15,7 @@ sealed interface SpaceEvents { data object ClearFailures : SpaceEvents data class AcceptInvite(val spaceRoom: SpaceRoom) : SpaceEvents data class DeclineInvite(val spaceRoom: SpaceRoom) : SpaceEvents + + data class ShowTopicViewer(val topic: String) : SpaceEvents + data object HideTopicViewer : SpaceEvents } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpacePresenter.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpacePresenter.kt index fdd090066e..faa4ca38d4 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpacePresenter.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpacePresenter.kt @@ -80,6 +80,8 @@ class SpacePresenter( val currentSpace by spaceRoomList.currentSpaceFlow.collectAsState() val (joinActions, setJoinActions) = remember { mutableStateOf(emptyMap>()) } + var topicViewerState: TopicViewerState by remember { mutableStateOf(TopicViewerState.Hidden) } + LaunchedEffect(children) { // Remove joined children from the join actions val joinedChildren = children @@ -112,6 +114,8 @@ class SpacePresenter( AcceptDeclineInviteEvents.DeclineInvite(invite = event.spaceRoom.toInviteData(), shouldConfirm = true, blockUser = false) ) } + SpaceEvents.HideTopicViewer -> topicViewerState = TopicViewerState.Hidden + is SpaceEvents.ShowTopicViewer -> topicViewerState = TopicViewerState.Shown(event.topic) } } return SpaceState( @@ -122,6 +126,7 @@ class SpacePresenter( hasMoreToLoad = hasMoreToLoad, joinActions = joinActions.toPersistentMap(), acceptDeclineInviteState = acceptDeclineInviteState, + topicViewerState = topicViewerState, eventSink = ::handleEvents, ) } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceState.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceState.kt index ed6bc3dcf7..2499e4c046 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceState.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceState.kt @@ -7,6 +7,7 @@ package io.element.android.features.space.impl.root +import androidx.compose.runtime.Immutable import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.core.RoomId @@ -23,6 +24,7 @@ data class SpaceState( val hasMoreToLoad: Boolean, val joinActions: ImmutableMap>, val acceptDeclineInviteState: AcceptDeclineInviteState, + val topicViewerState: TopicViewerState, val eventSink: (SpaceEvents) -> Unit ) { fun isJoining(spaceId: RoomId): Boolean = joinActions[spaceId] == AsyncAction.Loading @@ -30,3 +32,9 @@ data class SpaceState( it is AsyncAction.Failure } } + +@Immutable +sealed interface TopicViewerState { + data object Hidden : TopicViewerState + data class Shown(val topic: String) : TopicViewerState +} diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStateProvider.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStateProvider.kt index e58e7e82f4..a9ca00776c 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStateProvider.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStateProvider.kt @@ -51,7 +51,17 @@ open class SpaceStateProvider : PreviewParameterProvider { hasMoreToLoad = false, children = aListOfSpaceRooms(), joiningRooms = setOf(RoomId("!spaceId0:example.com")), - ) + ), + aSpaceState( + hasMoreToLoad = false, + topicViewerState = TopicViewerState.Shown( + topic = "Description of the space goes right here. Lorem ipsum dolor sit amet consectetur. " + + "Leo viverra morbi habitant in. Sem amet enim habitant nibh augue mauris. " + + "Interdum mauris ultrices tincidunt proin morbi erat aenean risus nibh. " + + "Diam amet sit fermentum vulputate faucibus." + ), + children = aListOfSpaceRooms(), + ), // Add other states here ) } @@ -70,6 +80,7 @@ fun aSpaceState( hideInvitesAvatar: Boolean = false, hasMoreToLoad: Boolean = false, acceptDeclineInviteState: AcceptDeclineInviteState = anAcceptDeclineInviteState(), + topicViewerState: TopicViewerState = TopicViewerState.Hidden, eventSink: (SpaceEvents) -> Unit = { }, ) = SpaceState( currentSpace = parentSpace, @@ -79,6 +90,7 @@ fun aSpaceState( hasMoreToLoad = hasMoreToLoad, joinActions = joinActions.toImmutableMap(), acceptDeclineInviteState = acceptDeclineInviteState, + topicViewerState = topicViewerState, eventSink = eventSink, ) diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceView.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceView.kt index ad2a34a848..463d07d907 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceView.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceView.kt @@ -7,13 +7,18 @@ package io.element.android.features.space.impl.root +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -33,6 +38,7 @@ 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.atomic.molecules.InviteButtonsRowMolecule +import io.element.android.libraries.designsystem.components.ClickableLinkText 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 @@ -48,6 +54,7 @@ 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.Icon import io.element.android.libraries.designsystem.theme.components.IconButton +import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar @@ -61,6 +68,7 @@ import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.delay +@OptIn(ExperimentalMaterial3Api::class) @Composable fun SpaceView( state: SpaceState, @@ -87,7 +95,10 @@ fun SpaceView( ) { SpaceViewContent( state = state, - onRoomClick = onRoomClick + onRoomClick = onRoomClick, + onTopicClick = { topic -> + state.eventSink(SpaceEvents.ShowTopicViewer(topic)) + } ) JoinRoomFailureEffect( hasAnyFailure = state.hasAnyFailure, @@ -97,6 +108,14 @@ fun SpaceView( } }, ) + if (state.topicViewerState is TopicViewerState.Shown) { + TopicViewerBottomSheet( + topicViewerState = state.topicViewerState, + onDismiss = { + state.eventSink(SpaceEvents.HideTopicViewer) + } + ) + } } @Composable @@ -120,10 +139,44 @@ private fun JoinRoomFailureEffect( } } +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun TopicViewerBottomSheet( + topicViewerState: TopicViewerState.Shown, + onDismiss: () -> Unit, + modifier: Modifier = Modifier, +) { + ModalBottomSheet( + onDismissRequest = onDismiss, + modifier = modifier, + sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true), + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + ) { + Text( + stringResource(CommonStrings.common_description), + style = ElementTheme.typography.fontBodyLgMedium, + color = ElementTheme.colors.textPrimary, + ) + Spacer(Modifier.height(8.dp)) + ClickableLinkText( + text = topicViewerState.topic, + interactionSource = remember { MutableInteractionSource() }, + style = ElementTheme.typography.fontBodyMdRegular, + color = ElementTheme.colors.textSecondary, + ) + } + } +} + @Composable private fun SpaceViewContent( state: SpaceState, onRoomClick: (spaceRoom: SpaceRoom) -> Unit, + onTopicClick: (String) -> Unit, modifier: Modifier = Modifier, ) { LazyColumn(modifier.fillMaxSize()) { @@ -134,9 +187,11 @@ private fun SpaceViewContent( avatarData = currentSpace.getAvatarData(AvatarSize.SpaceHeader), name = currentSpace.displayName, topic = currentSpace.topic, + topicMaxLines = 2, visibility = currentSpace.visibility, heroes = currentSpace.heroes.toImmutableList(), numberOfMembers = currentSpace.numJoinedMembers, + onTopicClick = onTopicClick ) } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ClickableLinkText.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ClickableLinkText.kt index 3638556da3..6c6d6d398e 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ClickableLinkText.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ClickableLinkText.kt @@ -20,6 +20,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.text.AnnotatedString @@ -51,6 +52,7 @@ fun ClickableLinkText( onClick: () -> Unit = {}, onLongClick: () -> Unit = {}, style: TextStyle = LocalTextStyle.current, + color: Color = Color.Unspecified, inlineContent: ImmutableMap = persistentMapOf(), ) { ClickableLinkText( @@ -62,6 +64,7 @@ fun ClickableLinkText( onClick = onClick, onLongClick = onLongClick, style = style, + color = color, inlineContent = inlineContent, ) } @@ -76,6 +79,7 @@ fun ClickableLinkText( onClick: () -> Unit = {}, onLongClick: () -> Unit = {}, style: TextStyle = LocalTextStyle.current, + color: Color = Color.Unspecified, inlineContent: ImmutableMap = persistentMapOf(), ) { @Suppress("NAME_SHADOWING") @@ -126,6 +130,7 @@ fun ClickableLinkText( text = annotatedString, modifier = modifier.then(pressIndicator), style = style, + color = color, onTextLayout = { layoutResult.value = it }, diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceHeaderView.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceHeaderView.kt index e59049f9f2..e9e151f027 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceHeaderView.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceHeaderView.kt @@ -7,6 +7,7 @@ package io.element.android.libraries.matrix.ui.components +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -43,6 +44,7 @@ fun SpaceHeaderView( numberOfMembers: Int, modifier: Modifier = Modifier, topicMaxLines: Int = Int.MAX_VALUE, + onTopicClick: ((String) -> Unit)? = null, ) { RoomPreviewOrganism( modifier = modifier.padding(24.dp), @@ -68,7 +70,16 @@ fun SpaceHeaderView( description = if (topic.isNullOrBlank()) { null } else { - { RoomPreviewDescriptionAtom(description = topic, maxLines = topicMaxLines) } + { + RoomPreviewDescriptionAtom( + description = topic, + maxLines = topicMaxLines, + modifier = Modifier.clickable( + enabled = onTopicClick != null, + onClick = { onTopicClick?.invoke(topic) } + ) + ) + } }, memberCount = { SpaceMembersView( From cb50262b13cb336fd456fab7e85a28408fee7b41 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 9 Oct 2025 11:25:54 +0200 Subject: [PATCH 2/9] misc(design) : introduce SimpleModalBottomSheet component --- .../components/SimpleModalBottomSheet.kt | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/SimpleModalBottomSheet.kt diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/SimpleModalBottomSheet.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/SimpleModalBottomSheet.kt new file mode 100644 index 0000000000..5b87b1171b --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/SimpleModalBottomSheet.kt @@ -0,0 +1,67 @@ +/* + * 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 + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +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.ExperimentalMaterial3Api +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.datasource.LoremIpsum +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet +import io.element.android.libraries.designsystem.theme.components.Text + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SimpleModalBottomSheet( + title: String, + onDismiss: () -> Unit, + modifier: Modifier = Modifier, + content: @Composable ColumnScope.() -> Unit, +) { + ModalBottomSheet( + onDismissRequest = onDismiss, + modifier = modifier, + sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true), + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + ) { + Text( + title, + style = ElementTheme.typography.fontBodyLgMedium, + color = ElementTheme.colors.textPrimary, + ) + Spacer(Modifier.height(8.dp)) + content() + } + } +} + +@PreviewsDayNight +@Composable +internal fun SimpleModalBottomSheetPreview() = ElementPreview { + SimpleModalBottomSheet(title = "A title", onDismiss = {}) { + Text( + text = LoremIpsum(20).values.first(), + color = ElementTheme.colors.textSecondary, + style = ElementTheme.typography.fontBodyMdRegular, + ) + } +} From 5244dddb73edaea308ceef495e7200c035f228bd Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 9 Oct 2025 11:27:35 +0200 Subject: [PATCH 3/9] feature(space): use the new SimpleModalBottomSheet for TopicViewer --- .../features/space/impl/root/SpaceView.kt | 39 ++++++------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceView.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceView.kt index 463d07d907..46c6a10e57 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceView.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceView.kt @@ -9,16 +9,12 @@ package io.element.android.features.space.impl.root import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -39,6 +35,7 @@ import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.atomic.molecules.InviteButtonsRowMolecule import io.element.android.libraries.designsystem.components.ClickableLinkText +import io.element.android.libraries.designsystem.components.SimpleModalBottomSheet 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 @@ -54,7 +51,6 @@ 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.Icon import io.element.android.libraries.designsystem.theme.components.IconButton -import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar @@ -139,36 +135,23 @@ private fun JoinRoomFailureEffect( } } -@OptIn(ExperimentalMaterial3Api::class) @Composable private fun TopicViewerBottomSheet( topicViewerState: TopicViewerState.Shown, onDismiss: () -> Unit, modifier: Modifier = Modifier, ) { - ModalBottomSheet( - onDismissRequest = onDismiss, - modifier = modifier, - sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true), + SimpleModalBottomSheet( + title = stringResource(CommonStrings.common_description), + onDismiss = onDismiss, + modifier = modifier ) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp), - ) { - Text( - stringResource(CommonStrings.common_description), - style = ElementTheme.typography.fontBodyLgMedium, - color = ElementTheme.colors.textPrimary, - ) - Spacer(Modifier.height(8.dp)) - ClickableLinkText( - text = topicViewerState.topic, - interactionSource = remember { MutableInteractionSource() }, - style = ElementTheme.typography.fontBodyMdRegular, - color = ElementTheme.colors.textSecondary, - ) - } + ClickableLinkText( + text = topicViewerState.topic, + interactionSource = remember { MutableInteractionSource() }, + style = ElementTheme.typography.fontBodyMdRegular, + color = ElementTheme.colors.textSecondary, + ) } } From ff0a07279f139155aace952466923602d213d816 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 9 Oct 2025 11:28:49 +0200 Subject: [PATCH 4/9] feature(space): better previews for Space screen --- .../space/impl/root/SpaceStateProvider.kt | 60 +++++++------------ 1 file changed, 22 insertions(+), 38 deletions(-) diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStateProvider.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStateProvider.kt index a9ca00776c..98a7363abb 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStateProvider.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStateProvider.kt @@ -8,6 +8,7 @@ package io.element.android.features.space.impl.root import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import androidx.compose.ui.tooling.preview.datasource.LoremIpsum import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState import io.element.android.features.invite.api.acceptdecline.anAcceptDeclineInviteState import io.element.android.libraries.architecture.AsyncAction @@ -25,60 +26,30 @@ open class SpaceStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aSpaceState(), + aSpaceState(parentSpace = aParentSpace(joinRule = JoinRule.Public)), + aSpaceState(parentSpace = aParentSpace(joinRule = JoinRule.Restricted(persistentListOf()))), + aSpaceState(children = aListOfSpaceRooms()), aSpaceState( - parentSpace = aSpaceRoom( - joinRule = JoinRule.Public - ) - ), - aSpaceState( - parentSpace = aSpaceRoom( - joinRule = JoinRule.Restricted(persistentListOf()) - ) - ), - aSpaceState( - parentSpace = aSpaceRoom( - numJoinedMembers = 5, - childrenCount = 10, - worldReadable = true, - ), - hasMoreToLoad = true, - ), - aSpaceState( - hasMoreToLoad = true, - children = aListOfSpaceRooms(), - ), - aSpaceState( - hasMoreToLoad = false, + parentSpace = aParentSpace(), children = aListOfSpaceRooms(), joiningRooms = setOf(RoomId("!spaceId0:example.com")), + hasMoreToLoad = false ), aSpaceState( - hasMoreToLoad = false, - topicViewerState = TopicViewerState.Shown( - topic = "Description of the space goes right here. Lorem ipsum dolor sit amet consectetur. " + - "Leo viverra morbi habitant in. Sem amet enim habitant nibh augue mauris. " + - "Interdum mauris ultrices tincidunt proin morbi erat aenean risus nibh. " + - "Diam amet sit fermentum vulputate faucibus." - ), - children = aListOfSpaceRooms(), + topicViewerState = TopicViewerState.Shown(topic = "Space description goes here." + LoremIpsum(20).values.first()), ), // Add other states here ) } fun aSpaceState( - parentSpace: SpaceRoom? = aSpaceRoom( - numJoinedMembers = 5, - childrenCount = 10, - worldReadable = true, - roomId = RoomId("!spaceId0:example.com"), - ), + parentSpace: SpaceRoom? = aParentSpace(), children: List = emptyList(), seenSpaceInvites: Set = emptySet(), joiningRooms: Set = emptySet(), joinActions: Map> = joiningRooms.associateWith { AsyncAction.Loading }, hideInvitesAvatar: Boolean = false, - hasMoreToLoad: Boolean = false, + hasMoreToLoad: Boolean = true, acceptDeclineInviteState: AcceptDeclineInviteState = anAcceptDeclineInviteState(), topicViewerState: TopicViewerState = TopicViewerState.Hidden, eventSink: (SpaceEvents) -> Unit = { }, @@ -94,6 +65,19 @@ fun aSpaceState( eventSink = eventSink, ) +private fun aParentSpace( + joinRule: JoinRule? = null, +): SpaceRoom { + return aSpaceRoom( + numJoinedMembers = 5, + childrenCount = 10, + worldReadable = true, + joinRule = joinRule, + roomId = RoomId("!spaceId0:example.com"), + topic = "Space description goes here. " + LoremIpsum(20).values.first(), + ) +} + private fun aListOfSpaceRooms(): List { return listOf( aSpaceRoom( From b96254429f4f688e749f55cdc6efced71ea843fc Mon Sep 17 00:00:00 2001 From: ElementBot Date: Thu, 9 Oct 2025 09:42:27 +0000 Subject: [PATCH 5/9] Update screenshots --- .../images/features.space.impl.root_SpaceView_Day_0_en.png | 4 ++-- .../images/features.space.impl.root_SpaceView_Day_1_en.png | 4 ++-- .../images/features.space.impl.root_SpaceView_Day_2_en.png | 4 ++-- .../images/features.space.impl.root_SpaceView_Day_3_en.png | 4 ++-- .../images/features.space.impl.root_SpaceView_Day_4_en.png | 4 ++-- .../images/features.space.impl.root_SpaceView_Day_5_en.png | 4 ++-- .../images/features.space.impl.root_SpaceView_Night_0_en.png | 4 ++-- .../images/features.space.impl.root_SpaceView_Night_1_en.png | 4 ++-- .../images/features.space.impl.root_SpaceView_Night_2_en.png | 4 ++-- .../images/features.space.impl.root_SpaceView_Night_3_en.png | 4 ++-- .../images/features.space.impl.root_SpaceView_Night_4_en.png | 4 ++-- .../images/features.space.impl.root_SpaceView_Night_5_en.png | 4 ++-- ...esignsystem.components_SimpleModalBottomSheet_Day_0_en.png | 3 +++ ...ignsystem.components_SimpleModalBottomSheet_Night_0_en.png | 3 +++ 14 files changed, 30 insertions(+), 24 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/libraries.designsystem.components_SimpleModalBottomSheet_Day_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.designsystem.components_SimpleModalBottomSheet_Night_0_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_0_en.png index 65b8867270..79e9d22715 100644 --- a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1251095a4bb43dc672c23b3b3780288f69a4ce26cd5ee249179e94b8a067d3c1 -size 19183 +oid sha256:b9c9e6833668344ddde682f0dde551af1f862647c7a3d749a12813d711ef2ee8 +size 34609 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_1_en.png index 0e81c2bfe6..64fc57b85d 100644 --- a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:546ea17f75c5f80282dfa9a021a68eb71e1436b371ff39b399ebe6f7aa5c5121 -size 19216 +oid sha256:1bdd937cb84e29f1f05c1d718b4350694c65d0c75c0e599effd6467793e331a6 +size 34771 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_2_en.png index a563c82161..fb4dc3941a 100644 --- a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f58e4f6aa72ef41dd5af1e0d309e3909ea1a7d24875135d0d04dd84a3341be8 -size 19572 +oid sha256:9d3ee4db00d7432da4808aeba2cd0a5409d0aefecf66e76366cf60f6096cd443 +size 35073 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_3_en.png index b274825b43..753fffb205 100644 --- a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:79d880a890173c6af166139e29b115d2bf658e75f18868137616af05f4db586d -size 20647 +oid sha256:4b095899fe3ff11e393923a8101c986eb3603778f1a4ded135c3619f6d83771b +size 64666 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_4_en.png index b92c300ce0..fabd2b2255 100644 --- a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:db775b9af1c424150eb5682b3f30df88728a4d28ae598aba1c1a22bf44e5d6b0 -size 52677 +oid sha256:91a3e568c74bd2b7a9d99c481504025290866d0335e38cacbe7340be7a4b61cc +size 65360 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_5_en.png index 4fc3d6bbdd..30517b5dc5 100644 --- a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bd2d09be9476f3ff9cb3710e5bbafc0f493f6afc4f77725cb67e23a7fc6f6e13 -size 51864 +oid sha256:f19d84a37f3c570fb53ae28b185cbe73b4fe94f69ba57e6935ac8a078f20afd2 +size 59751 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_0_en.png index 90951be41f..9a8a9b4d8c 100644 --- a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fb27bf8dbb2323da9c7ff2f3a77ed028e76d5b3ec8e2e95991a12feceb074aae -size 18816 +oid sha256:008f4e11e7bd9cc91e46381cb0509329e2299de22b7b485e3036e458033f9bb9 +size 34036 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_1_en.png index 30050d6876..aedc302e87 100644 --- a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e354f7346a10f75679427774ff96e9b13129b76c8b98944ad4e89b482b110a51 -size 18868 +oid sha256:2270b4da2b1e55027c253422c04f31f6884cf8accf273ce08efba95fec40d599 +size 34177 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_2_en.png index b859185d49..ac12a9caca 100644 --- a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d80cd45594d19ecf6e7bf103bb374dd86013bd05e7c10cd3df31e66c2b2cda01 -size 19223 +oid sha256:39afa43d6b9b8ba7d5ff83ba5a956fdfbb62510c57d34c81d65dd558e0501269 +size 34508 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_3_en.png index a37cc0959d..885b3097ae 100644 --- a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c9f4963aa89b1f9811793f50033b004cb5659f7c91d6f16d5204e932c29b9d0a -size 20271 +oid sha256:e6396f10b0bfb904eec898b0085af56f802c0f5a5e50af36313a31fa20a1dd0c +size 63449 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_4_en.png index 150431b180..92edaf6970 100644 --- a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6701688f5a1b8e59f42bc6e429a561a120576c5d119a3df123d4ee5fc1acb887 -size 51384 +oid sha256:c9425dfd394a554bdbf43c021ec1e26d0e7f273f4ba89df670170c73641b51a7 +size 64049 diff --git a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_5_en.png index 13c3fcb7ce..0807dfaec2 100644 --- a/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.space.impl.root_SpaceView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b51e599bbad12e70b116680b00cdd376c921769dbc7b2f918ba9a0919a88847 -size 50509 +oid sha256:93ed6d05c199a886a53ecffb7a56a586f4fba874fbf18afb4975782eb2d10744 +size 57977 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_SimpleModalBottomSheet_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_SimpleModalBottomSheet_Day_0_en.png new file mode 100644 index 0000000000..22bddb6738 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_SimpleModalBottomSheet_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8d27d23f9e54848b07eab23fb4b3a865a1e4036255c64fcf6cb944466e77fb4b +size 27306 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_SimpleModalBottomSheet_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_SimpleModalBottomSheet_Night_0_en.png new file mode 100644 index 0000000000..42e92893a9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_SimpleModalBottomSheet_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2f2e4d519448d1a5b0c67d7b808292f60ad6391a314b9946cff89aef8fc29313 +size 25914 From 1c256e7166b612094464531df1a861842c3a3176 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 9 Oct 2025 11:51:37 +0200 Subject: [PATCH 6/9] feature(space): add missing tests on SpaceEvents topic --- .../space/impl/root/SpacePresenterTest.kt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpacePresenterTest.kt b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpacePresenterTest.kt index 731fdb85a6..fbef7bb3a1 100644 --- a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpacePresenterTest.kt +++ b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpacePresenterTest.kt @@ -60,6 +60,7 @@ class SpacePresenterTest { assertThat(state.hasMoreToLoad).isTrue() assertThat(state.joinActions).isEmpty() assertThat(state.acceptDeclineInviteState).isEqualTo(anAcceptDeclineInviteState()) + assertThat(state.topicViewerState).isEqualTo(TopicViewerState.Hidden) advanceUntilIdle() paginateResult.assertions().isCalledOnce() } @@ -236,6 +237,24 @@ class SpacePresenterTest { } } + @Test + fun `present - topic viewer state`() = runTest { + val paginateResult = lambdaRecorder> { + Result.success(Unit) + } + val spaceRoomList = FakeSpaceRoomList(paginateResult = paginateResult) + val presenter = createSpacePresenter(spaceRoomList = spaceRoomList) + presenter.test { + val state = awaitItem() + assertThat(state.topicViewerState).isEqualTo(TopicViewerState.Hidden) + advanceUntilIdle() + state.eventSink(SpaceEvents.ShowTopicViewer("topic")) + assertThat(awaitItem().topicViewerState).isEqualTo(TopicViewerState.Shown("topic")) + state.eventSink(SpaceEvents.HideTopicViewer) + assertThat(awaitItem().topicViewerState).isEqualTo(TopicViewerState.Hidden) + } + } + @Test fun `present - accept invite is transmitted to acceptDeclineInviteState`() { `invite action is transmitted to acceptDeclineInviteState`( From 0019cf3318aca92d3d3e5d3d5e09efb0636d1c13 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 9 Oct 2025 13:40:53 +0200 Subject: [PATCH 7/9] Fix tests. --- .../element/android/features/space/impl/root/SpaceViewTest.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpaceViewTest.kt b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpaceViewTest.kt index 3c8d264731..8c6513e5ff 100644 --- a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpaceViewTest.kt +++ b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpaceViewTest.kt @@ -42,6 +42,7 @@ class SpaceViewTest { ensureCalledOnce { rule.setSpaceView( aSpaceState( + hasMoreToLoad = false, eventSink = eventsRecorder, ), onBackClick = it, @@ -58,6 +59,7 @@ class SpaceViewTest { rule.setSpaceView( aSpaceState( children = listOf(aSpaceRoom), + hasMoreToLoad = false, eventSink = eventsRecorder, ), onRoomClick = it, @@ -73,6 +75,7 @@ class SpaceViewTest { rule.setSpaceView( aSpaceState( children = listOf(aSpaceRoom), + hasMoreToLoad = false, eventSink = eventsRecorder, ), ) From b44ca52e2d25f959267c44796fc2dc9cf4228723 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 9 Oct 2025 14:55:27 +0200 Subject: [PATCH 8/9] Fix tests again --- .../element/android/features/space/impl/root/SpaceViewTest.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpaceViewTest.kt b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpaceViewTest.kt index 8c6513e5ff..be1d400497 100644 --- a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpaceViewTest.kt +++ b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpaceViewTest.kt @@ -31,6 +31,7 @@ import org.junit.Rule import org.junit.Test import org.junit.rules.TestRule import org.junit.runner.RunWith +import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) class SpaceViewTest { @@ -83,6 +84,7 @@ class SpaceViewTest { eventsRecorder.assertSingle(SpaceEvents.Join(aSpaceRoom)) } + @Config(qualifiers = "h1024dp") @Test fun `clicking on accept invite emits the expected Event`() { val aSpaceRoom = aSpaceRoom(roomId = A_ROOM_ID, state = CurrentUserMembership.INVITED) @@ -97,6 +99,7 @@ class SpaceViewTest { eventsRecorder.assertSingle(SpaceEvents.AcceptInvite(aSpaceRoom)) } + @Config(qualifiers = "h1024dp") @Test fun `clicking on decline invite emits the expected Event`() { val aSpaceRoom = aSpaceRoom(roomId = A_ROOM_ID, state = CurrentUserMembership.INVITED) From 1c232aa2813bea1e951bea43703f39b780628195 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 9 Oct 2025 15:06:51 +0200 Subject: [PATCH 9/9] feature(space): add SpaceViewTest related to topic --- .../features/space/impl/root/SpaceViewTest.kt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpaceViewTest.kt b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpaceViewTest.kt index be1d400497..2702133780 100644 --- a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpaceViewTest.kt +++ b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpaceViewTest.kt @@ -18,6 +18,7 @@ import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.spaces.SpaceRoom import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_NAME +import io.element.android.libraries.matrix.test.A_ROOM_TOPIC import io.element.android.libraries.previewutils.room.aSpaceRoom import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.EnsureNeverCalled @@ -91,6 +92,7 @@ class SpaceViewTest { val eventsRecorder = EventsRecorder() rule.setSpaceView( aSpaceState( + hasMoreToLoad = false, children = listOf(aSpaceRoom), eventSink = eventsRecorder, ), @@ -106,6 +108,7 @@ class SpaceViewTest { val eventsRecorder = EventsRecorder() rule.setSpaceView( aSpaceState( + hasMoreToLoad = false, children = listOf(aSpaceRoom), eventSink = eventsRecorder, ), @@ -113,6 +116,21 @@ class SpaceViewTest { rule.clickOn(CommonStrings.action_decline) eventsRecorder.assertSingle(SpaceEvents.DeclineInvite(aSpaceRoom)) } + + @Config(qualifiers = "h1024dp") + @Test + fun `clicking on topic emits the expected Event`() { + val eventsRecorder = EventsRecorder() + rule.setSpaceView( + aSpaceState( + parentSpace = aSpaceRoom(topic = A_ROOM_TOPIC), + hasMoreToLoad = false, + eventSink = eventsRecorder, + ) + ) + rule.onNodeWithText(A_ROOM_TOPIC).performClick() + eventsRecorder.assertSingle(SpaceEvents.ShowTopicViewer(A_ROOM_TOPIC)) + } } private fun AndroidComposeTestRule.setSpaceView(