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
@@ -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,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user