Merge pull request #5668 from element-hq/feature/fga/space_settings

Space : prepare Space Settings screen
This commit is contained in:
ganfra
2025-11-05 16:18:09 +01:00
committed by GitHub
25 changed files with 511 additions and 38 deletions

View File

@@ -30,7 +30,6 @@ import io.element.android.features.joinroom.api.JoinRoomEntryPoint
import io.element.android.features.roomaliasesolver.api.RoomAliasResolverEntryPoint
import io.element.android.features.roomaliasesolver.api.RoomAliasResolverEntryPoint.Params
import io.element.android.features.roomdirectory.api.RoomDescription
import io.element.android.features.space.api.SpaceEntryPoint
import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.NodeInputs
@@ -70,7 +69,6 @@ class RoomFlowNode(
private val joinRoomEntryPoint: JoinRoomEntryPoint,
private val roomAliasResolverEntryPoint: RoomAliasResolverEntryPoint,
private val membershipObserver: RoomMembershipObserver,
private val spaceEntryPoint: SpaceEntryPoint,
) : BaseFlowNode<RoomFlowNode.NavTarget>(
backstack = BackStack(
initialElement = NavTarget.Loading,
@@ -105,9 +103,6 @@ class RoomFlowNode(
@Parcelize
data class JoinedRoom(val roomId: RoomId) : NavTarget
@Parcelize
data class JoinedSpace(val spaceId: RoomId) : NavTarget
}
override fun onBuilt() {
@@ -209,15 +204,6 @@ class RoomFlowNode(
)
createNode<JoinedRoomFlowNode>(buildContext, plugins = listOf(inputs) + roomFlowNodeCallback)
}
is NavTarget.JoinedSpace -> {
val spaceCallback = plugins<SpaceEntryPoint.Callback>().single()
spaceEntryPoint.createNode(
parentNode = this,
buildContext = buildContext,
inputs = SpaceEntryPoint.Inputs(roomId = navTarget.spaceId),
callback = spaceCallback,
)
}
}
}

View File

@@ -197,10 +197,6 @@ class JoinedRoomLoadedFlowNode(
callback.navigateToRoom(roomId, viaParameters)
}
override fun navigateToRoomDetails() {
backstack.push(NavTarget.RoomDetails)
}
override fun navigateToRoomMemberList() {
backstack.push(NavTarget.RoomMemberList)
}

View File

@@ -28,7 +28,6 @@ interface SpaceEntryPoint : FeatureEntryPoint {
interface Callback : Plugin {
fun navigateToRoom(roomId: RoomId, viaParameters: List<String>)
fun navigateToRoomDetails()
fun navigateToRoomMemberList()
}
}

View File

@@ -18,6 +18,7 @@ import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.pop
import com.bumble.appyx.navmodel.backstack.operation.push
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject
@@ -26,14 +27,15 @@ import io.element.android.features.space.api.SpaceEntryPoint
import io.element.android.features.space.impl.di.SpaceFlowGraph
import io.element.android.features.space.impl.leave.LeaveSpaceNode
import io.element.android.features.space.impl.root.SpaceNode
import io.element.android.features.space.impl.settings.SpaceSettingsNode
import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.callback
import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.di.DependencyInjectionGraphOwner
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.libraries.matrix.api.spaces.SpaceService
import kotlinx.parcelize.Parcelize
@@ -42,6 +44,7 @@ import kotlinx.parcelize.Parcelize
class SpaceFlowNode(
@Assisted val buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
room: JoinedRoom,
spaceService: SpaceService,
graphFactory: SpaceFlowGraph.Factory,
) : BaseFlowNode<SpaceFlowNode.NavTarget>(
@@ -52,15 +55,17 @@ class SpaceFlowNode(
buildContext = buildContext,
plugins = plugins,
), DependencyInjectionGraphOwner {
private val inputs: SpaceEntryPoint.Inputs = inputs()
private val callback: SpaceEntryPoint.Callback = callback()
private val spaceRoomList = spaceService.spaceRoomList(inputs.roomId)
private val spaceRoomList = spaceService.spaceRoomList(room.roomId)
override val graph = graphFactory.create(spaceRoomList)
sealed interface NavTarget : Parcelable {
@Parcelize
data object Root : NavTarget
@Parcelize
data object Settings : NavTarget
@Parcelize
data object Leave : NavTarget
}
@@ -77,7 +82,16 @@ class SpaceFlowNode(
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
return when (navTarget) {
NavTarget.Leave -> {
createNode<LeaveSpaceNode>(buildContext, listOf(inputs))
val callback = object : LeaveSpaceNode.Callback {
override fun closeLeaveSpaceFlow() {
backstack.pop()
}
override fun navigateToRolesAndPermissions() {
// TODO
}
}
createNode<LeaveSpaceNode>(buildContext, listOf(callback))
}
NavTarget.Root -> {
val callback = object : SpaceNode.Callback {
@@ -85,8 +99,8 @@ class SpaceFlowNode(
callback.navigateToRoom(roomId, viaParameters)
}
override fun navigateToRoomDetails() {
callback.navigateToRoomDetails()
override fun navigateToSpaceSettings() {
backstack.push(NavTarget.Settings)
}
override fun navigateToRoomMemberList() {
@@ -97,7 +111,35 @@ class SpaceFlowNode(
backstack.push(NavTarget.Leave)
}
}
createNode<SpaceNode>(buildContext, listOf(inputs, callback))
createNode<SpaceNode>(buildContext, listOf(callback))
}
NavTarget.Settings -> {
val callback = object : SpaceSettingsNode.Callback {
override fun closeSettings() {
backstack.pop()
}
override fun navigateToSpaceInfo() {
// TODO
}
override fun navigateToSpaceMembers() {
callback.navigateToRoomMemberList()
}
override fun navigateToRolesAndPermissions() {
// TODO
}
override fun navigateToSecurityAndPrivacy() {
// TODO
}
override fun startLeaveSpaceFlow() {
backstack.push(NavTarget.Leave)
}
}
createNode<SpaceSettingsNode>(buildContext, listOf(callback))
}
}
}

View File

@@ -16,10 +16,10 @@ import com.bumble.appyx.core.plugin.Plugin
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode
import io.element.android.features.space.api.SpaceEntryPoint
import io.element.android.features.space.impl.di.SpaceFlowScope
import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.architecture.callback
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.room.JoinedRoom
@ContributesNode(SpaceFlowScope::class)
@AssistedInject
@@ -27,12 +27,19 @@ class LeaveSpaceNode(
@Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
matrixClient: MatrixClient,
room: JoinedRoom,
presenterFactory: LeaveSpacePresenter.Factory,
) : Node(buildContext, plugins = plugins) {
private val inputs: SpaceEntryPoint.Inputs = inputs()
private val leaveSpaceHandle = matrixClient.spaceService.getLeaveSpaceHandle(inputs.roomId)
interface Callback : Plugin {
fun closeLeaveSpaceFlow()
fun navigateToRolesAndPermissions()
}
private val leaveSpaceHandle = matrixClient.spaceService.getLeaveSpaceHandle(room.roomId)
private val presenter: LeaveSpacePresenter = presenterFactory.create(leaveSpaceHandle)
private val callback: Callback = callback()
override fun onBuilt() {
super.onBuilt()
lifecycle.subscribe(
@@ -47,7 +54,8 @@ class LeaveSpaceNode(
val state = presenter.present()
LeaveSpaceView(
state = state,
onCancel = ::navigateUp,
onCancel = callback::closeLeaveSpaceFlow,
onRolesAndPermissionsClick = callback::navigateToRolesAndPermissions,
modifier = modifier
)
}

View File

@@ -69,6 +69,7 @@ import io.element.android.libraries.ui.strings.CommonStrings
fun LeaveSpaceView(
state: LeaveSpaceState,
onCancel: () -> Unit,
onRolesAndPermissionsClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Scaffold(
@@ -130,6 +131,9 @@ fun LeaveSpaceView(
state.eventSink(LeaveSpaceEvents.LeaveSpace)
},
onCancel = onCancel,
// TODO enable when navigation is ready
showRolesAndPermissionsButton = false, // state.isLastAdmin,
onRolesAndPermissionsClick = onRolesAndPermissionsClick,
)
}
}
@@ -210,6 +214,8 @@ private fun LeaveSpaceButtons(
showLeaveButton: Boolean,
selectedRoomsCount: Int,
onLeaveSpace: () -> Unit,
showRolesAndPermissionsButton: Boolean,
onRolesAndPermissionsClick: () -> Unit,
onCancel: () -> Unit,
) {
ButtonColumnMolecule(
@@ -229,8 +235,14 @@ private fun LeaveSpaceButtons(
destructive = true,
)
}
// TODO For least admin space, add a button to open the settings.
// See https://www.figma.com/design/kcnHxunG1LDWXsJhaNuiHz/ER-145--Spaces-on-Element-X?node-id=4622-59600
if (showRolesAndPermissionsButton) {
Button(
text = stringResource(CommonStrings.action_go_to_roles_and_permissions),
onClick = onRolesAndPermissionsClick,
modifier = Modifier.fillMaxWidth(),
leadingIcon = IconSource.Vector(CompoundIcons.Settings()),
)
}
TextButton(
modifier = Modifier.fillMaxWidth(),
text = stringResource(CommonStrings.action_cancel),
@@ -345,5 +357,6 @@ internal fun LeaveSpaceViewPreview(
LeaveSpaceView(
state = state,
onCancel = {},
onRolesAndPermissionsClick = {},
)
}

View File

@@ -42,7 +42,7 @@ class SpaceNode(
) : Node(buildContext, plugins = plugins) {
interface Callback : Plugin {
fun navigateToRoom(roomId: RoomId, viaParameters: List<String>)
fun navigateToRoomDetails()
fun navigateToSpaceSettings()
fun navigateToRoomMemberList()
fun startLeaveSpaceFlow()
}
@@ -80,7 +80,7 @@ class SpaceNode(
callback.navigateToRoom(spaceRoom.roomId, spaceRoom.via)
},
onDetailsClick = {
callback.navigateToRoomDetails()
callback.navigateToSpaceSettings()
},
onShareSpace = {
onShareRoom(context)

View File

@@ -328,7 +328,7 @@ private fun SpaceViewTopBar(
},
text = {
Text(
text = stringResource(id = CommonStrings.action_leave),
text = stringResource(id = CommonStrings.action_leave_space),
color = ElementTheme.colors.textCriticalPrimary,
)
},

View File

@@ -0,0 +1,10 @@
/*
* 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.features.space.impl.settings
sealed interface SpaceSettingsEvents

View File

@@ -0,0 +1,58 @@
/*
* 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.features.space.impl.settings
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode
import io.element.android.features.space.impl.di.SpaceFlowScope
import io.element.android.libraries.architecture.appyx.launchMolecule
import io.element.android.libraries.architecture.callback
@ContributesNode(SpaceFlowScope::class)
@AssistedInject
class SpaceSettingsNode(
@Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
private val presenter: SpaceSettingsPresenter,
) : Node(buildContext, plugins = plugins) {
interface Callback : Plugin {
fun closeSettings()
fun navigateToSpaceInfo()
fun navigateToSpaceMembers()
fun navigateToRolesAndPermissions()
fun navigateToSecurityAndPrivacy()
fun startLeaveSpaceFlow()
}
private val callback: Callback = callback()
private val stateFlow = launchMolecule { presenter.present() }
@Composable
override fun View(modifier: Modifier) {
val state by stateFlow.collectAsState()
SpaceSettingsView(
state = state,
modifier = modifier,
onSpaceInfoClick = callback::navigateToSpaceInfo,
onBackClick = callback::closeSettings,
onMembersClick = callback::navigateToSpaceMembers,
onRolesAndPermissionsClick = callback::navigateToRolesAndPermissions,
onSecurityAndPrivacyClick = callback::navigateToSecurityAndPrivacy,
onLeaveSpaceClick = callback::startLeaveSpaceFlow,
)
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.features.space.impl.settings
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import dev.zacsweers.metro.Inject
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.libraries.matrix.ui.room.isOwnUserAdmin
@Inject
class SpaceSettingsPresenter(
private val room: JoinedRoom,
) : Presenter<SpaceSettingsState> {
@Composable
override fun present(): SpaceSettingsState {
val roomInfo by room.roomInfoFlow.collectAsState()
val isUserAdmin = room.isOwnUserAdmin()
return SpaceSettingsState(
roomId = room.roomId,
name = roomInfo.name.orEmpty(),
canonicalAlias = roomInfo.canonicalAlias,
avatarUrl = roomInfo.avatarUrl,
memberCount = roomInfo.activeMembersCount,
showRolesAndPermissions = isUserAdmin,
showSecurityAndPrivacy = isUserAdmin,
eventSink = {},
)
}
}

View File

@@ -0,0 +1,22 @@
/*
* 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.features.space.impl.settings
import io.element.android.libraries.matrix.api.core.RoomAlias
import io.element.android.libraries.matrix.api.core.RoomId
data class SpaceSettingsState(
val roomId: RoomId,
val name: String,
val canonicalAlias: RoomAlias?,
val avatarUrl: String?,
val memberCount: Long,
val showRolesAndPermissions: Boolean,
val showSecurityAndPrivacy: Boolean,
val eventSink: (SpaceSettingsEvents) -> Unit
)

View File

@@ -0,0 +1,42 @@
/*
* 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.features.space.impl.settings
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.matrix.api.core.RoomAlias
import io.element.android.libraries.matrix.api.core.RoomId
open class SpaceSettingsStateProvider : PreviewParameterProvider<SpaceSettingsState> {
override val values: Sequence<SpaceSettingsState>
get() = sequenceOf(
aSpaceSettingsState(),
aSpaceSettingsState(alias = null),
aSpaceSettingsState(showSecurityAndPrivacy = true),
aSpaceSettingsState(showRolesAndPermissions = true),
)
}
fun aSpaceSettingsState(
roomId: RoomId = RoomId("!aRoomId:element.io"),
name: String = "Space name",
alias: RoomAlias? = RoomAlias("#spacename:element.io"),
avatarUrl: String? = null,
memberCount: Long = 100,
showRolesAndPermissions: Boolean = false,
showSecurityAndPrivacy: Boolean = false,
eventSink: (SpaceSettingsEvents) -> Unit = {},
) = SpaceSettingsState(
roomId = roomId,
name = name,
canonicalAlias = alias,
avatarUrl = avatarUrl,
memberCount = memberCount,
showRolesAndPermissions = showRolesAndPermissions,
showSecurityAndPrivacy = showSecurityAndPrivacy,
eventSink = eventSink,
)

View File

@@ -0,0 +1,231 @@
/*
* 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.features.space.impl.settings
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
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
import io.element.android.features.space.impl.R
import io.element.android.libraries.designsystem.components.avatar.Avatar
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.components.button.BackButton
import io.element.android.libraries.designsystem.components.list.ListItemContent
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.IconSource
import io.element.android.libraries.designsystem.theme.components.ListItem
import io.element.android.libraries.designsystem.theme.components.ListItemStyle
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
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.ui.strings.CommonStrings
@Composable
fun SpaceSettingsView(
state: SpaceSettingsState,
onBackClick: () -> Unit,
onSpaceInfoClick: () -> Unit,
onMembersClick: () -> Unit,
onRolesAndPermissionsClick: () -> Unit,
onSecurityAndPrivacyClick: () -> Unit,
onLeaveSpaceClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Scaffold(
modifier = modifier,
topBar = {
SpaceSettingsTopBar(onBackClick = onBackClick)
},
) { padding ->
Column(
modifier = Modifier
.padding(padding)
.verticalScroll(rememberScrollState())
) {
SpaceInfoSection(
roomId = state.roomId,
name = state.name,
avatarUrl = state.avatarUrl,
canonicalAlias = state.canonicalAlias?.value,
onSpaceInfoClick = onSpaceInfoClick,
)
Section(isVisible = state.showSecurityAndPrivacy, content = {
SecurityAndPrivacyItem(
onClick = onSecurityAndPrivacyClick
)
})
Section(content = {
MembersItem(state.memberCount, onClick = onMembersClick)
if (state.showRolesAndPermissions) {
RolesAndPermissionsItem(onClick = onRolesAndPermissionsClick)
}
})
Section(content = {
LeaveSpaceItem(
onClick = onLeaveSpaceClick
)
})
}
}
}
@Composable
private fun SpaceInfoSection(
roomId: RoomId,
name: String,
avatarUrl: String?,
canonicalAlias: String?,
onSpaceInfoClick: () -> Unit,
) {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = onSpaceInfoClick)
.padding(16.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Avatar(
avatarData = AvatarData(roomId.value, name, avatarUrl, AvatarSize.SpaceListItem),
avatarType = AvatarType.Space(),
contentDescription = avatarUrl?.let { stringResource(CommonStrings.a11y_avatar) },
)
Spacer(Modifier.width(16.dp))
Column {
Text(
text = name,
style = ElementTheme.typography.fontHeadingMdRegular,
color = ElementTheme.colors.textPrimary,
)
if (canonicalAlias != null) {
Text(
text = canonicalAlias,
style = ElementTheme.typography.fontBodyMdRegular,
color = ElementTheme.colors.textSecondary,
)
}
}
}
}
@Composable
private fun Section(
modifier: Modifier = Modifier,
isVisible: Boolean = true,
content: @Composable ColumnScope.() -> Unit,
) {
if (isVisible) {
PreferenceCategory(content = content, modifier = modifier)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun SpaceSettingsTopBar(
onBackClick: () -> Unit,
modifier: Modifier = Modifier,
) {
TopAppBar(
titleStr = stringResource(CommonStrings.common_settings),
navigationIcon = { BackButton(onClick = onBackClick) },
modifier = modifier,
)
}
@Composable
private fun SecurityAndPrivacyItem(
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
ListItem(
headlineContent = { Text(stringResource(R.string.screen_space_settings_security_and_privacy)) },
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Lock())),
onClick = onClick,
modifier = modifier,
)
}
@Composable
private fun MembersItem(
memberCount: Long,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
ListItem(
headlineContent = { Text(stringResource(CommonStrings.common_people)) },
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.User())),
trailingContent = ListItemContent.Text(memberCount.toString()),
onClick = onClick,
modifier = modifier,
)
}
@Composable
private fun RolesAndPermissionsItem(
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
ListItem(
headlineContent = { Text(stringResource(R.string.screen_space_settings_roles_and_permissions)) },
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Admin())),
onClick = onClick,
modifier = modifier,
)
}
@Composable
private fun LeaveSpaceItem(
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
ListItem(
headlineContent = {
Text(stringResource(CommonStrings.action_leave_space))
},
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Leave())),
style = ListItemStyle.Destructive,
onClick = onClick,
modifier = modifier,
)
}
@PreviewsDayNight
@Composable
internal fun SpaceSettingsViewPreview(
@PreviewParameter(SpaceSettingsStateProvider::class) state: SpaceSettingsState
) = ElementPreview {
SpaceSettingsView(
state = state,
onBackClick = {},
onSpaceInfoClick = {},
onMembersClick = {},
onRolesAndPermissionsClick = {},
onSecurityAndPrivacyClick = {},
onLeaveSpaceClick = {},
modifier = Modifier,
)
}

View File

@@ -10,4 +10,7 @@
<string name="screen_leave_space_subtitle_only_last_admin">"You will not be removed from the following room(s) because you\'re the only administrator:"</string>
<string name="screen_leave_space_title">"Leave %1$s?"</string>
<string name="screen_leave_space_title_last_admin">"You are the only admin for %1$s"</string>
<string name="screen_space_settings_leave_space">"Leave space"</string>
<string name="screen_space_settings_roles_and_permissions">"Roles &amp; permissions"</string>
<string name="screen_space_settings_security_and_privacy">"Security &amp; privacy"</string>
</resources>

View File

@@ -15,6 +15,7 @@ import io.element.android.features.space.api.SpaceEntryPoint
import io.element.android.features.space.impl.di.FakeSpaceFlowGraph
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
import io.element.android.libraries.matrix.test.spaces.FakeSpaceRoomList
import io.element.android.libraries.matrix.test.spaces.FakeSpaceService
import io.element.android.tests.testutils.lambda.lambdaError
@@ -40,12 +41,12 @@ class DefaultSpaceEntryPointTest {
spaceService = FakeSpaceService(
spaceRoomListResult = { _: RoomId -> FakeSpaceRoomList(A_ROOM_ID) }
),
room = FakeJoinedRoom(),
graphFactory = FakeSpaceFlowGraph.Factory
)
}
val callback = object : SpaceEntryPoint.Callback {
override fun navigateToRoom(roomId: RoomId, viaParameters: List<String>) = lambdaError()
override fun navigateToRoomDetails() = lambdaError()
override fun navigateToRoomMemberList() = lambdaError()
}
val result = entryPoint.createNode(

View File

@@ -210,7 +210,8 @@
{
"name" : ":features:space:impl",
"includeRegex" : [
"screen\\.leave_space\\..*"
"screen\\.leave_space\\..*",
"screen\\.space_settings\\..*"
]
},
{