Room details: don't allow edits in DMs
If the room is a DM, we won't allow any editing functionality regardless of power levels. If there is no topic set, then the entire section is hidden, like in rooms without a topic where you lack the power level to change it. Closes #799
This commit is contained in:
parent
2b901f2678
commit
0f8cf50244
2 changed files with 68 additions and 18 deletions
|
|
@ -22,7 +22,7 @@ import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.produceState
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import io.element.android.features.leaveroom.api.LeaveRoomEvent
|
import io.element.android.features.leaveroom.api.LeaveRoomEvent
|
||||||
import io.element.android.features.leaveroom.api.LeaveRoomPresenter
|
import io.element.android.features.leaveroom.api.LeaveRoomPresenter
|
||||||
|
|
@ -55,14 +55,14 @@ class RoomDetailsPresenter @Inject constructor(
|
||||||
val canEditTopic by getCanSendStateEvent(membersState, StateEventType.ROOM_TOPIC)
|
val canEditTopic by getCanSendStateEvent(membersState, StateEventType.ROOM_TOPIC)
|
||||||
val dmMember by room.getDirectRoomMember(membersState)
|
val dmMember by room.getDirectRoomMember(membersState)
|
||||||
val roomMemberDetailsPresenter = roomMemberDetailsPresenter(dmMember)
|
val roomMemberDetailsPresenter = roomMemberDetailsPresenter(dmMember)
|
||||||
val roomType = getRoomType(dmMember)
|
val roomType by getRoomType(dmMember)
|
||||||
|
|
||||||
val topicState = remember(canEditTopic, room.topic) {
|
val topicState = remember(canEditTopic, room.topic, roomType) {
|
||||||
val topic = room.topic
|
val topic = room.topic
|
||||||
|
|
||||||
when {
|
when {
|
||||||
!topic.isNullOrBlank() -> RoomTopicState.ExistingTopic(topic)
|
!topic.isNullOrBlank() -> RoomTopicState.ExistingTopic(topic)
|
||||||
canEditTopic -> RoomTopicState.CanAddTopic
|
canEditTopic && roomType is RoomDetailsType.Room -> RoomTopicState.CanAddTopic
|
||||||
else -> RoomTopicState.Hidden
|
else -> RoomTopicState.Hidden
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -85,8 +85,8 @@ class RoomDetailsPresenter @Inject constructor(
|
||||||
memberCount = room.joinedMemberCount,
|
memberCount = room.joinedMemberCount,
|
||||||
isEncrypted = room.isEncrypted,
|
isEncrypted = room.isEncrypted,
|
||||||
canInvite = canInvite,
|
canInvite = canInvite,
|
||||||
canEdit = canEditAvatar || canEditName || canEditTopic,
|
canEdit = (canEditAvatar || canEditName || canEditTopic) && roomType == RoomDetailsType.Room,
|
||||||
roomType = roomType.value,
|
roomType = roomType,
|
||||||
roomMemberDetailsState = roomMemberDetailsState,
|
roomMemberDetailsState = roomMemberDetailsState,
|
||||||
leaveRoomState = leaveRoomState,
|
leaveRoomState = leaveRoomState,
|
||||||
eventSink = ::handleEvents,
|
eventSink = ::handleEvents,
|
||||||
|
|
@ -112,20 +112,12 @@ class RoomDetailsPresenter @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun getCanInvite(membersState: MatrixRoomMembersState): State<Boolean> {
|
private fun getCanInvite(membersState: MatrixRoomMembersState) = produceState(false, membersState) {
|
||||||
val canInvite = remember(membersState) { mutableStateOf(false) }
|
value = room.canInvite().getOrElse { false }
|
||||||
LaunchedEffect(membersState) {
|
|
||||||
canInvite.value = room.canInvite().getOrElse { false }
|
|
||||||
}
|
|
||||||
return canInvite
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun getCanSendStateEvent(membersState: MatrixRoomMembersState, type: StateEventType): State<Boolean> {
|
private fun getCanSendStateEvent(membersState: MatrixRoomMembersState, type: StateEventType) = produceState(false, membersState) {
|
||||||
val canSendEvent = remember(membersState) { mutableStateOf(false) }
|
value = room.canSendStateEvent(type).getOrElse { false }
|
||||||
LaunchedEffect(membersState) {
|
|
||||||
canSendEvent.value = room.canSendStateEvent(type).getOrElse { false }
|
|
||||||
}
|
|
||||||
return canSendEvent
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -173,6 +173,64 @@ class RoomDetailsPresenterTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `present - initial state when user can edit attributes in a DM`() = runTest {
|
||||||
|
val myRoomMember = aRoomMember(A_SESSION_ID)
|
||||||
|
val otherRoomMember = aRoomMember(A_USER_ID_2)
|
||||||
|
val room = aMatrixRoom(
|
||||||
|
isEncrypted = true,
|
||||||
|
isDirect = true,
|
||||||
|
).apply {
|
||||||
|
val roomMembers = listOf(myRoomMember, otherRoomMember)
|
||||||
|
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
|
||||||
|
|
||||||
|
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true))
|
||||||
|
givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(true))
|
||||||
|
givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(true))
|
||||||
|
givenCanInviteResult(Result.success(false))
|
||||||
|
}
|
||||||
|
val presenter = aRoomDetailsPresenter(room)
|
||||||
|
moleculeFlow(RecompositionClock.Immediate) {
|
||||||
|
presenter.present()
|
||||||
|
}.test {
|
||||||
|
// Initially false
|
||||||
|
assertThat(awaitItem().canEdit).isFalse()
|
||||||
|
// Then the asynchronous check completes, but editing is still disallowed because it's a DM
|
||||||
|
val settledState = awaitItem()
|
||||||
|
assertThat(settledState.canEdit).isFalse()
|
||||||
|
// If there is a topic, it's visible
|
||||||
|
assertThat(settledState.roomTopic).isEqualTo(RoomTopicState.ExistingTopic(room.topic!!))
|
||||||
|
|
||||||
|
cancelAndIgnoreRemainingEvents()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
fun `present - initial state when in a DM with no topic`() = runTest {
|
||||||
|
val myRoomMember = aRoomMember(A_SESSION_ID)
|
||||||
|
val otherRoomMember = aRoomMember(A_USER_ID_2)
|
||||||
|
val room = aMatrixRoom(
|
||||||
|
isEncrypted = true,
|
||||||
|
isDirect = true,
|
||||||
|
topic = null,
|
||||||
|
).apply {
|
||||||
|
val roomMembers = listOf(myRoomMember, otherRoomMember)
|
||||||
|
givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers))
|
||||||
|
|
||||||
|
givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true))
|
||||||
|
}
|
||||||
|
val presenter = aRoomDetailsPresenter(room)
|
||||||
|
moleculeFlow(RecompositionClock.Immediate) {
|
||||||
|
presenter.present()
|
||||||
|
}.test {
|
||||||
|
skipItems(1)
|
||||||
|
|
||||||
|
// There's no topic, so we hide the entire UI for DMs
|
||||||
|
assertThat(awaitItem().roomTopic).isEqualTo(RoomTopicState.Hidden)
|
||||||
|
|
||||||
|
cancelAndIgnoreRemainingEvents()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial state when user can edit all attributes`() = runTest {
|
fun `present - initial state when user can edit all attributes`() = runTest {
|
||||||
val room = aMatrixRoom().apply {
|
val room = aMatrixRoom().apply {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue