Sort the room member list and display member roles (#2412)
* Sort the room member list and display member roles --------- Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
parent
615ce72131
commit
87823fe8a4
22 changed files with 235 additions and 33 deletions
3
changelog.d/2256.feature
Normal file
3
changelog.d/2256.feature
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
Add moderation to rooms:
|
||||||
|
|
||||||
|
- Sort member in room member list by powerlevel, display their roles.
|
||||||
|
|
@ -27,7 +27,6 @@ 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.AvatarSize
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMembershipState
|
import io.element.android.libraries.matrix.api.room.RoomMembershipState
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||||
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
||||||
|
|
@ -38,6 +37,7 @@ import io.element.android.libraries.matrix.test.A_USER_ID
|
||||||
import io.element.android.libraries.matrix.test.A_USER_NAME
|
import io.element.android.libraries.matrix.test.A_USER_NAME
|
||||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||||
|
import io.element.android.libraries.matrix.test.room.aRoomMember
|
||||||
import io.element.android.libraries.matrix.test.room.aRoomSummaryDetails
|
import io.element.android.libraries.matrix.test.room.aRoomSummaryDetails
|
||||||
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
|
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
|
||||||
import io.element.android.libraries.push.api.notifications.NotificationDrawerManager
|
import io.element.android.libraries.push.api.notifications.NotificationDrawerManager
|
||||||
|
|
@ -431,7 +431,7 @@ class InviteListPresenterTests {
|
||||||
avatarUrl = null,
|
avatarUrl = null,
|
||||||
isDirect = false,
|
isDirect = false,
|
||||||
lastMessage = null,
|
lastMessage = null,
|
||||||
inviter = RoomMember(
|
inviter = aRoomMember(
|
||||||
userId = A_USER_ID,
|
userId = A_USER_ID,
|
||||||
displayName = A_USER_NAME,
|
displayName = A_USER_NAME,
|
||||||
avatarUrl = AN_AVATAR_URL,
|
avatarUrl = AN_AVATAR_URL,
|
||||||
|
|
@ -458,7 +458,7 @@ class InviteListPresenterTests {
|
||||||
avatarUrl = null,
|
avatarUrl = null,
|
||||||
isDirect = true,
|
isDirect = true,
|
||||||
lastMessage = null,
|
lastMessage = null,
|
||||||
inviter = RoomMember(
|
inviter = aRoomMember(
|
||||||
userId = A_USER_ID,
|
userId = A_USER_ID,
|
||||||
displayName = A_USER_NAME,
|
displayName = A_USER_NAME,
|
||||||
avatarUrl = AN_AVATAR_URL,
|
avatarUrl = AN_AVATAR_URL,
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,7 @@ internal fun MentionSuggestionsPickerView_Preview() {
|
||||||
powerLevel = 0L,
|
powerLevel = 0L,
|
||||||
normalizedPowerLevel = 0L,
|
normalizedPowerLevel = 0L,
|
||||||
isIgnored = false,
|
isIgnored = false,
|
||||||
|
role = RoomMember.Role.USER,
|
||||||
)
|
)
|
||||||
MentionSuggestionsPickerView(
|
MentionSuggestionsPickerView(
|
||||||
roomId = RoomId("!room:matrix.org"),
|
roomId = RoomId("!room:matrix.org"),
|
||||||
|
|
|
||||||
|
|
@ -103,5 +103,6 @@ private fun createDefaultRoomMemberForTyping(userId: UserId): RoomMember {
|
||||||
powerLevel = 0,
|
powerLevel = 0,
|
||||||
normalizedPowerLevel = 0,
|
normalizedPowerLevel = 0,
|
||||||
isIgnored = false,
|
isIgnored = false,
|
||||||
|
role = RoomMember.Role.USER,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -98,5 +98,6 @@ internal fun aTypingRoomMember(
|
||||||
powerLevel = 0,
|
powerLevel = 0,
|
||||||
normalizedPowerLevel = 0,
|
normalizedPowerLevel = 0,
|
||||||
isIgnored = false,
|
isIgnored = false,
|
||||||
|
role = RoomMember.Role.USER,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,6 @@ import io.element.android.libraries.featureflag.test.InMemorySessionPreferencesS
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMembershipState
|
|
||||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||||
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
||||||
import io.element.android.libraries.matrix.test.A_USER_ID_3
|
import io.element.android.libraries.matrix.test.A_USER_ID_3
|
||||||
|
|
@ -178,7 +176,6 @@ class TypingNotificationPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - reserveSpace becomes true once we get the first typing notification with room members`() = runTest {
|
fun `present - reserveSpace becomes true once we get the first typing notification with room members`() = runTest {
|
||||||
val aDefaultRoomMember = createDefaultRoomMember(A_USER_ID_2)
|
|
||||||
val room = FakeMatrixRoom()
|
val room = FakeMatrixRoom()
|
||||||
val presenter = createPresenter(matrixRoom = room)
|
val presenter = createPresenter(matrixRoom = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
|
@ -216,27 +213,17 @@ class TypingNotificationPresenterTest {
|
||||||
|
|
||||||
private fun createDefaultRoomMember(
|
private fun createDefaultRoomMember(
|
||||||
userId: UserId,
|
userId: UserId,
|
||||||
) = RoomMember(
|
) = aTypingRoomMember(
|
||||||
userId = userId,
|
userId = userId,
|
||||||
displayName = null,
|
displayName = null,
|
||||||
avatarUrl = null,
|
|
||||||
membership = RoomMembershipState.JOIN,
|
|
||||||
isNameAmbiguous = false,
|
isNameAmbiguous = false,
|
||||||
powerLevel = 0,
|
|
||||||
normalizedPowerLevel = 0,
|
|
||||||
isIgnored = false,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun createKnownRoomMember(
|
private fun createKnownRoomMember(
|
||||||
userId: UserId,
|
userId: UserId,
|
||||||
) = RoomMember(
|
) = aTypingRoomMember(
|
||||||
userId = userId,
|
userId = userId,
|
||||||
displayName = "Alice Doe",
|
displayName = "Alice Doe",
|
||||||
avatarUrl = "an_avatar_url",
|
|
||||||
membership = RoomMembershipState.JOIN,
|
|
||||||
isNameAmbiguous = true,
|
isNameAmbiguous = true,
|
||||||
powerLevel = 0,
|
|
||||||
normalizedPowerLevel = 0,
|
|
||||||
isIgnored = false,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ fun aDmRoomMember(
|
||||||
powerLevel: Long = 0,
|
powerLevel: Long = 0,
|
||||||
normalizedPowerLevel: Long = powerLevel,
|
normalizedPowerLevel: Long = powerLevel,
|
||||||
isIgnored: Boolean = false,
|
isIgnored: Boolean = false,
|
||||||
|
role: RoomMember.Role = RoomMember.Role.USER,
|
||||||
) = RoomMember(
|
) = RoomMember(
|
||||||
userId = userId,
|
userId = userId,
|
||||||
displayName = displayName,
|
displayName = displayName,
|
||||||
|
|
@ -64,6 +65,7 @@ fun aDmRoomMember(
|
||||||
powerLevel = powerLevel,
|
powerLevel = powerLevel,
|
||||||
normalizedPowerLevel = normalizedPowerLevel,
|
normalizedPowerLevel = normalizedPowerLevel,
|
||||||
isIgnored = isIgnored,
|
isIgnored = isIgnored,
|
||||||
|
role = role,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun aRoomDetailsState() = RoomDetailsState(
|
fun aRoomDetailsState() = RoomDetailsState(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.roomdetails.impl.members
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||||
|
import io.element.android.libraries.matrix.ui.room.sortingName
|
||||||
|
import java.text.Collator
|
||||||
|
|
||||||
|
// Comparator used to sort room members by power level (descending) and then by name (ascending)
|
||||||
|
internal class PowerLevelRoomMemberComparator : Comparator<RoomMember> {
|
||||||
|
// Used to simplify and compare unicode and ASCII chars (á == a)
|
||||||
|
private val collator = Collator.getInstance().apply {
|
||||||
|
decomposition = Collator.CANONICAL_DECOMPOSITION
|
||||||
|
}
|
||||||
|
override fun compare(o1: RoomMember, o2: RoomMember): Int {
|
||||||
|
return when {
|
||||||
|
o1.powerLevel > o2.powerLevel -> return -1
|
||||||
|
o1.powerLevel < o2.powerLevel -> return 1
|
||||||
|
else -> {
|
||||||
|
collator.compare(o1.sortingName(), o2.sortingName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -66,7 +66,9 @@ class RoomMemberListPresenter @Inject constructor(
|
||||||
roomMembers = AsyncData.Success(
|
roomMembers = AsyncData.Success(
|
||||||
RoomMembers(
|
RoomMembers(
|
||||||
invited = members.getOrDefault(RoomMembershipState.INVITE, emptyList()).toImmutableList(),
|
invited = members.getOrDefault(RoomMembershipState.INVITE, emptyList()).toImmutableList(),
|
||||||
joined = members.getOrDefault(RoomMembershipState.JOIN, emptyList()).toImmutableList(),
|
joined = members.getOrDefault(RoomMembershipState.JOIN, emptyList())
|
||||||
|
.sortedWith(PowerLevelRoomMemberComparator())
|
||||||
|
.toImmutableList(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -84,7 +86,9 @@ class RoomMemberListPresenter @Inject constructor(
|
||||||
SearchBarResultState.Results(
|
SearchBarResultState.Results(
|
||||||
RoomMembers(
|
RoomMembers(
|
||||||
invited = results.getOrDefault(RoomMembershipState.INVITE, emptyList()).toImmutableList(),
|
invited = results.getOrDefault(RoomMembershipState.INVITE, emptyList()).toImmutableList(),
|
||||||
joined = results.getOrDefault(RoomMembershipState.JOIN, emptyList()).toImmutableList(),
|
joined = results.getOrDefault(RoomMembershipState.JOIN, emptyList())
|
||||||
|
.sortedWith(PowerLevelRoomMemberComparator())
|
||||||
|
.toImmutableList(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ internal class RoomMemberListStateProvider : PreviewParameterProvider<RoomMember
|
||||||
roomMembers = AsyncData.Success(
|
roomMembers = AsyncData.Success(
|
||||||
RoomMembers(
|
RoomMembers(
|
||||||
invited = persistentListOf(aVictor(), aWalter()),
|
invited = persistentListOf(aVictor(), aWalter()),
|
||||||
joined = persistentListOf(anAlice(), aBob()),
|
joined = persistentListOf(anAlice(), aBob(), aWalter()),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|
@ -79,6 +79,7 @@ fun aRoomMember(
|
||||||
powerLevel: Long = 0L,
|
powerLevel: Long = 0L,
|
||||||
normalizedPowerLevel: Long = 0L,
|
normalizedPowerLevel: Long = 0L,
|
||||||
isIgnored: Boolean = false,
|
isIgnored: Boolean = false,
|
||||||
|
role: RoomMember.Role = RoomMember.Role.USER,
|
||||||
) = RoomMember(
|
) = RoomMember(
|
||||||
userId = userId,
|
userId = userId,
|
||||||
displayName = displayName,
|
displayName = displayName,
|
||||||
|
|
@ -88,6 +89,7 @@ fun aRoomMember(
|
||||||
powerLevel = powerLevel,
|
powerLevel = powerLevel,
|
||||||
normalizedPowerLevel = normalizedPowerLevel,
|
normalizedPowerLevel = normalizedPowerLevel,
|
||||||
isIgnored = isIgnored,
|
isIgnored = isIgnored,
|
||||||
|
role = role,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun aRoomMemberList() = persistentListOf(
|
fun aRoomMemberList() = persistentListOf(
|
||||||
|
|
@ -103,8 +105,8 @@ fun aRoomMemberList() = persistentListOf(
|
||||||
aWalter(),
|
aWalter(),
|
||||||
)
|
)
|
||||||
|
|
||||||
fun anAlice() = aRoomMember(UserId("@alice:server.org"), "Alice")
|
fun anAlice() = aRoomMember(UserId("@alice:server.org"), "Alice", role = RoomMember.Role.ADMIN)
|
||||||
fun aBob() = aRoomMember(UserId("@bob:server.org"), "Bob")
|
fun aBob() = aRoomMember(UserId("@bob:server.org"), "Bob", role = RoomMember.Role.MODERATOR)
|
||||||
|
|
||||||
fun aVictor() = aRoomMember(UserId("@victor:server.org"), "Victor", membership = RoomMembershipState.INVITE)
|
fun aVictor() = aRoomMember(UserId("@victor:server.org"), "Victor", membership = RoomMembershipState.INVITE)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -177,14 +177,28 @@ private fun RoomMemberListItem(
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
val roleText = when (roomMember.role) {
|
||||||
|
RoomMember.Role.ADMIN -> stringResource(R.string.screen_room_member_list_role_administrator)
|
||||||
|
RoomMember.Role.MODERATOR -> stringResource(R.string.screen_room_member_list_role_moderator)
|
||||||
|
RoomMember.Role.USER -> null
|
||||||
|
}
|
||||||
MatrixUserRow(
|
MatrixUserRow(
|
||||||
modifier = modifier.clickable(onClick = onClick),
|
modifier = modifier.clickable(onClick = onClick),
|
||||||
matrixUser = MatrixUser(
|
matrixUser = MatrixUser(
|
||||||
userId = roomMember.userId,
|
userId = roomMember.userId,
|
||||||
displayName = roomMember.displayName,
|
displayName = roomMember.displayName,
|
||||||
avatarUrl = roomMember.avatarUrl
|
avatarUrl = roomMember.avatarUrl,
|
||||||
),
|
),
|
||||||
avatarSize = AvatarSize.UserListItem,
|
avatarSize = AvatarSize.UserListItem,
|
||||||
|
trailingContent = roleText?.let {
|
||||||
|
@Composable {
|
||||||
|
Text(
|
||||||
|
text = it,
|
||||||
|
style = ElementTheme.typography.fontBodySmRegular,
|
||||||
|
color = ElementTheme.colors.textSecondary,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.roomdetails.members
|
||||||
|
|
||||||
|
import io.element.android.features.roomdetails.impl.members.PowerLevelRoomMemberComparator
|
||||||
|
import io.element.android.features.roomdetails.impl.members.aRoomMember
|
||||||
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
|
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||||
|
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
||||||
|
import io.element.android.libraries.matrix.test.A_USER_ID_3
|
||||||
|
import io.element.android.libraries.matrix.test.A_USER_ID_4
|
||||||
|
import io.element.android.libraries.matrix.test.A_USER_ID_5
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class PowerLevelRoomMemberComparatorTest {
|
||||||
|
@Test
|
||||||
|
fun `order is Admin, then Moderator, then User`() {
|
||||||
|
val memberList = listOf(
|
||||||
|
aRoomMember(userId = UserId("@admin:example.com"), powerLevel = 100),
|
||||||
|
aRoomMember(userId = UserId("@moderator:example.com"), powerLevel = 50),
|
||||||
|
aRoomMember(userId = UserId("@user:example.com"), powerLevel = 0),
|
||||||
|
).shuffled()
|
||||||
|
|
||||||
|
val ordered = memberList.sortedWith(PowerLevelRoomMemberComparator())
|
||||||
|
assert(ordered[0].userId == UserId("@admin:example.com"))
|
||||||
|
assert(ordered[1].userId == UserId("@moderator:example.com"))
|
||||||
|
assert(ordered[2].userId == UserId("@user:example.com"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `with the same power level, alphabetical ascending order for name is used`() {
|
||||||
|
val memberList = listOf(
|
||||||
|
aRoomMember(userId = A_USER_ID, displayName = "First - admin", powerLevel = 100),
|
||||||
|
aRoomMember(userId = A_USER_ID_2, displayName = "Second - admin", powerLevel = 100),
|
||||||
|
aRoomMember(userId = A_USER_ID_3, displayName = "Third - admin", powerLevel = 100),
|
||||||
|
aRoomMember(userId = A_USER_ID_4, displayName = "First - user", powerLevel = 0),
|
||||||
|
aRoomMember(userId = A_USER_ID_5, displayName = "Second - user", powerLevel = 0),
|
||||||
|
).shuffled()
|
||||||
|
|
||||||
|
val ordered = memberList.sortedWith(PowerLevelRoomMemberComparator())
|
||||||
|
assert(ordered[0].userId == A_USER_ID)
|
||||||
|
assert(ordered[1].userId == A_USER_ID_2)
|
||||||
|
assert(ordered[2].userId == A_USER_ID_3)
|
||||||
|
assert(ordered[3].userId == A_USER_ID_4)
|
||||||
|
assert(ordered[4].userId == A_USER_ID_5)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when no names are provided, alphabetical order uses user id`() {
|
||||||
|
val memberList = listOf(
|
||||||
|
aRoomMember(userId = A_USER_ID, displayName = "Z - LAST!", powerLevel = 100),
|
||||||
|
aRoomMember(userId = A_USER_ID_2, powerLevel = 100),
|
||||||
|
aRoomMember(userId = A_USER_ID_3, powerLevel = 100),
|
||||||
|
).shuffled()
|
||||||
|
|
||||||
|
val ordered = memberList.sortedWith(PowerLevelRoomMemberComparator())
|
||||||
|
assert(ordered[0].userId == A_USER_ID_2)
|
||||||
|
assert(ordered[1].userId == A_USER_ID_3)
|
||||||
|
assert(ordered[2].userId == A_USER_ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `unicode characters are simplified and compared, order ignores case`() {
|
||||||
|
val memberList = listOf(
|
||||||
|
aRoomMember(userId = A_USER_ID, displayName = "First", powerLevel = 100),
|
||||||
|
aRoomMember(userId = A_USER_ID_2, displayName = "Șecond", powerLevel = 100),
|
||||||
|
aRoomMember(userId = A_USER_ID_3, displayName = "third", powerLevel = 100),
|
||||||
|
).shuffled()
|
||||||
|
|
||||||
|
val ordered = memberList.sortedWith(PowerLevelRoomMemberComparator())
|
||||||
|
assert(ordered[0].userId == A_USER_ID)
|
||||||
|
assert(ordered[1].userId == A_USER_ID_2)
|
||||||
|
assert(ordered[2].userId == A_USER_ID_3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -27,7 +27,17 @@ data class RoomMember(
|
||||||
val powerLevel: Long,
|
val powerLevel: Long,
|
||||||
val normalizedPowerLevel: Long,
|
val normalizedPowerLevel: Long,
|
||||||
val isIgnored: Boolean,
|
val isIgnored: Boolean,
|
||||||
|
val role: Role,
|
||||||
) {
|
) {
|
||||||
|
/**
|
||||||
|
* Role of the RoomMember, based on its [powerLevel].
|
||||||
|
*/
|
||||||
|
enum class Role {
|
||||||
|
ADMIN,
|
||||||
|
MODERATOR,
|
||||||
|
USER
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disambiguated display name for the RoomMember.
|
* Disambiguated display name for the RoomMember.
|
||||||
* If the display name is null, the user ID is returned.
|
* If the display name is null, the user ID is returned.
|
||||||
|
|
@ -49,6 +59,10 @@ enum class RoomMembershipState {
|
||||||
LEAVE
|
LEAVE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the best name value to display for the RoomMember.
|
||||||
|
* If the [RoomMember.displayName] is present and not empty it'll be used, otherwise the [RoomMember.userId] will be used.
|
||||||
|
*/
|
||||||
fun RoomMember.getBestName(): String {
|
fun RoomMember.getBestName(): String {
|
||||||
return displayName?.takeIf { it.isNotEmpty() } ?: userId.value
|
return displayName?.takeIf { it.isNotEmpty() } ?: userId.value
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,5 +24,5 @@ import kotlinx.parcelize.Parcelize
|
||||||
data class MatrixUser(
|
data class MatrixUser(
|
||||||
val userId: UserId,
|
val userId: UserId,
|
||||||
val displayName: String? = null,
|
val displayName: String? = null,
|
||||||
val avatarUrl: String? = null
|
val avatarUrl: String? = null,
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package io.element.android.libraries.matrix.impl.room.member
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMembershipState
|
import io.element.android.libraries.matrix.api.room.RoomMembershipState
|
||||||
|
import uniffi.matrix_sdk.RoomMemberRole
|
||||||
import org.matrix.rustcomponents.sdk.MembershipState as RustMembershipState
|
import org.matrix.rustcomponents.sdk.MembershipState as RustMembershipState
|
||||||
import org.matrix.rustcomponents.sdk.RoomMember as RustRoomMember
|
import org.matrix.rustcomponents.sdk.RoomMember as RustRoomMember
|
||||||
|
|
||||||
|
|
@ -33,9 +34,17 @@ object RoomMemberMapper {
|
||||||
it.powerLevel(),
|
it.powerLevel(),
|
||||||
it.normalizedPowerLevel(),
|
it.normalizedPowerLevel(),
|
||||||
it.isIgnored(),
|
it.isIgnored(),
|
||||||
|
mapRole(it.suggestedRoleForPowerLevel())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun mapRole(role: RoomMemberRole): RoomMember.Role =
|
||||||
|
when (role) {
|
||||||
|
RoomMemberRole.ADMINISTRATOR -> RoomMember.Role.ADMIN
|
||||||
|
RoomMemberRole.MODERATOR -> RoomMember.Role.MODERATOR
|
||||||
|
RoomMemberRole.USER -> RoomMember.Role.USER
|
||||||
|
}
|
||||||
|
|
||||||
fun mapMembership(membershipState: RustMembershipState): RoomMembershipState =
|
fun mapMembership(membershipState: RustMembershipState): RoomMembershipState =
|
||||||
when (membershipState) {
|
when (membershipState) {
|
||||||
RustMembershipState.BAN -> RoomMembershipState.BAN
|
RustMembershipState.BAN -> RoomMembershipState.BAN
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ import org.matrix.rustcomponents.sdk.NoPointer
|
||||||
import org.matrix.rustcomponents.sdk.Room
|
import org.matrix.rustcomponents.sdk.Room
|
||||||
import org.matrix.rustcomponents.sdk.RoomMember
|
import org.matrix.rustcomponents.sdk.RoomMember
|
||||||
import org.matrix.rustcomponents.sdk.RoomMembersIterator
|
import org.matrix.rustcomponents.sdk.RoomMembersIterator
|
||||||
|
import uniffi.matrix_sdk.RoomMemberRole
|
||||||
|
|
||||||
class RoomMemberListFetcherTest {
|
class RoomMemberListFetcherTest {
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -268,6 +269,7 @@ class FakeRustRoomMember(
|
||||||
private val membership: MembershipState = MembershipState.JOIN,
|
private val membership: MembershipState = MembershipState.JOIN,
|
||||||
private val isNameAmbiguous: Boolean = false,
|
private val isNameAmbiguous: Boolean = false,
|
||||||
private val powerLevel: Long = 0L,
|
private val powerLevel: Long = 0L,
|
||||||
|
private val role: RoomMemberRole = RoomMemberRole.USER,
|
||||||
) : RoomMember(NoPointer) {
|
) : RoomMember(NoPointer) {
|
||||||
override fun userId(): String {
|
override fun userId(): String {
|
||||||
return userId.value
|
return userId.value
|
||||||
|
|
@ -300,4 +302,8 @@ class FakeRustRoomMember(
|
||||||
override fun isIgnored(): Boolean {
|
override fun isIgnored(): Boolean {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun suggestedRoleForPowerLevel(): RoomMemberRole {
|
||||||
|
return role
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ fun aRoomMember(
|
||||||
powerLevel: Long = 0L,
|
powerLevel: Long = 0L,
|
||||||
normalizedPowerLevel: Long = 0L,
|
normalizedPowerLevel: Long = 0L,
|
||||||
isIgnored: Boolean = false,
|
isIgnored: Boolean = false,
|
||||||
|
role: RoomMember.Role = RoomMember.Role.USER,
|
||||||
) = RoomMember(
|
) = RoomMember(
|
||||||
userId = userId,
|
userId = userId,
|
||||||
displayName = displayName,
|
displayName = displayName,
|
||||||
|
|
@ -38,4 +39,5 @@ fun aRoomMember(
|
||||||
powerLevel = powerLevel,
|
powerLevel = powerLevel,
|
||||||
normalizedPowerLevel = normalizedPowerLevel,
|
normalizedPowerLevel = normalizedPowerLevel,
|
||||||
isIgnored = isIgnored,
|
isIgnored = isIgnored,
|
||||||
|
role = role,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.matrix.ui.room
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name value to use when sorting room members.
|
||||||
|
*
|
||||||
|
* If the display name is not null and not empty, it is returned.
|
||||||
|
* Otherwise, the user ID is returned without the initial "@".
|
||||||
|
*/
|
||||||
|
fun RoomMember.sortingName(): String {
|
||||||
|
return displayName?.takeIf { it.isNotEmpty() } ?: userId.value.drop(1)
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:eba9e3f6b232813ccb2c42eb9ede3b594769619e4ce8f13b9d43c53688da070b
|
oid sha256:76bd5b2b4e4277cd9f87c8d39ec7490cde4d9de0f1f99cde8cd9061c19a9f34f
|
||||||
size 38958
|
size 47105
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:839119e003cdbccea31c8a19ee50248890a3bc042a0f820163ce5fdd7411f193
|
oid sha256:da396a8bdd3b9124c0b896e0dd79e7835091041b0544041421bf25b7f2edcaef
|
||||||
size 25282
|
size 25960
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:372179764b951cb9074a0e573241402ec2137045f26d9ffea1d567b93377c624
|
oid sha256:02874e8bf08b040dc154b81e579c602e023abd99b2af8732a9e322921042ac8d
|
||||||
size 38233
|
size 46597
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:6ac113aa7a641c20d9c725078509de96e6878adf95e7ff456d77c309f263a16f
|
oid sha256:3f764f7ca578de87f854d4bd090bc642a851f4ce85deee91c0ca77e3d4a5f582
|
||||||
size 24936
|
size 25653
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue