Merge pull request #5619 from element-hq/feature/fga/space_members

Space : add view members entry
This commit is contained in:
ganfra 2025-10-29 10:53:45 +01:00 committed by GitHub
commit 277dd528ee
9 changed files with 57 additions and 13 deletions

View file

@ -22,8 +22,6 @@ import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.appnav.di.RoomGraphFactory import io.element.android.appnav.di.RoomGraphFactory
import io.element.android.appnav.room.RoomNavigationTarget import io.element.android.appnav.room.RoomNavigationTarget
import io.element.android.appnav.room.joined.JoinedRoomLoadedFlowNode.Inputs
import io.element.android.appnav.room.joined.JoinedRoomLoadedFlowNode.NavTarget
import io.element.android.features.messages.api.MessagesEntryPoint import io.element.android.features.messages.api.MessagesEntryPoint
import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint
import io.element.android.features.space.api.SpaceEntryPoint import io.element.android.features.space.api.SpaceEntryPoint
@ -154,6 +152,9 @@ class JoinedRoomLoadedFlowNode(
NavTarget.RoomNotificationSettings -> { NavTarget.RoomNotificationSettings -> {
createRoomDetailsNode(buildContext, RoomDetailsEntryPoint.InitialTarget.RoomNotificationSettings) createRoomDetailsNode(buildContext, RoomDetailsEntryPoint.InitialTarget.RoomNotificationSettings)
} }
NavTarget.RoomMemberList -> {
createRoomDetailsNode(buildContext, RoomDetailsEntryPoint.InitialTarget.RoomMemberList)
}
NavTarget.Space -> { NavTarget.Space -> {
createSpaceNode(buildContext) createSpaceNode(buildContext)
} }
@ -169,6 +170,10 @@ class JoinedRoomLoadedFlowNode(
override fun onOpenDetails() { override fun onOpenDetails() {
backstack.push(NavTarget.RoomDetails) backstack.push(NavTarget.RoomDetails)
} }
override fun onOpenMemberList() {
backstack.push(NavTarget.RoomMemberList)
}
} }
return spaceEntryPoint.nodeBuilder(this, buildContext) return spaceEntryPoint.nodeBuilder(this, buildContext)
.inputs(SpaceEntryPoint.Inputs(roomId = inputs.room.roomId)) .inputs(SpaceEntryPoint.Inputs(roomId = inputs.room.roomId))
@ -216,6 +221,9 @@ class JoinedRoomLoadedFlowNode(
@Parcelize @Parcelize
data object RoomDetails : NavTarget data object RoomDetails : NavTarget
@Parcelize
data object RoomMemberList : NavTarget
@Parcelize @Parcelize
data class RoomMemberDetails(val userId: UserId) : NavTarget data class RoomMemberDetails(val userId: UserId) : NavTarget
@ -229,17 +237,17 @@ class JoinedRoomLoadedFlowNode(
} }
} }
private fun initialElement(plugins: List<Plugin>): NavTarget { private fun initialElement(plugins: List<Plugin>): JoinedRoomLoadedFlowNode.NavTarget {
val input = plugins.filterIsInstance<Inputs>().single() val input = plugins.filterIsInstance<JoinedRoomLoadedFlowNode.Inputs>().single()
return when (input.initialElement) { return when (input.initialElement) {
is RoomNavigationTarget.Root -> { is RoomNavigationTarget.Root -> {
if (input.room.roomInfoFlow.value.isSpace) { if (input.room.roomInfoFlow.value.isSpace) {
NavTarget.Space JoinedRoomLoadedFlowNode.NavTarget.Space
} else { } else {
NavTarget.Messages(input.initialElement.eventId) JoinedRoomLoadedFlowNode.NavTarget.Messages(input.initialElement.eventId)
} }
} }
RoomNavigationTarget.Details -> NavTarget.RoomDetails RoomNavigationTarget.Details -> JoinedRoomLoadedFlowNode.NavTarget.RoomDetails
RoomNavigationTarget.NotificationSettings -> NavTarget.RoomNotificationSettings RoomNavigationTarget.NotificationSettings -> JoinedRoomLoadedFlowNode.NavTarget.RoomNotificationSettings
} }
} }

View file

@ -23,6 +23,9 @@ interface RoomDetailsEntryPoint : FeatureEntryPoint {
@Parcelize @Parcelize
data object RoomDetails : InitialTarget data object RoomDetails : InitialTarget
@Parcelize
data object RoomMemberList : InitialTarget
@Parcelize @Parcelize
data class RoomMemberDetails(val roomMemberId: UserId) : InitialTarget data class RoomMemberDetails(val roomMemberId: UserId) : InitialTarget

View file

@ -44,4 +44,5 @@ internal fun InitialTarget.toNavTarget() = when (this) {
is InitialTarget.RoomDetails -> NavTarget.RoomDetails is InitialTarget.RoomDetails -> NavTarget.RoomDetails
is InitialTarget.RoomMemberDetails -> NavTarget.RoomMemberDetails(roomMemberId) is InitialTarget.RoomMemberDetails -> NavTarget.RoomMemberDetails(roomMemberId)
is InitialTarget.RoomNotificationSettings -> NavTarget.RoomNotificationSettings(showUserDefinedSettingStyle = true) is InitialTarget.RoomNotificationSettings -> NavTarget.RoomNotificationSettings(showUserDefinedSettingStyle = true)
InitialTarget.RoomMemberList -> NavTarget.RoomMemberList
} }

View file

@ -33,5 +33,7 @@ interface SpaceEntryPoint : FeatureEntryPoint {
interface Callback : Plugin { interface Callback : Plugin {
fun onOpenRoom(roomId: RoomId, viaParameters: List<String>) fun onOpenRoom(roomId: RoomId, viaParameters: List<String>)
fun onOpenDetails() fun onOpenDetails()
fun onOpenMemberList()
} }
} }

View file

@ -88,6 +88,10 @@ class SpaceFlowNode(
callback.onOpenDetails() callback.onOpenDetails()
} }
override fun onOpenMemberList() {
callback.onOpenMemberList()
}
override fun onLeaveSpace() { override fun onLeaveSpace() {
backstack.push(NavTarget.Leave) backstack.push(NavTarget.Leave)
} }

View file

@ -42,6 +42,8 @@ class SpaceNode(
interface Callback : Plugin { interface Callback : Plugin {
fun onOpenRoom(roomId: RoomId, viaParameters: List<String>) fun onOpenRoom(roomId: RoomId, viaParameters: List<String>)
fun onOpenDetails() fun onOpenDetails()
fun onOpenMemberList()
fun onLeaveSpace() fun onLeaveSpace()
} }
@ -83,6 +85,9 @@ class SpaceNode(
onShareSpace = { onShareSpace = {
onShareRoom(context) onShareRoom(context)
}, },
onViewMembersClick = {
callback.onOpenMemberList()
},
acceptDeclineInviteView = { acceptDeclineInviteView = {
acceptDeclineInviteView.Render( acceptDeclineInviteView.Render(
state = state.acceptDeclineInviteState, state = state.acceptDeclineInviteState,

View file

@ -78,6 +78,7 @@ fun SpaceView(
onShareSpace: () -> Unit, onShareSpace: () -> Unit,
onLeaveSpaceClick: () -> Unit, onLeaveSpaceClick: () -> Unit,
onDetailsClick: () -> Unit, onDetailsClick: () -> Unit,
onViewMembersClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
acceptDeclineInviteView: @Composable () -> Unit, acceptDeclineInviteView: @Composable () -> Unit,
) { ) {
@ -89,7 +90,8 @@ fun SpaceView(
onBackClick = onBackClick, onBackClick = onBackClick,
onLeaveSpaceClick = onLeaveSpaceClick, onLeaveSpaceClick = onLeaveSpaceClick,
onShareSpace = onShareSpace, onShareSpace = onShareSpace,
onDetailsClick = onDetailsClick onDetailsClick = onDetailsClick,
onViewMembersClick = onViewMembersClick,
) )
}, },
content = { padding -> content = { padding ->
@ -256,6 +258,7 @@ private fun SpaceViewTopBar(
onLeaveSpaceClick: () -> Unit, onLeaveSpaceClick: () -> Unit,
onDetailsClick: () -> Unit, onDetailsClick: () -> Unit,
onShareSpace: () -> Unit, onShareSpace: () -> Unit,
onViewMembersClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
TopAppBar( TopAppBar(
@ -304,6 +307,20 @@ private fun SpaceViewTopBar(
) )
} }
) )
DropdownMenuItem(
onClick = {
showMenu = false
onViewMembersClick()
},
text = { Text(stringResource(id = CommonStrings.screen_space_menu_action_members)) },
leadingIcon = {
Icon(
imageVector = CompoundIcons.User(),
tint = ElementTheme.colors.iconSecondary,
contentDescription = null,
)
}
)
DropdownMenuItem( DropdownMenuItem(
onClick = { onClick = {
showMenu = false showMenu = false
@ -344,10 +361,10 @@ private fun SpaceAvatarAndNameRow(
) )
Text( Text(
modifier = Modifier modifier = Modifier
.padding(horizontal = 8.dp) .padding(horizontal = 8.dp)
.semantics { .semantics {
heading() heading()
}, },
text = name ?: stringResource(CommonStrings.common_no_space_name), text = name ?: stringResource(CommonStrings.common_no_space_name),
style = ElementTheme.typography.fontBodyLgMedium, style = ElementTheme.typography.fontBodyLgMedium,
fontStyle = FontStyle.Italic.takeIf { name == null }, fontStyle = FontStyle.Italic.takeIf { name == null },
@ -403,6 +420,7 @@ internal fun SpaceViewPreview(
onLeaveSpaceClick = {}, onLeaveSpaceClick = {},
acceptDeclineInviteView = {}, acceptDeclineInviteView = {},
onDetailsClick = {}, onDetailsClick = {},
onViewMembersClick = {},
onBackClick = {}, onBackClick = {},
) )
} }

View file

@ -46,6 +46,7 @@ class DefaultSpaceEntryPointTest {
val callback = object : SpaceEntryPoint.Callback { val callback = object : SpaceEntryPoint.Callback {
override fun onOpenRoom(roomId: RoomId, viaParameters: List<String>) = lambdaError() override fun onOpenRoom(roomId: RoomId, viaParameters: List<String>) = lambdaError()
override fun onOpenDetails() = lambdaError() override fun onOpenDetails() = lambdaError()
override fun onOpenMemberList() = lambdaError()
} }
val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null))
.inputs(nodeInputs) .inputs(nodeInputs)

View file

@ -140,6 +140,7 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setSpace
onShareSpace: () -> Unit = EnsureNeverCalled(), onShareSpace: () -> Unit = EnsureNeverCalled(),
onLeaveSpaceClick: () -> Unit = EnsureNeverCalled(), onLeaveSpaceClick: () -> Unit = EnsureNeverCalled(),
onDetailsClick: () -> Unit = EnsureNeverCalled(), onDetailsClick: () -> Unit = EnsureNeverCalled(),
onViewMembersClick: () -> Unit = EnsureNeverCalled(),
acceptDeclineInviteView: @Composable () -> Unit = {}, acceptDeclineInviteView: @Composable () -> Unit = {},
) { ) {
setContent { setContent {
@ -150,6 +151,7 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setSpace
onShareSpace = onShareSpace, onShareSpace = onShareSpace,
onLeaveSpaceClick = onLeaveSpaceClick, onLeaveSpaceClick = onLeaveSpaceClick,
onDetailsClick = onDetailsClick, onDetailsClick = onDetailsClick,
onViewMembersClick = onViewMembersClick,
acceptDeclineInviteView = acceptDeclineInviteView, acceptDeclineInviteView = acceptDeclineInviteView,
) )
} }