Skip to content

Commit 103743c

Browse files
authored
Merge pull request #2663 from element-hq/feature/bma/testChangeRolesView
Fix a bunch of small issues around moderation and test change roles view
2 parents 705490d + c0caa90 commit 103743c

File tree

26 files changed

+446
-106
lines changed

26 files changed

+446
-106
lines changed

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsPresenter.kt

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@ import io.element.android.libraries.architecture.AsyncAction
2929
import io.element.android.libraries.architecture.Presenter
3030
import io.element.android.libraries.architecture.runUpdatingState
3131
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
32+
import io.element.android.libraries.matrix.api.core.UserId
3233
import io.element.android.libraries.matrix.api.room.MatrixRoom
3334
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
3435
import io.element.android.libraries.matrix.api.room.RoomMember
36+
import io.element.android.libraries.matrix.api.room.joinedRoomMembers
3537
import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange
3638
import io.element.android.services.analytics.api.AnalyticsService
3739
import kotlinx.coroutines.CoroutineScope
@@ -47,14 +49,22 @@ class RolesAndPermissionsPresenter @Inject constructor(
4749
override fun present(): RolesAndPermissionsState {
4850
val coroutineScope = rememberCoroutineScope()
4951
val roomInfo by room.roomInfoFlow.collectAsState(initial = null)
52+
val roomMembers by room.membersStateFlow.collectAsState()
53+
// Get the list of joined room members, in order to filter members present in the power
54+
// level state Event, but not member of the room anymore.
55+
val joinedRoomMemberIds by remember {
56+
derivedStateOf {
57+
roomMembers.joinedRoomMembers().map { it.userId }
58+
}
59+
}
5060
val moderatorCount by remember {
5161
derivedStateOf {
52-
roomInfo.userCountWithRole(RoomMember.Role.MODERATOR)
62+
roomInfo.userCountWithRole(joinedRoomMemberIds, RoomMember.Role.MODERATOR)
5363
}
5464
}
5565
val adminCount by remember {
5666
derivedStateOf {
57-
roomInfo.userCountWithRole(RoomMember.Role.ADMIN)
67+
roomInfo.userCountWithRole(joinedRoomMemberIds, RoomMember.Role.ADMIN)
5868
}
5969
}
6070
val changeOwnRoleAction = remember { mutableStateOf<AsyncAction<Unit>>(AsyncAction.Uninitialized) }
@@ -108,11 +118,9 @@ class RolesAndPermissionsPresenter @Inject constructor(
108118
}
109119
}
110120

111-
private fun MatrixRoomInfo?.userCountWithRole(role: RoomMember.Role): Int {
112-
return if (this != null) {
113-
userPowerLevels.count { (_, level) -> RoomMember.Role.forPowerLevel(level) == role }
114-
} else {
115-
0
121+
private fun MatrixRoomInfo?.userCountWithRole(joinedRoomMemberIds: List<UserId>, role: RoomMember.Role): Int {
122+
return this?.userPowerLevels.orEmpty().count { (userId, level) ->
123+
RoomMember.Role.forPowerLevel(level) == role && userId in joinedRoomMemberIds
116124
}
117125
}
118126
}

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesEvent.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@
1616

1717
package io.element.android.features.roomdetails.impl.rolesandpermissions.changeroles
1818

19-
import io.element.android.libraries.matrix.api.room.RoomMember
19+
import io.element.android.libraries.matrix.api.user.MatrixUser
2020

2121
sealed interface ChangeRolesEvent {
2222
data object ToggleSearchActive : ChangeRolesEvent
2323
data class QueryChanged(val query: String?) : ChangeRolesEvent
24-
data class UserSelectionToggled(val roomMember: RoomMember) : ChangeRolesEvent
24+
data class UserSelectionToggled(val matrixUser: MatrixUser) : ChangeRolesEvent
2525
data object Save : ChangeRolesEvent
2626
data object Exit : ChangeRolesEvent
2727
data object CancelExit : ChangeRolesEvent

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesPresenter.kt

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ import io.element.android.libraries.matrix.api.core.UserId
4242
import io.element.android.libraries.matrix.api.room.MatrixRoom
4343
import io.element.android.libraries.matrix.api.room.RoomMember
4444
import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange
45+
import io.element.android.libraries.matrix.api.room.powerlevels.usersWithRole
46+
import io.element.android.libraries.matrix.api.room.toMatrixUser
4547
import io.element.android.libraries.matrix.api.user.MatrixUser
4648
import io.element.android.services.analytics.api.AnalyticsService
4749
import kotlinx.collections.immutable.ImmutableList
@@ -129,11 +131,11 @@ class ChangeRolesPresenter @AssistedInject constructor(
129131
}
130132
is ChangeRolesEvent.UserSelectionToggled -> {
131133
val newList = selectedUsers.value.toMutableList()
132-
val index = newList.indexOfFirst { it.userId == event.roomMember.userId }
134+
val index = newList.indexOfFirst { it.userId == event.matrixUser.userId }
133135
if (index >= 0) {
134136
newList.removeAt(index)
135137
} else {
136-
newList.add(event.roomMember.toMatrixUser())
138+
newList.add(event.matrixUser)
137139
}
138140
selectedUsers.value = newList.toImmutableList()
139141
}
@@ -183,12 +185,6 @@ class ChangeRolesPresenter @AssistedInject constructor(
183185
return sortedWith(PowerLevelRoomMemberComparator()).toImmutableList()
184186
}
185187

186-
private fun RoomMember.toMatrixUser() = MatrixUser(
187-
userId = userId,
188-
displayName = displayName,
189-
avatarUrl = avatarUrl,
190-
)
191-
192188
private fun CoroutineScope.save(
193189
usersWithRole: ImmutableList<MatrixUser>,
194190
selectedUsers: MutableState<ImmutableList<MatrixUser>>,

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesStateProvider.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ internal fun aChangeRolesState(
6262
exitState: AsyncAction<Unit> = AsyncAction.Uninitialized,
6363
savingState: AsyncAction<Unit> = AsyncAction.Uninitialized,
6464
canRemoveMember: (UserId) -> Boolean = { true },
65+
eventSink: (ChangeRolesEvent) -> Unit = {},
6566
) = ChangeRolesState(
6667
role = role,
6768
query = query,
@@ -72,7 +73,7 @@ internal fun aChangeRolesState(
7273
exitState = exitState,
7374
savingState = savingState,
7475
canChangeMemberRole = canRemoveMember,
75-
eventSink = {},
76+
eventSink = eventSink,
7677
)
7778

7879
internal fun aChangeRolesStateWithSelectedUsers() = aChangeRolesState(

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesView.kt

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
4646
import androidx.compose.ui.unit.dp
4747
import io.element.android.compound.theme.ElementTheme
4848
import io.element.android.features.roomdetails.impl.R
49-
import io.element.android.features.roomdetails.impl.members.aRoomMember
5049
import io.element.android.libraries.architecture.AsyncAction
5150
import io.element.android.libraries.designsystem.components.ProgressDialog
5251
import io.element.android.libraries.designsystem.components.async.AsyncActionView
@@ -68,6 +67,7 @@ import io.element.android.libraries.designsystem.theme.components.TextButton
6867
import io.element.android.libraries.designsystem.theme.components.TopAppBar
6968
import io.element.android.libraries.matrix.api.core.UserId
7069
import io.element.android.libraries.matrix.api.room.RoomMember
70+
import io.element.android.libraries.matrix.api.room.toMatrixUser
7171
import io.element.android.libraries.matrix.api.user.MatrixUser
7272
import io.element.android.libraries.matrix.ui.components.MatrixUserRow
7373
import io.element.android.libraries.matrix.ui.components.SelectedUsersRowList
@@ -83,12 +83,8 @@ fun ChangeRolesView(
8383
modifier: Modifier = Modifier,
8484
) {
8585
val updatedOnBackPressed by rememberUpdatedState(newValue = onBackPressed)
86-
BackHandler {
87-
if (state.isSearchActive) {
88-
state.eventSink(ChangeRolesEvent.ToggleSearchActive)
89-
} else {
90-
state.eventSink(ChangeRolesEvent.Exit)
91-
}
86+
BackHandler(enabled = !state.isSearchActive) {
87+
state.eventSink(ChangeRolesEvent.Exit)
9288
}
9389

9490
Box(modifier = modifier) {
@@ -129,7 +125,9 @@ fun ChangeRolesView(
129125
) {
130126
val lazyListState = rememberLazyListState()
131127
SearchBar(
132-
modifier = Modifier.padding(bottom = 16.dp),
128+
modifier = Modifier
129+
.fillMaxWidth()
130+
.padding(bottom = 16.dp),
133131
placeHolderTitle = stringResource(CommonStrings.common_search_for_someone),
134132
query = state.query.orEmpty(),
135133
onQueryChange = { state.eventSink(ChangeRolesEvent.QueryChanged(it)) },
@@ -143,7 +141,7 @@ fun ChangeRolesView(
143141
searchResults = members,
144142
selectedUsers = state.selectedUsers,
145143
canRemoveMember = state.canChangeMemberRole,
146-
onSelectionToggled = { state.eventSink(ChangeRolesEvent.UserSelectionToggled(it)) },
144+
onSelectionToggled = { state.eventSink(ChangeRolesEvent.UserSelectionToggled(it.toMatrixUser())) },
147145
selectedUsersList = {},
148146
)
149147
}
@@ -159,13 +157,13 @@ fun ChangeRolesView(
159157
searchResults = (state.searchResults as? SearchBarResultState.Results)?.results ?: persistentListOf(),
160158
selectedUsers = state.selectedUsers,
161159
canRemoveMember = state.canChangeMemberRole,
162-
onSelectionToggled = { state.eventSink(ChangeRolesEvent.UserSelectionToggled(it)) },
160+
onSelectionToggled = { state.eventSink(ChangeRolesEvent.UserSelectionToggled(it.toMatrixUser())) },
163161
selectedUsersList = { users ->
164162
SelectedUsersRowList(
165163
contentPadding = PaddingValues(start = 16.dp, end = 16.dp, bottom = 16.dp),
166164
selectedUsers = users,
167165
onUserRemoved = {
168-
state.eventSink(ChangeRolesEvent.UserSelectionToggled(aRoomMember(it.userId)))
166+
state.eventSink(ChangeRolesEvent.UserSelectionToggled(it))
169167
},
170168
canDeselect = { state.canChangeMemberRole(it.userId) },
171169
)

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsView.kt

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@
1717
package io.element.android.features.roomdetails.impl.rolesandpermissions.permissions
1818

1919
import androidx.activity.compose.BackHandler
20-
import androidx.compose.foundation.layout.Column
20+
import androidx.compose.foundation.layout.fillMaxSize
2121
import androidx.compose.foundation.layout.padding
22+
import androidx.compose.foundation.lazy.LazyColumn
2223
import androidx.compose.material3.ExperimentalMaterial3Api
2324
import androidx.compose.runtime.Composable
2425
import androidx.compose.ui.Modifier
@@ -80,29 +81,35 @@ fun ChangeRoomPermissionsView(
8081
)
8182
}
8283
) { padding ->
83-
Column(modifier = Modifier.padding(padding)) {
84+
LazyColumn(
85+
modifier = Modifier
86+
.padding(padding)
87+
.fillMaxSize()
88+
) {
8489
for ((index, permissionItem) in state.items.withIndex()) {
85-
ListSectionHeader(titleForSection(item = permissionItem), hasDivider = index > 0)
86-
SelectRoleItem(
87-
permissionsItem = permissionItem,
88-
role = RoomMember.Role.ADMIN,
89-
currentPermissions = state.currentPermissions
90-
) { item, role ->
91-
state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(item, role))
92-
}
93-
SelectRoleItem(
94-
permissionsItem = permissionItem,
95-
role = RoomMember.Role.MODERATOR,
96-
currentPermissions = state.currentPermissions
97-
) { item, role ->
98-
state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(item, role))
99-
}
100-
SelectRoleItem(
101-
permissionsItem = permissionItem,
102-
role = RoomMember.Role.USER,
103-
currentPermissions = state.currentPermissions
104-
) { item, role ->
105-
state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(item, role))
90+
item {
91+
ListSectionHeader(titleForSection(item = permissionItem), hasDivider = index > 0)
92+
SelectRoleItem(
93+
permissionsItem = permissionItem,
94+
role = RoomMember.Role.ADMIN,
95+
currentPermissions = state.currentPermissions
96+
) { item, role ->
97+
state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(item, role))
98+
}
99+
SelectRoleItem(
100+
permissionsItem = permissionItem,
101+
role = RoomMember.Role.MODERATOR,
102+
currentPermissions = state.currentPermissions
103+
) { item, role ->
104+
state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(item, role))
105+
}
106+
SelectRoleItem(
107+
permissionsItem = permissionItem,
108+
role = RoomMember.Role.USER,
109+
currentPermissions = state.currentPermissions
110+
) { item, role ->
111+
state.eventSink(ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(item, role))
112+
}
106113
}
107114
}
108115
}

features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/rolesandpermissions/changeroles/ChangeRolesPresenterTests.kt

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import app.cash.molecule.moleculeFlow
2121
import app.cash.turbine.test
2222
import com.google.common.truth.Truth.assertThat
2323
import im.vector.app.features.analytics.plan.RoomModeration
24-
import io.element.android.features.roomdetails.impl.members.aRoomMember
2524
import io.element.android.features.roomdetails.impl.members.aRoomMemberList
2625
import io.element.android.features.roomdetails.impl.rolesandpermissions.changeroles.ChangeRolesEvent
2726
import io.element.android.features.roomdetails.impl.rolesandpermissions.changeroles.ChangeRolesPresenter
@@ -30,6 +29,7 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers
3029
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
3130
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
3231
import io.element.android.libraries.matrix.api.room.RoomMember
32+
import io.element.android.libraries.matrix.api.user.MatrixUser
3333
import io.element.android.libraries.matrix.test.A_USER_ID
3434
import io.element.android.libraries.matrix.test.A_USER_ID_2
3535
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
@@ -154,10 +154,10 @@ class ChangeRolesPresenterTests {
154154
val initialState = awaitItem()
155155
assertThat(initialState.selectedUsers).hasSize(1)
156156

157-
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(aRoomMember(A_USER_ID_2)))
157+
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(MatrixUser(A_USER_ID_2)))
158158
assertThat(awaitItem().selectedUsers).hasSize(2)
159159

160-
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(aRoomMember(A_USER_ID_2)))
160+
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(MatrixUser(A_USER_ID_2)))
161161
assertThat(awaitItem().selectedUsers).hasSize(1)
162162
}
163163
}
@@ -177,13 +177,13 @@ class ChangeRolesPresenterTests {
177177
assertThat(initialState.hasPendingChanges).isFalse()
178178
assertThat(initialState.selectedUsers).hasSize(1)
179179

180-
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(aRoomMember(A_USER_ID_2)))
180+
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(MatrixUser(A_USER_ID_2)))
181181
with(awaitItem()) {
182182
assertThat(selectedUsers).hasSize(2)
183183
assertThat(hasPendingChanges).isTrue()
184184
}
185185

186-
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(aRoomMember(A_USER_ID_2)))
186+
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(MatrixUser(A_USER_ID_2)))
187187
with(awaitItem()) {
188188
assertThat(selectedUsers).hasSize(1)
189189
assertThat(hasPendingChanges).isFalse()
@@ -226,7 +226,7 @@ class ChangeRolesPresenterTests {
226226
assertThat(initialState.hasPendingChanges).isFalse()
227227
assertThat(initialState.exitState).isEqualTo(AsyncAction.Uninitialized)
228228

229-
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(aRoomMember(A_USER_ID_2)))
229+
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(MatrixUser(A_USER_ID_2)))
230230

231231
awaitItem().eventSink(ChangeRolesEvent.Exit)
232232
val confirmingState = awaitItem()
@@ -252,7 +252,7 @@ class ChangeRolesPresenterTests {
252252
assertThat(initialState.hasPendingChanges).isFalse()
253253
assertThat(initialState.exitState).isEqualTo(AsyncAction.Uninitialized)
254254

255-
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(aRoomMember(A_USER_ID_2)))
255+
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(MatrixUser(A_USER_ID_2)))
256256
val updatedState = awaitItem()
257257
assertThat(updatedState.hasPendingChanges).isTrue()
258258
skipItems(1)
@@ -279,8 +279,7 @@ class ChangeRolesPresenterTests {
279279
val initialState = awaitItem()
280280
assertThat(initialState.selectedUsers).hasSize(1)
281281

282-
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(aRoomMember(A_USER_ID_2)))
283-
282+
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(MatrixUser(A_USER_ID_2)))
284283
awaitItem().eventSink(ChangeRolesEvent.Save)
285284
val confirmingState = awaitItem()
286285
assertThat(confirmingState.savingState).isEqualTo(AsyncAction.Confirming)
@@ -304,7 +303,7 @@ class ChangeRolesPresenterTests {
304303
val initialState = awaitItem()
305304
assertThat(initialState.selectedUsers).hasSize(1)
306305

307-
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(aRoomMember(A_USER_ID_2)))
306+
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(MatrixUser(A_USER_ID_2)))
308307

309308
awaitItem().eventSink(ChangeRolesEvent.Save)
310309
val confirmingState = awaitItem()
@@ -334,7 +333,7 @@ class ChangeRolesPresenterTests {
334333
val initialState = awaitItem()
335334
assertThat(initialState.selectedUsers).hasSize(1)
336335

337-
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(aRoomMember(A_USER_ID_2)))
336+
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(MatrixUser(A_USER_ID_2)))
338337

339338
awaitItem().eventSink(ChangeRolesEvent.Save)
340339
assertThat(awaitItem().savingState).isEqualTo(AsyncAction.Success(Unit))
@@ -357,7 +356,7 @@ class ChangeRolesPresenterTests {
357356
val initialState = awaitItem()
358357
assertThat(initialState.selectedUsers).hasSize(1)
359358

360-
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(aRoomMember(A_USER_ID_2)))
359+
initialState.eventSink(ChangeRolesEvent.UserSelectionToggled(MatrixUser(A_USER_ID_2)))
361360

362361
awaitItem().eventSink(ChangeRolesEvent.Save)
363362
val failedState = awaitItem()

0 commit comments

Comments
 (0)