From aeeaa48df7d99131e2c318aa15a79ce9328bcfc1 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 15 Jan 2026 17:37:34 +0100 Subject: [PATCH] Replace SpaceState.currentSpace with spaceInfo (RoomInfo) --- .../space/impl/root/SpacePresenter.kt | 5 +- .../features/space/impl/root/SpaceState.kt | 6 +- .../space/impl/root/SpaceStateProvider.kt | 64 +++++++++++++----- .../features/space/impl/root/SpaceView.kt | 66 +++++++++---------- .../space/impl/root/SpacePresenterTest.kt | 20 +----- .../features/space/impl/root/SpaceViewTest.kt | 3 +- 6 files changed, 86 insertions(+), 78 deletions(-) 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 9ca68e866f..60db576df8 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 @@ -53,7 +53,6 @@ import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch -import kotlin.jvm.optionals.getOrNull @Inject class SpacePresenter( @@ -103,7 +102,6 @@ class SpacePresenter( val canAccessSpaceSettings by remember { derivedStateOf { isSpaceSettingsEnabled && permissions.settingsPermissions.hasAny(roomInfo.joinRule) } } - val currentSpace by spaceRoomList.currentSpaceFlow.collectAsState() val (joinActions, setJoinActions) = remember { mutableStateOf(emptyMap>()) } var topicViewerState: TopicViewerState by remember { mutableStateOf(TopicViewerState.Hidden) } @@ -216,8 +214,7 @@ class SpacePresenter( } } return SpaceState( - currentSpaceId = spaceRoomList.roomId, - currentSpace = currentSpace.getOrNull(), + spaceInfo = roomInfo, children = filteredChildren, seenSpaceInvites = seenSpaceInvites, hideInvitesAvatar = hideInvitesAvatar, 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 df384e68f2..a669c294b5 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 @@ -12,14 +12,14 @@ 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 +import io.element.android.libraries.matrix.api.room.RoomInfo import io.element.android.libraries.matrix.api.spaces.SpaceRoom import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableMap import kotlinx.collections.immutable.ImmutableSet data class SpaceState( - private val currentSpaceId: RoomId, - val currentSpace: SpaceRoom?, + val spaceInfo: RoomInfo, val children: ImmutableList, val seenSpaceInvites: ImmutableSet, val hideInvitesAvatar: Boolean, @@ -40,8 +40,6 @@ data class SpaceState( it is AsyncAction.Failure } - val currentSpaceDisplayName = currentSpace?.displayName ?: currentSpaceId.value - val showManageRoomsAction: Boolean = canEditSpaceGraph && children.any { spaceRoom -> !spaceRoom.isSpace } val selectedCount: Int = selectedRoomIds.size val isRemoveButtonEnabled: Boolean = selectedRoomIds.isNotEmpty() 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 90095e9436..9641e17625 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 @@ -15,6 +15,8 @@ import io.element.android.features.invite.api.acceptdecline.anAcceptDeclineInvit import io.element.android.libraries.architecture.AsyncAction 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.RoomInfo +import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility import io.element.android.libraries.matrix.api.room.join.JoinRule import io.element.android.libraries.matrix.api.spaces.SpaceRoom import io.element.android.libraries.previewutils.room.aSpaceRoom @@ -27,11 +29,11 @@ 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(spaceInfo = aSpaceInfo(joinRule = JoinRule.Public)), + aSpaceState(spaceInfo = aSpaceInfo(joinRule = JoinRule.Restricted(persistentListOf()))), aSpaceState(children = aListOfSpaceRooms()), aSpaceState( - parentSpace = aParentSpace(), + spaceInfo = aSpaceInfo(), children = aListOfSpaceRooms(), joiningRooms = setOf(RoomId("!spaceId0:example.com")), hasMoreToLoad = false @@ -41,19 +43,19 @@ open class SpaceStateProvider : PreviewParameterProvider { ), // Manage mode states aSpaceState( - parentSpace = aParentSpace(), + spaceInfo = aSpaceInfo(), children = aListOfSpaceRooms(), isManageMode = true, selectedRoomIds = emptySet(), ), aSpaceState( - parentSpace = aParentSpace(), + spaceInfo = aSpaceInfo(), children = aListOfSpaceRooms(), isManageMode = true, selectedRoomIds = setOf(RoomId("!spaceId0:example.com"), RoomId("!spaceId1:example.com")), ), aSpaceState( - parentSpace = aParentSpace(), + spaceInfo = aSpaceInfo(), children = aListOfSpaceRooms(), isManageMode = true, selectedRoomIds = setOf(RoomId("!spaceId0:example.com")), @@ -63,7 +65,7 @@ open class SpaceStateProvider : PreviewParameterProvider { } fun aSpaceState( - parentSpace: SpaceRoom = aParentSpace(), + spaceInfo: RoomInfo = aSpaceInfo(), children: List = emptyList(), seenSpaceInvites: Set = emptySet(), joiningRooms: Set = emptySet(), @@ -79,8 +81,7 @@ fun aSpaceState( removeRoomsAction: AsyncAction = AsyncAction.Uninitialized, eventSink: (SpaceEvents) -> Unit = { }, ) = SpaceState( - currentSpaceId = parentSpace.roomId, - currentSpace = parentSpace, + spaceInfo = spaceInfo, children = children.toImmutableList(), seenSpaceInvites = seenSpaceInvites.toImmutableSet(), hideInvitesAvatar = hideInvitesAvatar, @@ -96,16 +97,45 @@ fun aSpaceState( eventSink = eventSink, ) -private fun aParentSpace( +private fun aSpaceInfo( joinRule: JoinRule? = null, -): SpaceRoom { - return aSpaceRoom( - numJoinedMembers = 5, - childrenCount = 10, - worldReadable = true, - joinRule = joinRule, - roomId = RoomId("!spaceId0:example.com"), +): RoomInfo { + return RoomInfo( + id = RoomId("!spaceId0:example.com"), + name = "A Space", + rawName = "A Space", topic = "Space description goes here. " + LoremIpsum(20).values.first(), + avatarUrl = null, + isPublic = true, + isDirect = false, + isEncrypted = false, + joinRule = joinRule, + isSpace = true, + isFavorite = false, + canonicalAlias = null, + alternativeAliases = persistentListOf(), + currentUserMembership = CurrentUserMembership.JOINED, + inviter = null, + activeMembersCount = 5, + invitedMembersCount = 0, + joinedMembersCount = 5, + roomPowerLevels = null, + highlightCount = 0, + notificationCount = 0, + userDefinedNotificationMode = null, + hasRoomCall = false, + activeRoomCallParticipants = persistentListOf(), + isMarkedUnread = false, + numUnreadMessages = 0, + numUnreadNotifications = 0, + numUnreadMentions = 0, + heroes = persistentListOf(), + pinnedEventIds = persistentListOf(), + creators = persistentListOf(), + historyVisibility = RoomHistoryVisibility.Joined, + successorRoom = null, + roomVersion = "11", + privilegedCreatorRole = false, ) } 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 5dcb57dfdb..0887e69e7f 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 @@ -77,7 +77,9 @@ import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TextButton import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.matrix.api.room.CurrentUserMembership +import io.element.android.libraries.matrix.api.room.RoomInfo import io.element.android.libraries.matrix.api.spaces.SpaceRoom +import io.element.android.libraries.matrix.api.spaces.SpaceRoomVisibility import io.element.android.libraries.matrix.ui.components.JoinButton import io.element.android.libraries.matrix.ui.components.SpaceHeaderView import io.element.android.libraries.matrix.ui.components.SpaceRoomItemView @@ -131,7 +133,7 @@ fun SpaceView( exit = fadeOut() ) { SpaceViewTopBar( - currentSpace = state.currentSpace, + spaceInfo = state.spaceInfo, canAccessSpaceSettings = state.canAccessSpaceSettings, showManageRoomsAction = state.showManageRoomsAction, onBackClick = onBackClick, @@ -166,7 +168,7 @@ fun SpaceView( eventSink = state.eventSink ) RemoveRoomsActionView( - spaceDisplayName = state.currentSpaceDisplayName, + spaceDisplayName = state.spaceInfo.name ?: state.spaceInfo.id.value, removeRoomsAction = state.removeRoomsAction, selectedCount = state.selectedCount, onConfirm = { state.eventSink(SpaceEvents.ConfirmRoomRemoval) }, @@ -235,27 +237,25 @@ private fun SpaceViewContent( modifier: Modifier = Modifier, ) { LazyColumn(modifier.fillMaxSize()) { - val currentSpace = state.currentSpace - if (currentSpace != null) { - item(key = "space_header") { - AnimatedVisibility( - !state.isManageMode, - enter = fadeIn() + expandVertically(), - exit = fadeOut() + shrinkVertically() - ) { - Column { - SpaceHeaderView( - 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 - ) - HorizontalDivider() - } + val spaceInfo = state.spaceInfo + item(key = "space_header") { + AnimatedVisibility( + !state.isManageMode, + enter = fadeIn() + expandVertically(), + exit = fadeOut() + shrinkVertically() + ) { + Column { + SpaceHeaderView( + avatarData = spaceInfo.getAvatarData(AvatarSize.SpaceHeader), + name = spaceInfo.name, + topic = spaceInfo.topic, + topicMaxLines = 2, + visibility = SpaceRoomVisibility.fromJoinRule(spaceInfo.joinRule), + heroes = spaceInfo.heroes, + numberOfMembers = spaceInfo.joinedMembersCount.toInt(), + onTopicClick = onTopicClick + ) + HorizontalDivider() } } } @@ -337,7 +337,7 @@ private fun LoadingMoreIndicator( @OptIn(ExperimentalMaterial3Api::class) @Composable private fun SpaceViewTopBar( - currentSpace: SpaceRoom?, + spaceInfo: RoomInfo, canAccessSpaceSettings: Boolean, showManageRoomsAction: Boolean, onBackClick: () -> Unit, @@ -354,16 +354,14 @@ private fun SpaceViewTopBar( BackButton(onClick = onBackClick) }, title = { - if (currentSpace != null) { - val roundedCornerShape = RoundedCornerShape(8.dp) - SpaceAvatarAndNameRow( - name = currentSpace.displayName, - avatarData = currentSpace.getAvatarData(AvatarSize.TimelineRoom), - modifier = Modifier - .clip(roundedCornerShape) - .clickable(enabled = canAccessSpaceSettings, onClick = onSettingsClick) - ) - } + val roundedCornerShape = RoundedCornerShape(8.dp) + SpaceAvatarAndNameRow( + name = spaceInfo.name, + avatarData = spaceInfo.getAvatarData(AvatarSize.TimelineRoom), + modifier = Modifier + .clip(roundedCornerShape) + .clickable(enabled = canAccessSpaceSettings, onClick = onSettingsClick) + ) }, actions = { var showMenu by remember { mutableStateOf(false) } 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 08fb977d1b..f5d9f96e27 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 @@ -36,6 +36,7 @@ import io.element.android.libraries.matrix.test.A_ROOM_ID_2 import io.element.android.libraries.matrix.test.A_ROOM_ID_3 import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.room.FakeBaseRoom +import io.element.android.libraries.matrix.test.room.aRoomInfo import io.element.android.libraries.matrix.test.room.join.FakeJoinRoom import io.element.android.libraries.matrix.test.room.powerlevels.FakeRoomPermissions import io.element.android.libraries.matrix.test.spaces.FakeSpaceRoomList @@ -63,7 +64,7 @@ class SpacePresenterTest { val presenter = createSpacePresenter(spaceRoomList = spaceRoomList) presenter.test { val state = awaitItem() - assertThat(state.currentSpace).isNull() + assertThat(state.spaceInfo).isNotNull() assertThat(state.children).isEmpty() assertThat(state.seenSpaceInvites).isEmpty() assertThat(state.hideInvitesAvatar).isFalse() @@ -143,23 +144,6 @@ class SpacePresenterTest { } } - @Test - fun `present - current space value`() = runTest { - val paginateResult = lambdaRecorder> { - Result.success(Unit) - } - val spaceRoomList = FakeSpaceRoomList(paginateResult = paginateResult) - val presenter = createSpacePresenter(spaceRoomList = spaceRoomList) - presenter.test { - val state = awaitItem() - advanceUntilIdle() - assertThat(state.currentSpace).isNull() - val aSpace = aSpaceRoom() - spaceRoomList.emitCurrentSpace(aSpace) - assertThat(awaitItem().currentSpace).isEqualTo(aSpace) - } - } - @Test fun `present - children value`() = runTest { val paginateResult = lambdaRecorder> { 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 3ef5151a50..57e14b05e5 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 @@ -22,6 +22,7 @@ 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.matrix.test.room.aRoomInfo import io.element.android.libraries.previewutils.room.aSpaceRoom import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.EnsureNeverCalled @@ -127,7 +128,7 @@ class SpaceViewTest { val eventsRecorder = EventsRecorder() rule.setSpaceView( aSpaceState( - parentSpace = aSpaceRoom(topic = A_ROOM_TOPIC), + spaceInfo = aRoomInfo(topic = A_ROOM_TOPIC), hasMoreToLoad = false, eventSink = eventsRecorder, )