Add special flow for leaving a space as the last owner (#6112)
* When the user is in a v12 room, use different UI to select the last owner when leaving - Add `LeaveSpaceRoom.areCreatorsPrivileged` to detect when this is happening. - Import new strings. - Build the new UI. - Attach it to a change member roles screen navigation. * Don't display the `isLastOwner` UI if the user is the only joined one in the room * Rename `LeaveSpaceState.isLastOwner` to `.needsOwnerChange`. This way, it's easier to understand the difference with the passed `LeaveSpaceRoom.isLastOwner` value * Add a test for the new check of user not being the last joined member * Fix paddings in `LeaveSpaceView` * Update screenshots --------- Co-authored-by: ElementBot <android@element.io>
This commit is contained in:
committed by
GitHub
parent
e34b15823e
commit
63f24f0ae1
@@ -7,8 +7,6 @@
|
||||
<string name="screen_create_room_name_placeholder">"Add name…"</string>
|
||||
<string name="screen_create_room_new_room_title">"New room"</string>
|
||||
<string name="screen_create_room_new_space_title">"New space"</string>
|
||||
<string name="screen_create_room_parent_space_home_description">"(no space)"</string>
|
||||
<string name="screen_create_room_parent_space_home_title">"Home"</string>
|
||||
<string name="screen_create_room_private_option_description">"Only people invited can join."</string>
|
||||
<string name="screen_create_room_private_option_title">"Private"</string>
|
||||
<string name="screen_create_room_public_option_description">"Anyone can find this room.
|
||||
|
||||
@@ -52,4 +52,5 @@ dependencies {
|
||||
testImplementation(projects.libraries.featureflag.test)
|
||||
testImplementation(projects.features.createroom.test)
|
||||
testImplementation(projects.features.invite.test)
|
||||
testImplementation(projects.features.rolesandpermissions.test)
|
||||
}
|
||||
|
||||
@@ -22,10 +22,13 @@ 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 com.bumble.appyx.navmodel.backstack.operation.replace
|
||||
import dev.zacsweers.metro.Assisted
|
||||
import dev.zacsweers.metro.AssistedInject
|
||||
import io.element.android.annotations.ContributesNode
|
||||
import io.element.android.features.createroom.api.CreateRoomEntryPoint
|
||||
import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesEntryPoint
|
||||
import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType
|
||||
import io.element.android.features.space.api.SpaceEntryPoint
|
||||
import io.element.android.features.space.impl.addroom.AddRoomToSpaceNode
|
||||
import io.element.android.features.space.impl.di.SpaceFlowGraph
|
||||
@@ -38,10 +41,15 @@ import io.element.android.libraries.architecture.callback
|
||||
import io.element.android.libraries.architecture.createNode
|
||||
import io.element.android.libraries.di.DependencyInjectionGraphOwner
|
||||
import io.element.android.libraries.di.RoomScope
|
||||
import io.element.android.libraries.di.annotations.SessionCoroutineScope
|
||||
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 io.element.android.libraries.matrix.api.spaces.loadAllIncrementally
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@ContributesNode(RoomScope::class)
|
||||
@@ -49,10 +57,12 @@ import kotlinx.parcelize.Parcelize
|
||||
class SpaceFlowNode(
|
||||
@Assisted val buildContext: BuildContext,
|
||||
@Assisted plugins: List<Plugin>,
|
||||
room: JoinedRoom,
|
||||
private val room: JoinedRoom,
|
||||
spaceService: SpaceService,
|
||||
graphFactory: SpaceFlowGraph.Factory,
|
||||
private val createRoomEntryPoint: CreateRoomEntryPoint,
|
||||
private val changeRoomMemberRolesEntryPoint: ChangeRoomMemberRolesEntryPoint,
|
||||
@SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope,
|
||||
) : BaseFlowNode<SpaceFlowNode.NavTarget>(
|
||||
backstack = BackStack(
|
||||
initialElement = NavTarget.Root,
|
||||
@@ -80,6 +90,9 @@ class SpaceFlowNode(
|
||||
|
||||
@Parcelize
|
||||
data object AddRoom : NavTarget
|
||||
|
||||
@Parcelize
|
||||
data object ChangeOwners : NavTarget
|
||||
}
|
||||
|
||||
override fun onBuilt() {
|
||||
@@ -105,6 +118,10 @@ class SpaceFlowNode(
|
||||
override fun navigateToRolesAndPermissions() {
|
||||
backstack.push(NavTarget.Settings(SpaceSettingsFlowNode.NavTarget.RolesAndPermissions))
|
||||
}
|
||||
|
||||
override fun navigateToChooseOwners() {
|
||||
backstack.replace(NavTarget.ChangeOwners)
|
||||
}
|
||||
}
|
||||
createNode<LeaveSpaceNode>(buildContext, listOf(callback))
|
||||
}
|
||||
@@ -177,6 +194,29 @@ class SpaceFlowNode(
|
||||
}
|
||||
createNode<AddRoomToSpaceNode>(buildContext, listOf(callback))
|
||||
}
|
||||
NavTarget.ChangeOwners -> {
|
||||
val node = changeRoomMemberRolesEntryPoint.createNode(
|
||||
parentNode = this,
|
||||
buildContext = buildContext,
|
||||
room = room,
|
||||
listType = ChangeRoomMemberRolesListType.SelectNewOwnersWhenLeaving,
|
||||
)
|
||||
|
||||
val completionProxy = node as ChangeRoomMemberRolesEntryPoint.NodeProxy
|
||||
sessionCoroutineScope.launch {
|
||||
val changedOwners = withContext(NonCancellable) {
|
||||
completionProxy.waitForCompletion()
|
||||
}
|
||||
|
||||
if (changedOwners) {
|
||||
backstack.replace(NavTarget.Leave)
|
||||
} else {
|
||||
backstack.pop()
|
||||
}
|
||||
}
|
||||
|
||||
node
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ class LeaveSpaceNode(
|
||||
interface Callback : Plugin {
|
||||
fun closeLeaveSpaceFlow()
|
||||
fun navigateToRolesAndPermissions()
|
||||
fun navigateToChooseOwners()
|
||||
}
|
||||
|
||||
private val leaveSpaceHandle = matrixClient.spaceService.getLeaveSpaceHandle(room.roomId)
|
||||
@@ -57,6 +58,7 @@ class LeaveSpaceNode(
|
||||
state = state,
|
||||
onCancel = callback::closeLeaveSpaceFlow,
|
||||
onRolesAndPermissionsClick = callback::navigateToRolesAndPermissions,
|
||||
onChooseOwnersClick = callback::navigateToChooseOwners,
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
|
||||
@@ -92,6 +92,7 @@ class LeaveSpacePresenter(
|
||||
SelectableSpaceRoom(
|
||||
spaceRoom = room.spaceRoom,
|
||||
isLastOwner = room.isLastOwner,
|
||||
joinedMembersCount = room.spaceRoom.numJoinedMembers,
|
||||
isSelected = selectedRoomIds.contains(room.spaceRoom.roomId),
|
||||
)
|
||||
}.toImmutableList()
|
||||
@@ -130,9 +131,11 @@ class LeaveSpacePresenter(
|
||||
}
|
||||
}
|
||||
|
||||
val currentSpaceToLeave = leaveSpaceRooms.dataOrNull()?.current
|
||||
return LeaveSpaceState(
|
||||
spaceName = leaveSpaceRooms.dataOrNull()?.current?.spaceRoom?.displayName,
|
||||
isLastOwner = leaveSpaceRooms.dataOrNull()?.current?.isLastOwner == true,
|
||||
spaceName = currentSpaceToLeave?.spaceRoom?.displayName,
|
||||
needsOwnerChange = currentSpaceToLeave?.let { it.spaceRoom.numJoinedMembers > 1 && it.isLastOwner } == true,
|
||||
areCreatorsPrivileged = currentSpaceToLeave?.areCreatorsPrivileged == true,
|
||||
selectableSpaceRooms = selectableSpaceRooms,
|
||||
leaveSpaceAction = leaveSpaceAction.value,
|
||||
eventSink = ::handleEvent,
|
||||
|
||||
@@ -15,7 +15,8 @@ import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
data class LeaveSpaceState(
|
||||
val spaceName: String?,
|
||||
val isLastOwner: Boolean,
|
||||
val needsOwnerChange: Boolean,
|
||||
val areCreatorsPrivileged: Boolean,
|
||||
val selectableSpaceRooms: AsyncData<ImmutableList<SelectableSpaceRoom>>,
|
||||
val leaveSpaceAction: AsyncAction<Unit>,
|
||||
val eventSink: (LeaveSpaceEvents) -> Unit,
|
||||
@@ -25,7 +26,7 @@ data class LeaveSpaceState(
|
||||
private val selectableRooms: ImmutableList<SelectableSpaceRoom>
|
||||
|
||||
init {
|
||||
val partition = rooms.partition { it.isLastOwner }
|
||||
val partition = rooms.partition { it.isLastOwner && it.joinedMembersCount > 1 }
|
||||
lastAdminRooms = partition.first.toImmutableList()
|
||||
selectableRooms = partition.second.toImmutableList()
|
||||
}
|
||||
@@ -33,12 +34,12 @@ data class LeaveSpaceState(
|
||||
/**
|
||||
* True if we should show the quick action to select/deselect all rooms.
|
||||
*/
|
||||
val showQuickAction = isLastOwner.not() && selectableRooms.isNotEmpty()
|
||||
val showQuickAction = needsOwnerChange.not() && selectableRooms.isNotEmpty()
|
||||
|
||||
/**
|
||||
* True if we should show the leave button.
|
||||
*/
|
||||
val showLeaveButton = isLastOwner.not() && selectableSpaceRooms is AsyncData.Success
|
||||
val showLeaveButton = needsOwnerChange.not() && selectableSpaceRooms is AsyncData.Success
|
||||
|
||||
/**
|
||||
* True if there all the selectable rooms are selected.
|
||||
|
||||
@@ -109,17 +109,23 @@ class LeaveSpaceStateProvider : PreviewParameterProvider<LeaveSpaceState> {
|
||||
aLeaveSpaceState(
|
||||
isLastOwner = true,
|
||||
),
|
||||
aLeaveSpaceState(
|
||||
isLastOwner = true,
|
||||
areCreatorsPrivileged = true,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun aLeaveSpaceState(
|
||||
spaceName: String? = "Space name",
|
||||
isLastOwner: Boolean = false,
|
||||
areCreatorsPrivileged: Boolean = false,
|
||||
selectableSpaceRooms: AsyncData<ImmutableList<SelectableSpaceRoom>> = AsyncData.Uninitialized,
|
||||
leaveSpaceAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||
) = LeaveSpaceState(
|
||||
spaceName = spaceName,
|
||||
isLastOwner = isLastOwner,
|
||||
needsOwnerChange = isLastOwner,
|
||||
areCreatorsPrivileged = areCreatorsPrivileged,
|
||||
selectableSpaceRooms = selectableSpaceRooms,
|
||||
leaveSpaceAction = leaveSpaceAction,
|
||||
eventSink = { }
|
||||
@@ -128,9 +134,11 @@ fun aLeaveSpaceState(
|
||||
fun aSelectableSpaceRoom(
|
||||
spaceRoom: SpaceRoom = aSpaceRoom(),
|
||||
isLastOwner: Boolean = false,
|
||||
joinedMembersCount: Int = 2,
|
||||
isSelected: Boolean = false,
|
||||
) = SelectableSpaceRoom(
|
||||
spaceRoom = spaceRoom,
|
||||
isLastOwner = isLastOwner,
|
||||
joinedMembersCount = joinedMembersCount,
|
||||
isSelected = isSelected,
|
||||
)
|
||||
|
||||
@@ -12,14 +12,13 @@ package io.element.android.features.space.impl.leave
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
@@ -40,6 +39,7 @@ import io.element.android.features.space.impl.R
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
|
||||
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
|
||||
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
|
||||
import io.element.android.libraries.designsystem.components.BigIcon
|
||||
import io.element.android.libraries.designsystem.components.async.AsyncActionView
|
||||
import io.element.android.libraries.designsystem.components.async.AsyncFailure
|
||||
@@ -54,7 +54,6 @@ import io.element.android.libraries.designsystem.theme.components.Button
|
||||
import io.element.android.libraries.designsystem.theme.components.Checkbox
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.IconSource
|
||||
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.TextButton
|
||||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||
@@ -71,30 +70,42 @@ fun LeaveSpaceView(
|
||||
state: LeaveSpaceState,
|
||||
onCancel: () -> Unit,
|
||||
onRolesAndPermissionsClick: () -> Unit,
|
||||
onChooseOwnersClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Scaffold(
|
||||
HeaderFooterPage(
|
||||
modifier = modifier,
|
||||
contentPadding = PaddingValues(bottom = 14.dp),
|
||||
topBar = {
|
||||
LeaveSpaceHeader(
|
||||
state = state,
|
||||
onBackClick = onCancel,
|
||||
TopAppBar(
|
||||
navigationIcon = {
|
||||
BackButton(onClick = onCancel)
|
||||
},
|
||||
title = {},
|
||||
)
|
||||
},
|
||||
containerColor = ElementTheme.colors.bgCanvasDefault,
|
||||
) { padding ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(padding)
|
||||
.imePadding()
|
||||
.consumeWindowInsets(padding)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.weight(1f),
|
||||
) {
|
||||
if (state.isLastOwner.not()) {
|
||||
header = {
|
||||
LeaveSpaceHeader(state = state)
|
||||
},
|
||||
footer = {
|
||||
LeaveSpaceButtons(
|
||||
showLeaveButton = state.showLeaveButton,
|
||||
selectedRoomsCount = state.selectedRoomsCount,
|
||||
onLeaveSpace = {
|
||||
state.eventSink(LeaveSpaceEvents.LeaveSpace)
|
||||
},
|
||||
onCancel = onCancel,
|
||||
showRolesAndPermissionsButton = state.needsOwnerChange && !state.areCreatorsPrivileged,
|
||||
showChooseOwnersButton = state.needsOwnerChange && state.areCreatorsPrivileged,
|
||||
onChooseOwnersButtonClick = onChooseOwnersClick,
|
||||
onRolesAndPermissionsClick = onRolesAndPermissionsClick,
|
||||
)
|
||||
},
|
||||
content = {
|
||||
if (state.needsOwnerChange.not()) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.padding(top = 20.dp),
|
||||
) {
|
||||
when (state.selectableSpaceRooms) {
|
||||
is AsyncData.Success -> {
|
||||
// List rooms where the user is the only admin
|
||||
@@ -125,18 +136,8 @@ fun LeaveSpaceView(
|
||||
}
|
||||
}
|
||||
}
|
||||
LeaveSpaceButtons(
|
||||
showLeaveButton = state.showLeaveButton,
|
||||
selectedRoomsCount = state.selectedRoomsCount,
|
||||
onLeaveSpace = {
|
||||
state.eventSink(LeaveSpaceEvents.LeaveSpace)
|
||||
},
|
||||
onCancel = onCancel,
|
||||
showRolesAndPermissionsButton = state.isLastOwner,
|
||||
onRolesAndPermissionsClick = onRolesAndPermissionsClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
AsyncActionView(
|
||||
async = state.leaveSpaceAction,
|
||||
@@ -149,25 +150,27 @@ fun LeaveSpaceView(
|
||||
@Composable
|
||||
private fun LeaveSpaceHeader(
|
||||
state: LeaveSpaceState,
|
||||
onBackClick: () -> Unit,
|
||||
) {
|
||||
Column {
|
||||
TopAppBar(
|
||||
navigationIcon = {
|
||||
BackButton(onClick = onBackClick)
|
||||
},
|
||||
title = {},
|
||||
)
|
||||
IconTitleSubtitleMolecule(
|
||||
modifier = Modifier.padding(top = 0.dp, bottom = 8.dp, start = 24.dp, end = 24.dp),
|
||||
modifier = Modifier.padding(top = 24.dp, bottom = 8.dp, start = 24.dp, end = 24.dp),
|
||||
iconStyle = BigIcon.Style.AlertSolid,
|
||||
title = stringResource(
|
||||
if (state.isLastOwner) R.string.screen_leave_space_title_last_admin else R.string.screen_leave_space_title,
|
||||
state.spaceName ?: stringResource(CommonStrings.common_space)
|
||||
),
|
||||
title = if (state.needsOwnerChange) {
|
||||
if (state.areCreatorsPrivileged) {
|
||||
stringResource(R.string.screen_leave_space_title_last_owner)
|
||||
} else {
|
||||
stringResource(R.string.screen_leave_space_title_last_admin, state.spaceName ?: stringResource(CommonStrings.common_space))
|
||||
}
|
||||
} else {
|
||||
stringResource(R.string.screen_leave_space_title, state.spaceName ?: stringResource(CommonStrings.common_space))
|
||||
},
|
||||
subTitle =
|
||||
if (state.isLastOwner) {
|
||||
stringResource(R.string.screen_leave_space_subtitle_last_admin)
|
||||
if (state.needsOwnerChange) {
|
||||
if (state.areCreatorsPrivileged) {
|
||||
stringResource(R.string.screen_leave_space_subtitle_last_owner, state.spaceName ?: stringResource(CommonStrings.common_space))
|
||||
} else {
|
||||
stringResource(R.string.screen_leave_space_subtitle_last_admin)
|
||||
}
|
||||
} else if (state.selectableSpaceRooms is AsyncData.Success && state.selectableSpaceRooms.data.isNotEmpty()) {
|
||||
if (state.hasOnlyLastAdminRoom) {
|
||||
stringResource(R.string.screen_leave_space_subtitle_only_last_admin)
|
||||
@@ -216,10 +219,12 @@ private fun LeaveSpaceButtons(
|
||||
onLeaveSpace: () -> Unit,
|
||||
showRolesAndPermissionsButton: Boolean,
|
||||
onRolesAndPermissionsClick: () -> Unit,
|
||||
showChooseOwnersButton: Boolean,
|
||||
onChooseOwnersButtonClick: () -> Unit,
|
||||
onCancel: () -> Unit,
|
||||
) {
|
||||
ButtonColumnMolecule(
|
||||
modifier = Modifier.padding(16.dp)
|
||||
modifier = Modifier.padding(top = 16.dp)
|
||||
) {
|
||||
if (showLeaveButton) {
|
||||
val text = if (selectedRoomsCount > 0) {
|
||||
@@ -243,6 +248,14 @@ private fun LeaveSpaceButtons(
|
||||
leadingIcon = IconSource.Vector(CompoundIcons.Settings()),
|
||||
)
|
||||
}
|
||||
if (showChooseOwnersButton) {
|
||||
Button(
|
||||
text = stringResource(R.string.screen_leave_space_choose_owners_action),
|
||||
onClick = onChooseOwnersButtonClick,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
destructive = true,
|
||||
)
|
||||
}
|
||||
TextButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(CommonStrings.action_cancel),
|
||||
@@ -262,6 +275,7 @@ private fun SpaceItem(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = 66.dp)
|
||||
.padding(horizontal = 16.dp)
|
||||
.toggleable(
|
||||
value = selectableSpaceRoom.isSelected,
|
||||
role = Role.Checkbox,
|
||||
@@ -276,9 +290,9 @@ private fun SpaceItem(
|
||||
onClick = onClick,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
Avatar(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
avatarData = room.getAvatarData(AvatarSize.LeaveSpaceRoom),
|
||||
avatarType = if (room.isSpace) AvatarType.Space() else AvatarType.Room(),
|
||||
)
|
||||
@@ -358,5 +372,6 @@ internal fun LeaveSpaceViewPreview(
|
||||
state = state,
|
||||
onCancel = {},
|
||||
onRolesAndPermissionsClick = {},
|
||||
onChooseOwnersClick = {},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -13,5 +13,6 @@ import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
||||
data class SelectableSpaceRoom(
|
||||
val spaceRoom: SpaceRoom,
|
||||
val isLastOwner: Boolean,
|
||||
val joinedMembersCount: Int,
|
||||
val isSelected: Boolean,
|
||||
)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_leave_space_choose_owners_action">"Choose owners"</string>
|
||||
<string name="screen_leave_space_last_admin_info">"%1$s (Admin)"</string>
|
||||
<plurals name="screen_leave_space_submit">
|
||||
<item quantity="one">"Leave %1$d room and space"</item>
|
||||
@@ -7,9 +8,11 @@
|
||||
</plurals>
|
||||
<string name="screen_leave_space_subtitle">"Select the rooms you’d like to leave which you\'re not the only administrator for:"</string>
|
||||
<string name="screen_leave_space_subtitle_last_admin">"You need to assign another admin for this space before you can leave."</string>
|
||||
<string name="screen_leave_space_subtitle_last_owner">"You are the only owner of %1$s. You need to transfer ownership to someone else before you leave."</string>
|
||||
<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_leave_space_title_last_owner">"Transfer ownership"</string>
|
||||
<string name="screen_space_add_room_action">"Room"</string>
|
||||
<string name="screen_space_add_rooms_room_access_description">"Adding a room will not affect the room access. To change the access go to Room settings > Security & privacy."</string>
|
||||
<string name="screen_space_empty_state_title">"Add your first room"</string>
|
||||
|
||||
@@ -12,6 +12,7 @@ import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.testing.junit4.util.MainDispatcherRule
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.changeroommemberroles.test.FakeChangeRoomMemberRolesEntryPoint
|
||||
import io.element.android.features.createroom.api.FakeCreateRoomEntryPoint
|
||||
import io.element.android.features.space.api.SpaceEntryPoint
|
||||
import io.element.android.features.space.impl.di.FakeSpaceFlowGraph
|
||||
@@ -22,6 +23,7 @@ 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
|
||||
import io.element.android.tests.testutils.node.TestParentNode
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
@@ -33,7 +35,7 @@ class DefaultSpaceEntryPointTest {
|
||||
val mainDispatcherRule = MainDispatcherRule()
|
||||
|
||||
@Test
|
||||
fun `test node builder`() {
|
||||
fun `test node builder`() = runTest {
|
||||
val entryPoint = DefaultSpaceEntryPoint()
|
||||
val nodeInputs = SpaceEntryPoint.Inputs(A_ROOM_ID)
|
||||
val parentNode = TestParentNode.create { buildContext, plugins ->
|
||||
@@ -46,6 +48,8 @@ class DefaultSpaceEntryPointTest {
|
||||
room = FakeJoinedRoom(),
|
||||
graphFactory = FakeSpaceFlowGraph.Factory,
|
||||
createRoomEntryPoint = FakeCreateRoomEntryPoint(),
|
||||
changeRoomMemberRolesEntryPoint = FakeChangeRoomMemberRolesEntryPoint(),
|
||||
sessionCoroutineScope = backgroundScope,
|
||||
)
|
||||
}
|
||||
val callback = object : SpaceEntryPoint.Callback {
|
||||
|
||||
@@ -29,11 +29,6 @@ import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class LeaveSpacePresenterTest {
|
||||
private val aSpace = aSpaceRoom(
|
||||
roomId = A_SPACE_ID,
|
||||
displayName = A_SPACE_NAME,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `present - initial state`() = runTest {
|
||||
val presenter = createLeaveSpacePresenter(
|
||||
@@ -44,7 +39,7 @@ class LeaveSpacePresenterTest {
|
||||
presenter.test {
|
||||
val state = awaitItem()
|
||||
assertThat(state.spaceName).isNull()
|
||||
assertThat(state.isLastOwner).isFalse()
|
||||
assertThat(state.needsOwnerChange).isFalse()
|
||||
assertThat(state.selectableSpaceRooms.isLoading()).isTrue()
|
||||
assertThat(state.leaveSpaceAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
@@ -87,7 +82,7 @@ class LeaveSpacePresenterTest {
|
||||
skipItems(2)
|
||||
val finalState = awaitItem()
|
||||
assertThat(finalState.spaceName).isEqualTo(A_SPACE_NAME)
|
||||
assertThat(finalState.isLastOwner).isTrue()
|
||||
assertThat(finalState.needsOwnerChange).isTrue()
|
||||
// The current state is not in the sub room list
|
||||
assertThat(finalState.selectableSpaceRooms.dataOrNull()!!).isEmpty()
|
||||
}
|
||||
@@ -145,8 +140,8 @@ class LeaveSpacePresenterTest {
|
||||
roomsResult = {
|
||||
Result.success(
|
||||
listOf(
|
||||
LeaveSpaceRoom(aSpaceRoom(roomId = A_ROOM_ID), isLastOwner = false),
|
||||
LeaveSpaceRoom(aSpaceRoom(roomId = A_ROOM_ID_2), isLastOwner = true),
|
||||
LeaveSpaceRoom(aSpaceRoom(roomId = A_ROOM_ID), isLastOwner = false, areCreatorsPrivileged = false),
|
||||
LeaveSpaceRoom(aSpaceRoom(roomId = A_ROOM_ID_2), isLastOwner = true, areCreatorsPrivileged = false),
|
||||
)
|
||||
)
|
||||
},
|
||||
@@ -157,7 +152,7 @@ class LeaveSpacePresenterTest {
|
||||
skipItems(3)
|
||||
val state = awaitItem()
|
||||
assertThat(state.spaceName).isNull()
|
||||
assertThat(state.isLastOwner).isFalse()
|
||||
assertThat(state.needsOwnerChange).isFalse()
|
||||
val data = state.selectableSpaceRooms.dataOrNull()!!
|
||||
assertThat(data.size).isEqualTo(2)
|
||||
// Only one room is selectable as the user is the last admin in the other one
|
||||
@@ -232,6 +227,20 @@ class LeaveSpacePresenterTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - needsOwnerChange is false if user is the last joined member`() = runTest {
|
||||
val presenter = createLeaveSpacePresenter(
|
||||
leaveSpaceHandle = FakeLeaveSpaceHandle(
|
||||
roomsResult = { Result.success(listOf(aLeaveSpaceRoom(spaceRoom = aSpaceRoom(numJoinedMembers = 1), isLastOwner = true))) },
|
||||
)
|
||||
)
|
||||
presenter.test {
|
||||
skipItems(3)
|
||||
val state = awaitItem()
|
||||
assertThat(state.needsOwnerChange).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createLeaveSpacePresenter(
|
||||
leaveSpaceHandle: LeaveSpaceHandle = FakeLeaveSpaceHandle(),
|
||||
): LeaveSpacePresenter {
|
||||
@@ -241,13 +250,18 @@ class LeaveSpacePresenterTest {
|
||||
}
|
||||
}
|
||||
|
||||
private val aSpace = aSpaceRoom(
|
||||
roomId = A_SPACE_ID,
|
||||
displayName = A_SPACE_NAME,
|
||||
numJoinedMembers = 2,
|
||||
)
|
||||
|
||||
private fun aLeaveSpaceRoom(
|
||||
spaceRoom: SpaceRoom = aSpaceRoom(
|
||||
roomId = A_SPACE_ID,
|
||||
displayName = A_SPACE_NAME,
|
||||
),
|
||||
spaceRoom: SpaceRoom = aSpace,
|
||||
isLastOwner: Boolean = false,
|
||||
areCreatorsPrivileged: Boolean = false,
|
||||
) = LeaveSpaceRoom(
|
||||
spaceRoom = spaceRoom,
|
||||
isLastOwner = isLastOwner,
|
||||
areCreatorsPrivileged = areCreatorsPrivileged,
|
||||
)
|
||||
|
||||
@@ -11,4 +11,5 @@ package io.element.android.libraries.matrix.api.spaces
|
||||
data class LeaveSpaceRoom(
|
||||
val spaceRoom: SpaceRoom,
|
||||
val isLastOwner: Boolean,
|
||||
val areCreatorsPrivileged: Boolean,
|
||||
)
|
||||
|
||||
@@ -41,6 +41,7 @@ class RustLeaveSpaceHandle(
|
||||
LeaveSpaceRoom(
|
||||
spaceRoom = spaceRoomMapper.map(leaveSpaceRoom.spaceRoom),
|
||||
isLastOwner = leaveSpaceRoom.isLastOwner,
|
||||
areCreatorsPrivileged = leaveSpaceRoom.areCreatorsPrivileged,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user