diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt index bb6cb823d2..df5247188a 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt @@ -23,6 +23,7 @@ import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runUpdatingStateNoSuccess import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.api.roomlist.RoomListService @@ -49,6 +50,10 @@ class EditDefaultNotificationSettingPresenter( fun create(oneToOne: Boolean): EditDefaultNotificationSettingPresenter } + private val collator = Collator.getInstance().apply { + decomposition = Collator.CANONICAL_DECOMPOSITION + } + @Composable override fun present(): EditDefaultNotificationSettingState { var displayMentionsOnlyDisclaimer by remember { mutableStateOf(false) } @@ -121,10 +126,10 @@ class EditDefaultNotificationSettingPresenter( summaries: List, roomsWithUserDefinedMode: MutableState> ) { - val roomWithUserDefinedRules: Set = notificationSettingsService.getRoomsWithUserDefinedRules().getOrDefault(emptyList()).toSet() + val roomWithUserDefinedRules: Set = notificationSettingsService.getRoomsWithUserDefinedRules().getOrDefault(emptyList()).toSet() roomsWithUserDefinedMode.value = summaries .filter { roomSummary -> - roomWithUserDefinedRules.contains(roomSummary.roomId.value) && roomSummary.isOneToOne == isOneToOne + roomWithUserDefinedRules.contains(roomSummary.roomId) && roomSummary.isOneToOne == isOneToOne } .map { roomSummary -> EditNotificationSettingRoomInfo( @@ -138,7 +143,12 @@ class EditDefaultNotificationSettingPresenter( ) } // locale sensitive sorting - .sortedWith(compareBy(Collator.getInstance()) { roomSummary -> roomSummary.name }) + .sortedWith( + compareBy(collator) { roomSummary -> + // Collator does not handle null values, so we provide a fallback + roomSummary.name ?: roomSummary.roomId.value + } + ) } private fun CoroutineScope.setDefaultNotificationMode(mode: RoomNotificationMode, action: MutableState>) = launch { diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTest.kt index e7d352b453..153c265560 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTest.kt @@ -8,33 +8,34 @@ package io.element.android.features.preferences.impl.notifications -import app.cash.molecule.RecompositionMode -import app.cash.molecule.moleculeFlow -import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.preferences.impl.notifications.edit.EditDefaultNotificationSettingPresenter import io.element.android.features.preferences.impl.notifications.edit.EditDefaultNotificationSettingStateEvents import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.test.AN_EXCEPTION +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.A_ROOM_ID_2 import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService import io.element.android.libraries.matrix.test.room.aRoomSummary import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService import io.element.android.tests.testutils.awaitLastSequentialItem import io.element.android.tests.testutils.consumeItemsUntilPredicate +import io.element.android.tests.testutils.test import kotlinx.coroutines.test.runTest import org.junit.Test class EditDefaultNotificationSettingsPresenterTest { @Test fun `present - ensures initial state is correct`() = runTest { - val notificationSettingsService = FakeNotificationSettingsService() + val notificationSettingsService = FakeNotificationSettingsService( + getRoomsWithUserDefinedRulesResult = { Result.success(emptyList()) }, + ) val presenter = createEditDefaultNotificationSettingPresenter(notificationSettingsService) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() assertThat(initialState.mode).isNull() assertThat(initialState.isOneToOne).isFalse() + assertThat(initialState.roomsWithUserDefinedMode).isEmpty() val loadedState = consumeItemsUntilPredicate { it.mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY @@ -49,13 +50,12 @@ class EditDefaultNotificationSettingsPresenterTest { fun `present - ensure list of rooms with user defined mode`() = runTest { val notificationSettingsService = FakeNotificationSettingsService( initialRoomMode = RoomNotificationMode.ALL_MESSAGES, - initialRoomModeIsDefault = false + initialRoomModeIsDefault = false, + getRoomsWithUserDefinedRulesResult = { Result.success(listOf(A_ROOM_ID)) }, ) val roomListService = FakeRoomListService() val presenter = createEditDefaultNotificationSettingPresenter(notificationSettingsService, roomListService) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { roomListService.postAllRooms(listOf(aRoomSummary(userDefinedNotificationMode = RoomNotificationMode.ALL_MESSAGES))) val loadedState = consumeItemsUntilPredicate { state -> state.roomsWithUserDefinedMode.any { it.notificationMode == RoomNotificationMode.ALL_MESSAGES } @@ -64,12 +64,78 @@ class EditDefaultNotificationSettingsPresenterTest { } } + @Test + fun `present - ensure list of rooms is sorted`() = runTest { + val notificationSettingsService = FakeNotificationSettingsService( + initialRoomMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY, + initialRoomModeIsDefault = false, + getRoomsWithUserDefinedRulesResult = { Result.success(listOf(A_ROOM_ID, A_ROOM_ID_2)) }, + ) + val roomListService = FakeRoomListService() + val presenter = createEditDefaultNotificationSettingPresenter(notificationSettingsService, roomListService) + presenter.test { + roomListService.postAllRooms( + listOf( + aRoomSummary( + roomId = A_ROOM_ID, + name = "Z", + userDefinedNotificationMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY, + ), + aRoomSummary( + roomId = A_ROOM_ID_2, + name = "A", + userDefinedNotificationMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY, + ), + ), + ) + val loadedState = consumeItemsUntilPredicate { state -> + state.roomsWithUserDefinedMode.any { it.notificationMode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY } + }.last() + assertThat(loadedState.roomsWithUserDefinedMode[0].name).isEqualTo("A") + assertThat(loadedState.roomsWithUserDefinedMode[1].name).isEqualTo("Z") + } + } + + @Test + fun `present - ensure list of rooms is sorted, with name null`() = runTest { + val notificationSettingsService = FakeNotificationSettingsService( + initialRoomMode = RoomNotificationMode.MUTE, + initialRoomModeIsDefault = false, + getRoomsWithUserDefinedRulesResult = { Result.success(listOf(A_ROOM_ID, A_ROOM_ID_2)) }, + ) + val roomListService = FakeRoomListService() + val presenter = createEditDefaultNotificationSettingPresenter(notificationSettingsService, roomListService) + presenter.test { + roomListService.postAllRooms( + listOf( + aRoomSummary( + roomId = A_ROOM_ID, + name = "Z", + userDefinedNotificationMode = RoomNotificationMode.MUTE, + ), + aRoomSummary( + roomId = A_ROOM_ID_2, + name = null, + userDefinedNotificationMode = RoomNotificationMode.MUTE, + ), + ), + ) + val loadedState = consumeItemsUntilPredicate { state -> + state.roomsWithUserDefinedMode.any { it.notificationMode == RoomNotificationMode.MUTE } + }.last() + assertThat(loadedState.roomsWithUserDefinedMode[0].name).isNull() + assertThat(loadedState.roomsWithUserDefinedMode[1].name).isEqualTo("Z") + } + } + @Test fun `present - edit default notification setting`() = runTest { - val presenter = createEditDefaultNotificationSettingPresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + val presenter = createEditDefaultNotificationSettingPresenter( + notificationSettingsService = FakeNotificationSettingsService( + getRoomsWithUserDefinedRulesResult = { Result.success(emptyList()) }, + ), + ) + presenter.test { awaitItem().eventSink(EditDefaultNotificationSettingStateEvents.SetNotificationMode(RoomNotificationMode.ALL_MESSAGES)) val loadedState = consumeItemsUntilPredicate { it.mode == RoomNotificationMode.ALL_MESSAGES @@ -80,12 +146,12 @@ class EditDefaultNotificationSettingsPresenterTest { @Test fun `present - edit default notification setting failed`() = runTest { - val notificationSettingsService = FakeNotificationSettingsService() + val notificationSettingsService = FakeNotificationSettingsService( + getRoomsWithUserDefinedRulesResult = { Result.success(emptyList()) }, + ) val presenter = createEditDefaultNotificationSettingPresenter(notificationSettingsService) notificationSettingsService.givenSetDefaultNotificationModeError(AN_EXCEPTION) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { awaitItem().eventSink(EditDefaultNotificationSettingStateEvents.SetNotificationMode(RoomNotificationMode.ALL_MESSAGES)) val errorState = consumeItemsUntilPredicate { it.changeNotificationSettingAction.isFailure() @@ -101,13 +167,13 @@ class EditDefaultNotificationSettingsPresenterTest { @Test fun `present - display mentions only warning if homeserver does not support it`() = runTest { - val notificationSettingsService = FakeNotificationSettingsService().apply { + val notificationSettingsService = FakeNotificationSettingsService( + getRoomsWithUserDefinedRulesResult = { Result.success(emptyList()) }, + ).apply { givenCanHomeServerPushEncryptedEventsToDeviceResult(Result.success(false)) } val presenter = createEditDefaultNotificationSettingPresenter(notificationSettingsService) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { assertThat(awaitLastSequentialItem().displayMentionsOnlyDisclaimer).isTrue() } } diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt index 2b02cdfdb2..86012462ce 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesPresenter.kt @@ -61,6 +61,8 @@ class ChangeRolesPresenter( fun create(role: RoomMember.Role): ChangeRolesPresenter } + private val powerLevelRoomMemberComparator = PowerLevelRoomMemberComparator() + @Composable override fun present(): ChangeRolesState { val dataSource = remember { RoomMemberListDataSource(room, dispatchers) } @@ -176,17 +178,7 @@ class ChangeRolesPresenter( } private fun List.groupedByRole(): MembersByRole { - val groupedMembers = MembersByRole(this) - return MembersByRole( - owners = groupedMembers.owners.sorted(), - admins = groupedMembers.admins.sorted(), - moderators = groupedMembers.moderators.sorted(), - members = groupedMembers.members.sorted(), - ) - } - - private fun Iterable.sorted(): ImmutableList { - return sortedWith(PowerLevelRoomMemberComparator()).toImmutableList() + return MembersByRole(this, powerLevelRoomMemberComparator) } private fun CoroutineScope.save( diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesState.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesState.kt index 45ab913a31..71fef01fa7 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesState.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesState.kt @@ -13,8 +13,8 @@ import io.element.android.libraries.designsystem.theme.components.SearchBarResul 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.user.MatrixUser -import io.element.android.libraries.matrix.ui.room.PowerLevelRoomMemberComparator import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList data class ChangeRolesState( @@ -30,21 +30,24 @@ data class ChangeRolesState( ) data class MembersByRole( - val owners: ImmutableList, - val admins: ImmutableList, - val moderators: ImmutableList, - val members: ImmutableList, + val owners: ImmutableList = persistentListOf(), + val admins: ImmutableList = persistentListOf(), + val moderators: ImmutableList = persistentListOf(), + val members: ImmutableList = persistentListOf(), ) { - constructor(members: List) : this( - owners = members.filter { it.role is RoomMember.Role.Owner }.sorted(), - admins = members.filter { it.role == RoomMember.Role.Admin }.sorted(), - moderators = members.filter { it.role == RoomMember.Role.Moderator }.sorted(), - members = members.filter { it.role == RoomMember.Role.User }.sorted(), + constructor(members: List, comparator: Comparator) : this( + owners = members.filterAndSort(comparator) { it.role is RoomMember.Role.Owner }, + admins = members.filterAndSort(comparator) { it.role == RoomMember.Role.Admin }, + moderators = members.filterAndSort(comparator) { it.role == RoomMember.Role.Moderator }, + members = members.filterAndSort(comparator) { it.role == RoomMember.Role.User }, ) fun isEmpty() = owners.isEmpty() && admins.isEmpty() && moderators.isEmpty() && members.isEmpty() } -private fun Iterable.sorted(): ImmutableList { - return sortedWith(PowerLevelRoomMemberComparator()).toImmutableList() +private fun Iterable.filterAndSort( + comparator: Comparator, + predicate: (RoomMember) -> Boolean, +): ImmutableList { + return filter(predicate).sortedWith(comparator).toImmutableList() } diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesStateProvider.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesStateProvider.kt index 14662bacdd..df74b71ae1 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesStateProvider.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesStateProvider.kt @@ -17,6 +17,7 @@ import io.element.android.libraries.matrix.api.room.RoomMembershipState import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.libraries.matrix.ui.components.aMatrixUserList +import io.element.android.libraries.matrix.ui.room.PowerLevelRoomMemberComparator import io.element.android.libraries.previewutils.room.aRoomMember import io.element.android.libraries.previewutils.room.aRoomMemberList import kotlinx.collections.immutable.ImmutableList @@ -36,7 +37,12 @@ class ChangeRolesStateProvider : PreviewParameterProvider { aChangeRolesStateWithSelectedUsers().copy( query = "Alice", isSearchActive = true, - searchResults = SearchBarResultState.Results(MembersByRole(aRoomMemberList().take(1).toImmutableList())), + searchResults = SearchBarResultState.Results( + MembersByRole( + members = aRoomMemberList().take(1), + comparator = PowerLevelRoomMemberComparator(), + ) + ), selectedUsers = aMatrixUserList().take(1).toImmutableList(), ), aChangeRolesStateWithSelectedUsers().copy(savingState = AsyncAction.ConfirmingCancellation), @@ -87,7 +93,8 @@ internal fun aChangeRolesStateWithSelectedUsers() = aChangeRolesState( } else { roomMember } - } + }, + comparator = PowerLevelRoomMemberComparator(), ) ), hasPendingChanges = true, @@ -126,7 +133,8 @@ internal fun aChangeRolesStateWithOwners( displayName = "David", role = RoomMember.Role.User, ), - ) + ), + comparator = PowerLevelRoomMemberComparator(), ), ), canRemoveMember = { userId -> diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesView.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesView.kt index 81b11aa837..3a981b66b6 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesView.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesView.kt @@ -145,7 +145,7 @@ fun ChangeRolesView( SearchResultsList( currentRole = state.role, lazyListState = lazyListState, - searchResults = (state.searchResults as? SearchBarResultState.Results)?.results ?: MembersByRole(emptyList()), + searchResults = (state.searchResults as? SearchBarResultState.Results)?.results ?: MembersByRole(), selectedUsers = state.selectedUsers, canRemoveMember = state.canChangeMemberRole, onToggleSelection = { state.eventSink(ChangeRolesEvent.UserSelectionToggled(it.toMatrixUser())) }, diff --git a/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/MembersByRoleTest.kt b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/MembersByRoleTest.kt index 2e1d18a6ca..66a06cddc3 100644 --- a/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/MembersByRoleTest.kt +++ b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/MembersByRoleTest.kt @@ -18,6 +18,7 @@ import io.element.android.libraries.matrix.test.A_USER_ID_5 import io.element.android.libraries.matrix.test.A_USER_ID_6 import io.element.android.libraries.matrix.test.A_USER_ID_7 import io.element.android.libraries.matrix.test.room.aRoomMember +import io.element.android.libraries.matrix.ui.room.PowerLevelRoomMemberComparator import kotlinx.collections.immutable.persistentListOf import org.junit.Test @@ -33,7 +34,7 @@ class MembersByRoleTest { aRoomMember(A_USER_ID_6, displayName = "Justin", role = RoomMember.Role.Owner(isCreator = true)), aRoomMember(A_USER_ID_7, displayName = "Mallory", role = RoomMember.Role.Owner(isCreator = false)), ) - val membersByRole = MembersByRole(members = members) + val membersByRole = MembersByRole(members = members, comparator = PowerLevelRoomMemberComparator()) assertThat(membersByRole.owners).containsExactly( aRoomMember(A_USER_ID_6, displayName = "Justin", role = RoomMember.Role.Owner(isCreator = true)), aRoomMember(A_USER_ID_7, displayName = "Mallory", role = RoomMember.Role.Owner(isCreator = false)), @@ -52,37 +53,25 @@ class MembersByRoleTest { @Test fun `isEmpty - only returns true with no members of any role`() { - val emptyMembersByRole = MembersByRole(emptyList()) + val emptyMembersByRole = MembersByRole() assertThat(emptyMembersByRole.isEmpty()).isTrue() val membersByRoleWithOwners = MembersByRole( owners = persistentListOf(aRoomMember(A_USER_ID, role = RoomMember.Role.Admin)), - admins = persistentListOf(), - moderators = persistentListOf(), - members = persistentListOf(), ) assertThat(membersByRoleWithOwners.isEmpty()).isFalse() val membersByRoleWithAdmins = MembersByRole( - owners = persistentListOf(), admins = persistentListOf(aRoomMember(A_USER_ID, role = RoomMember.Role.Admin)), - moderators = persistentListOf(), - members = persistentListOf(), ) assertThat(membersByRoleWithAdmins.isEmpty()).isFalse() val membersByRoleWithModerators = MembersByRole( - owners = persistentListOf(), - admins = persistentListOf(), moderators = persistentListOf(aRoomMember(A_USER_ID, role = RoomMember.Role.Moderator)), - members = persistentListOf(), ) assertThat(membersByRoleWithModerators.isEmpty()).isFalse() val membersByRoleWithMembers = MembersByRole( - owners = persistentListOf(), - admins = persistentListOf(), - moderators = persistentListOf(), members = persistentListOf(aRoomMember(A_USER_ID, role = RoomMember.Role.User)), ) assertThat(membersByRoleWithMembers.isEmpty()).isFalse() diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/PowerLevelRoomMemberComparator.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/PowerLevelRoomMemberComparator.kt deleted file mode 100644 index a14a0205cc..0000000000 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/PowerLevelRoomMemberComparator.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2024, 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -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 { - // 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()) - } - } - } -} diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt index 92705b1775..e69ddb744a 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt @@ -33,6 +33,7 @@ import io.element.android.libraries.matrix.api.room.RoomMembersState import io.element.android.libraries.matrix.api.room.RoomMembershipState import io.element.android.libraries.matrix.api.room.roomMembers import io.element.android.libraries.matrix.api.room.toMatrixUser +import io.element.android.libraries.matrix.ui.room.PowerLevelRoomMemberComparator import io.element.android.libraries.matrix.ui.room.canInviteAsState import io.element.android.libraries.matrix.ui.room.roomMemberIdentityStateChange import kotlinx.collections.immutable.ImmutableMap @@ -52,7 +53,8 @@ class RoomMemberListPresenter( private val roomMembersModerationPresenter: Presenter, private val encryptionService: EncryptionService, ) : Presenter { - var roomMembers: AsyncData by mutableStateOf(AsyncData.Loading()) + private var roomMembers: AsyncData by mutableStateOf(AsyncData.Loading()) + private val powerLevelRoomMemberComparator = PowerLevelRoomMemberComparator() @Composable override fun present(): RoomMemberListState { @@ -103,7 +105,7 @@ class RoomMemberListPresenter( .map { it.withIdentityState(roomMemberIdentityStates) } .toImmutableList(), joined = members.getOrDefault(RoomMembershipState.JOIN, emptyList()) - .sortedWith(PowerLevelRoomMemberComparator()) + .sortedWith(powerLevelRoomMemberComparator) .map { it.withIdentityState(roomMemberIdentityStates) } .toImmutableList(), banned = members.getOrDefault(RoomMembershipState.BAN, emptyList()) @@ -133,7 +135,7 @@ class RoomMemberListPresenter( .map { it.withIdentityState(roomMemberIdentityStates) } .toImmutableList(), joined = results.getOrDefault(RoomMembershipState.JOIN, emptyList()) - .sortedWith(PowerLevelRoomMemberComparator()) + .sortedWith(powerLevelRoomMemberComparator) .map { it.withIdentityState(roomMemberIdentityStates) } .toImmutableList(), banned = results.getOrDefault(RoomMembershipState.BAN, emptyList()) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notificationsettings/NotificationSettingsService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notificationsettings/NotificationSettingsService.kt index 213daa4a6b..4d8ce8afb4 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notificationsettings/NotificationSettingsService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notificationsettings/NotificationSettingsService.kt @@ -32,7 +32,7 @@ interface NotificationSettingsService { suspend fun setCallEnabled(enabled: Boolean): Result suspend fun isInviteForMeEnabled(): Result suspend fun setInviteForMeEnabled(enabled: Boolean): Result - suspend fun getRoomsWithUserDefinedRules(): Result> + suspend fun getRoomsWithUserDefinedRules(): Result> suspend fun canHomeServerPushEncryptedEventsToDevice(): Result suspend fun getRawPushRules(): Result } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt index 95b8a31cfa..7da0f14d14 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt @@ -131,9 +131,9 @@ class RustNotificationSettingsService( } } - override suspend fun getRoomsWithUserDefinedRules(): Result> = + override suspend fun getRoomsWithUserDefinedRules(): Result> = runCatchingExceptions { - notificationSettings.await().getRoomsWithUserDefinedRules(enabled = true) + notificationSettings.await().getRoomsWithUserDefinedRules(enabled = true).map(::RoomId) } override suspend fun canHomeServerPushEncryptedEventsToDevice(): Result = diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notificationsettings/FakeNotificationSettingsService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notificationsettings/FakeNotificationSettingsService.kt index 1d60ecd4dc..564cd231b2 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notificationsettings/FakeNotificationSettingsService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notificationsettings/FakeNotificationSettingsService.kt @@ -12,7 +12,6 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.api.room.RoomNotificationSettings -import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_NOTIFICATION_MODE import io.element.android.tests.testutils.lambda.lambdaError import kotlinx.coroutines.flow.MutableStateFlow @@ -26,6 +25,7 @@ class FakeNotificationSettingsService( initialOneToOneDefaultMode: RoomNotificationMode = RoomNotificationMode.ALL_MESSAGES, initialEncryptedOneToOneDefaultMode: RoomNotificationMode = RoomNotificationMode.ALL_MESSAGES, private val getRawPushRulesResult: () -> Result = { lambdaError() }, + private val getRoomsWithUserDefinedRulesResult: () -> Result> = { lambdaError() }, ) : NotificationSettingsService { private val notificationSettingsStateFlow = MutableStateFlow(Unit) private var defaultGroupRoomNotificationMode: RoomNotificationMode = initialGroupDefaultMode @@ -154,8 +154,8 @@ class FakeNotificationSettingsService( return Result.success(Unit) } - override suspend fun getRoomsWithUserDefinedRules(): Result> { - return Result.success(if (roomNotificationModeIsDefault) listOf() else listOf(A_ROOM_ID.value)) + override suspend fun getRoomsWithUserDefinedRules(): Result> { + return getRoomsWithUserDefinedRulesResult() } override suspend fun canHomeServerPushEncryptedEventsToDevice(): Result { diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/members/PowerLevelBaseRoomMemberComparatorTest.kt b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/room/PowerLevelRoomMemberComparatorTest.kt similarity index 95% rename from features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/members/PowerLevelBaseRoomMemberComparatorTest.kt rename to libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/room/PowerLevelRoomMemberComparatorTest.kt index ce6a21b0e5..d8240715b7 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/members/PowerLevelBaseRoomMemberComparatorTest.kt +++ b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/room/PowerLevelRoomMemberComparatorTest.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.roomdetails.impl.members +package io.element.android.libraries.matrix.ui.room import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.test.A_USER_ID @@ -14,9 +14,10 @@ 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 io.element.android.libraries.matrix.test.room.aRoomMember import org.junit.Test -class PowerLevelBaseRoomMemberComparatorTest { +class PowerLevelRoomMemberComparatorTest { @Test fun `order is Admin, then Moderator, then User`() { val memberList = listOf(