Replace SpaceState.currentSpace with spaceInfo (RoomInfo)

This commit is contained in:
ganfra
2026-01-15 17:37:34 +01:00
parent b560b0443e
commit aeeaa48df7
6 changed files with 86 additions and 78 deletions

View File

@@ -53,7 +53,6 @@ import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.jvm.optionals.getOrNull
@Inject @Inject
class SpacePresenter( class SpacePresenter(
@@ -103,7 +102,6 @@ class SpacePresenter(
val canAccessSpaceSettings by remember { val canAccessSpaceSettings by remember {
derivedStateOf { isSpaceSettingsEnabled && permissions.settingsPermissions.hasAny(roomInfo.joinRule) } derivedStateOf { isSpaceSettingsEnabled && permissions.settingsPermissions.hasAny(roomInfo.joinRule) }
} }
val currentSpace by spaceRoomList.currentSpaceFlow.collectAsState()
val (joinActions, setJoinActions) = remember { mutableStateOf(emptyMap<RoomId, AsyncAction<Unit>>()) } val (joinActions, setJoinActions) = remember { mutableStateOf(emptyMap<RoomId, AsyncAction<Unit>>()) }
var topicViewerState: TopicViewerState by remember { mutableStateOf(TopicViewerState.Hidden) } var topicViewerState: TopicViewerState by remember { mutableStateOf(TopicViewerState.Hidden) }
@@ -216,8 +214,7 @@ class SpacePresenter(
} }
} }
return SpaceState( return SpaceState(
currentSpaceId = spaceRoomList.roomId, spaceInfo = roomInfo,
currentSpace = currentSpace.getOrNull(),
children = filteredChildren, children = filteredChildren,
seenSpaceInvites = seenSpaceInvites, seenSpaceInvites = seenSpaceInvites,
hideInvitesAvatar = hideInvitesAvatar, hideInvitesAvatar = hideInvitesAvatar,

View File

@@ -12,14 +12,14 @@ import androidx.compose.runtime.Immutable
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState
import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.matrix.api.core.RoomId 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 io.element.android.libraries.matrix.api.spaces.SpaceRoom
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.ImmutableMap import kotlinx.collections.immutable.ImmutableMap
import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.ImmutableSet
data class SpaceState( data class SpaceState(
private val currentSpaceId: RoomId, val spaceInfo: RoomInfo,
val currentSpace: SpaceRoom?,
val children: ImmutableList<SpaceRoom>, val children: ImmutableList<SpaceRoom>,
val seenSpaceInvites: ImmutableSet<RoomId>, val seenSpaceInvites: ImmutableSet<RoomId>,
val hideInvitesAvatar: Boolean, val hideInvitesAvatar: Boolean,
@@ -40,8 +40,6 @@ data class SpaceState(
it is AsyncAction.Failure it is AsyncAction.Failure
} }
val currentSpaceDisplayName = currentSpace?.displayName ?: currentSpaceId.value
val showManageRoomsAction: Boolean = canEditSpaceGraph && children.any { spaceRoom -> !spaceRoom.isSpace } val showManageRoomsAction: Boolean = canEditSpaceGraph && children.any { spaceRoom -> !spaceRoom.isSpace }
val selectedCount: Int = selectedRoomIds.size val selectedCount: Int = selectedRoomIds.size
val isRemoveButtonEnabled: Boolean = selectedRoomIds.isNotEmpty() val isRemoveButtonEnabled: Boolean = selectedRoomIds.isNotEmpty()

View File

@@ -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.architecture.AsyncAction
import io.element.android.libraries.matrix.api.core.RoomId 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.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.room.join.JoinRule
import io.element.android.libraries.matrix.api.spaces.SpaceRoom import io.element.android.libraries.matrix.api.spaces.SpaceRoom
import io.element.android.libraries.previewutils.room.aSpaceRoom import io.element.android.libraries.previewutils.room.aSpaceRoom
@@ -27,11 +29,11 @@ open class SpaceStateProvider : PreviewParameterProvider<SpaceState> {
override val values: Sequence<SpaceState> override val values: Sequence<SpaceState>
get() = sequenceOf( get() = sequenceOf(
aSpaceState(), aSpaceState(),
aSpaceState(parentSpace = aParentSpace(joinRule = JoinRule.Public)), aSpaceState(spaceInfo = aSpaceInfo(joinRule = JoinRule.Public)),
aSpaceState(parentSpace = aParentSpace(joinRule = JoinRule.Restricted(persistentListOf()))), aSpaceState(spaceInfo = aSpaceInfo(joinRule = JoinRule.Restricted(persistentListOf()))),
aSpaceState(children = aListOfSpaceRooms()), aSpaceState(children = aListOfSpaceRooms()),
aSpaceState( aSpaceState(
parentSpace = aParentSpace(), spaceInfo = aSpaceInfo(),
children = aListOfSpaceRooms(), children = aListOfSpaceRooms(),
joiningRooms = setOf(RoomId("!spaceId0:example.com")), joiningRooms = setOf(RoomId("!spaceId0:example.com")),
hasMoreToLoad = false hasMoreToLoad = false
@@ -41,19 +43,19 @@ open class SpaceStateProvider : PreviewParameterProvider<SpaceState> {
), ),
// Manage mode states // Manage mode states
aSpaceState( aSpaceState(
parentSpace = aParentSpace(), spaceInfo = aSpaceInfo(),
children = aListOfSpaceRooms(), children = aListOfSpaceRooms(),
isManageMode = true, isManageMode = true,
selectedRoomIds = emptySet(), selectedRoomIds = emptySet(),
), ),
aSpaceState( aSpaceState(
parentSpace = aParentSpace(), spaceInfo = aSpaceInfo(),
children = aListOfSpaceRooms(), children = aListOfSpaceRooms(),
isManageMode = true, isManageMode = true,
selectedRoomIds = setOf(RoomId("!spaceId0:example.com"), RoomId("!spaceId1:example.com")), selectedRoomIds = setOf(RoomId("!spaceId0:example.com"), RoomId("!spaceId1:example.com")),
), ),
aSpaceState( aSpaceState(
parentSpace = aParentSpace(), spaceInfo = aSpaceInfo(),
children = aListOfSpaceRooms(), children = aListOfSpaceRooms(),
isManageMode = true, isManageMode = true,
selectedRoomIds = setOf(RoomId("!spaceId0:example.com")), selectedRoomIds = setOf(RoomId("!spaceId0:example.com")),
@@ -63,7 +65,7 @@ open class SpaceStateProvider : PreviewParameterProvider<SpaceState> {
} }
fun aSpaceState( fun aSpaceState(
parentSpace: SpaceRoom = aParentSpace(), spaceInfo: RoomInfo = aSpaceInfo(),
children: List<SpaceRoom> = emptyList(), children: List<SpaceRoom> = emptyList(),
seenSpaceInvites: Set<RoomId> = emptySet(), seenSpaceInvites: Set<RoomId> = emptySet(),
joiningRooms: Set<RoomId> = emptySet(), joiningRooms: Set<RoomId> = emptySet(),
@@ -79,8 +81,7 @@ fun aSpaceState(
removeRoomsAction: AsyncAction<Unit> = AsyncAction.Uninitialized, removeRoomsAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
eventSink: (SpaceEvents) -> Unit = { }, eventSink: (SpaceEvents) -> Unit = { },
) = SpaceState( ) = SpaceState(
currentSpaceId = parentSpace.roomId, spaceInfo = spaceInfo,
currentSpace = parentSpace,
children = children.toImmutableList(), children = children.toImmutableList(),
seenSpaceInvites = seenSpaceInvites.toImmutableSet(), seenSpaceInvites = seenSpaceInvites.toImmutableSet(),
hideInvitesAvatar = hideInvitesAvatar, hideInvitesAvatar = hideInvitesAvatar,
@@ -96,16 +97,45 @@ fun aSpaceState(
eventSink = eventSink, eventSink = eventSink,
) )
private fun aParentSpace( private fun aSpaceInfo(
joinRule: JoinRule? = null, joinRule: JoinRule? = null,
): SpaceRoom { ): RoomInfo {
return aSpaceRoom( return RoomInfo(
numJoinedMembers = 5, id = RoomId("!spaceId0:example.com"),
childrenCount = 10, name = "A Space",
worldReadable = true, rawName = "A Space",
joinRule = joinRule,
roomId = RoomId("!spaceId0:example.com"),
topic = "Space description goes here. " + LoremIpsum(20).values.first(), 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,
) )
} }

View File

@@ -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.TextButton
import io.element.android.libraries.designsystem.theme.components.TopAppBar 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.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.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.JoinButton
import io.element.android.libraries.matrix.ui.components.SpaceHeaderView import io.element.android.libraries.matrix.ui.components.SpaceHeaderView
import io.element.android.libraries.matrix.ui.components.SpaceRoomItemView import io.element.android.libraries.matrix.ui.components.SpaceRoomItemView
@@ -131,7 +133,7 @@ fun SpaceView(
exit = fadeOut() exit = fadeOut()
) { ) {
SpaceViewTopBar( SpaceViewTopBar(
currentSpace = state.currentSpace, spaceInfo = state.spaceInfo,
canAccessSpaceSettings = state.canAccessSpaceSettings, canAccessSpaceSettings = state.canAccessSpaceSettings,
showManageRoomsAction = state.showManageRoomsAction, showManageRoomsAction = state.showManageRoomsAction,
onBackClick = onBackClick, onBackClick = onBackClick,
@@ -166,7 +168,7 @@ fun SpaceView(
eventSink = state.eventSink eventSink = state.eventSink
) )
RemoveRoomsActionView( RemoveRoomsActionView(
spaceDisplayName = state.currentSpaceDisplayName, spaceDisplayName = state.spaceInfo.name ?: state.spaceInfo.id.value,
removeRoomsAction = state.removeRoomsAction, removeRoomsAction = state.removeRoomsAction,
selectedCount = state.selectedCount, selectedCount = state.selectedCount,
onConfirm = { state.eventSink(SpaceEvents.ConfirmRoomRemoval) }, onConfirm = { state.eventSink(SpaceEvents.ConfirmRoomRemoval) },
@@ -235,27 +237,25 @@ private fun SpaceViewContent(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
LazyColumn(modifier.fillMaxSize()) { LazyColumn(modifier.fillMaxSize()) {
val currentSpace = state.currentSpace val spaceInfo = state.spaceInfo
if (currentSpace != null) { item(key = "space_header") {
item(key = "space_header") { AnimatedVisibility(
AnimatedVisibility( !state.isManageMode,
!state.isManageMode, enter = fadeIn() + expandVertically(),
enter = fadeIn() + expandVertically(), exit = fadeOut() + shrinkVertically()
exit = fadeOut() + shrinkVertically() ) {
) { Column {
Column { SpaceHeaderView(
SpaceHeaderView( avatarData = spaceInfo.getAvatarData(AvatarSize.SpaceHeader),
avatarData = currentSpace.getAvatarData(AvatarSize.SpaceHeader), name = spaceInfo.name,
name = currentSpace.displayName, topic = spaceInfo.topic,
topic = currentSpace.topic, topicMaxLines = 2,
topicMaxLines = 2, visibility = SpaceRoomVisibility.fromJoinRule(spaceInfo.joinRule),
visibility = currentSpace.visibility, heroes = spaceInfo.heroes,
heroes = currentSpace.heroes.toImmutableList(), numberOfMembers = spaceInfo.joinedMembersCount.toInt(),
numberOfMembers = currentSpace.numJoinedMembers, onTopicClick = onTopicClick
onTopicClick = onTopicClick )
) HorizontalDivider()
HorizontalDivider()
}
} }
} }
} }
@@ -337,7 +337,7 @@ private fun LoadingMoreIndicator(
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
private fun SpaceViewTopBar( private fun SpaceViewTopBar(
currentSpace: SpaceRoom?, spaceInfo: RoomInfo,
canAccessSpaceSettings: Boolean, canAccessSpaceSettings: Boolean,
showManageRoomsAction: Boolean, showManageRoomsAction: Boolean,
onBackClick: () -> Unit, onBackClick: () -> Unit,
@@ -354,16 +354,14 @@ private fun SpaceViewTopBar(
BackButton(onClick = onBackClick) BackButton(onClick = onBackClick)
}, },
title = { title = {
if (currentSpace != null) { val roundedCornerShape = RoundedCornerShape(8.dp)
val roundedCornerShape = RoundedCornerShape(8.dp) SpaceAvatarAndNameRow(
SpaceAvatarAndNameRow( name = spaceInfo.name,
name = currentSpace.displayName, avatarData = spaceInfo.getAvatarData(AvatarSize.TimelineRoom),
avatarData = currentSpace.getAvatarData(AvatarSize.TimelineRoom), modifier = Modifier
modifier = Modifier .clip(roundedCornerShape)
.clip(roundedCornerShape) .clickable(enabled = canAccessSpaceSettings, onClick = onSettingsClick)
.clickable(enabled = canAccessSpaceSettings, onClick = onSettingsClick) )
)
}
}, },
actions = { actions = {
var showMenu by remember { mutableStateOf(false) } var showMenu by remember { mutableStateOf(false) }

View File

@@ -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.A_ROOM_ID_3
import io.element.android.libraries.matrix.test.FakeMatrixClient 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.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.join.FakeJoinRoom
import io.element.android.libraries.matrix.test.room.powerlevels.FakeRoomPermissions import io.element.android.libraries.matrix.test.room.powerlevels.FakeRoomPermissions
import io.element.android.libraries.matrix.test.spaces.FakeSpaceRoomList import io.element.android.libraries.matrix.test.spaces.FakeSpaceRoomList
@@ -63,7 +64,7 @@ class SpacePresenterTest {
val presenter = createSpacePresenter(spaceRoomList = spaceRoomList) val presenter = createSpacePresenter(spaceRoomList = spaceRoomList)
presenter.test { presenter.test {
val state = awaitItem() val state = awaitItem()
assertThat(state.currentSpace).isNull() assertThat(state.spaceInfo).isNotNull()
assertThat(state.children).isEmpty() assertThat(state.children).isEmpty()
assertThat(state.seenSpaceInvites).isEmpty() assertThat(state.seenSpaceInvites).isEmpty()
assertThat(state.hideInvitesAvatar).isFalse() assertThat(state.hideInvitesAvatar).isFalse()
@@ -143,23 +144,6 @@ class SpacePresenterTest {
} }
} }
@Test
fun `present - current space value`() = runTest {
val paginateResult = lambdaRecorder<Result<Unit>> {
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 @Test
fun `present - children value`() = runTest { fun `present - children value`() = runTest {
val paginateResult = lambdaRecorder<Result<Unit>> { val paginateResult = lambdaRecorder<Result<Unit>> {

View File

@@ -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_ID
import io.element.android.libraries.matrix.test.A_ROOM_NAME 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.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.previewutils.room.aSpaceRoom
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.tests.testutils.EnsureNeverCalled import io.element.android.tests.testutils.EnsureNeverCalled
@@ -127,7 +128,7 @@ class SpaceViewTest {
val eventsRecorder = EventsRecorder<SpaceEvents>() val eventsRecorder = EventsRecorder<SpaceEvents>()
rule.setSpaceView( rule.setSpaceView(
aSpaceState( aSpaceState(
parentSpace = aSpaceRoom(topic = A_ROOM_TOPIC), spaceInfo = aRoomInfo(topic = A_ROOM_TOPIC),
hasMoreToLoad = false, hasMoreToLoad = false,
eventSink = eventsRecorder, eventSink = eventsRecorder,
) )