From 212a8e3aa3b7be460579f6619bb459080807b2a0 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 2 Feb 2026 21:46:33 +0100 Subject: [PATCH] Add room list filter combining based on space selection --- .../home/impl/filters/RoomListFilter.kt | 11 ++++++ .../impl/filters/RoomListFiltersPresenter.kt | 35 ++----------------- .../home/impl/roomlist/RoomListPresenter.kt | 18 ++++++++++ .../impl/spacefilters/SpaceFiltersState.kt | 8 +++++ .../filters/RoomListFiltersPresenterTest.kt | 17 --------- .../matrix/api/roomlist/RoomListFilter.kt | 6 ++++ .../impl/roomlist/RoomListFilterMapper.kt | 2 ++ 7 files changed, 48 insertions(+), 49 deletions(-) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFilter.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFilter.kt index 1f627eca4e..3e07c565db 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFilter.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFilter.kt @@ -9,6 +9,7 @@ package io.element.android.features.home.impl.filters import io.element.android.features.home.impl.R +import io.element.android.libraries.matrix.api.roomlist.RoomListFilter as MatrixRoomListFilter /** * Enum class representing the different filters that can be applied to the room list. @@ -30,3 +31,13 @@ enum class RoomListFilter(val stringResource: Int) { Invites -> setOf(Rooms, People, Unread, Favourites) } } + +fun RoomListFilter.into(): MatrixRoomListFilter { + return when (this) { + RoomListFilter.Rooms -> MatrixRoomListFilter.Category.Group + RoomListFilter.People -> MatrixRoomListFilter.Category.People + RoomListFilter.Unread -> MatrixRoomListFilter.Unread + RoomListFilter.Favourites -> MatrixRoomListFilter.Favorite + RoomListFilter.Invites -> MatrixRoomListFilter.Invite + } +} diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersPresenter.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersPresenter.kt index 3c808045fd..e73660219c 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersPresenter.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersPresenter.kt @@ -9,24 +9,17 @@ package io.element.android.features.home.impl.filters import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.produceState import dev.zacsweers.metro.Inject -import io.element.android.features.home.impl.datasource.RoomListDataSource import io.element.android.features.home.impl.filters.selection.FilterSelectionStrategy import io.element.android.libraries.architecture.Presenter import kotlinx.collections.immutable.toImmutableList -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.map -import io.element.android.libraries.matrix.api.roomlist.RoomListFilter as MatrixRoomListFilter @Inject class RoomListFiltersPresenter( - private val roomListDataSource: RoomListDataSource, private val filterSelectionStrategy: FilterSelectionStrategy, ) : Presenter { - private val initialFilters = filterSelectionStrategy.filterSelectionStates.value.toImmutableList() - @Composable override fun present(): RoomListFiltersState { fun handleEvent(event: RoomListFiltersEvent) { @@ -40,31 +33,9 @@ class RoomListFiltersPresenter( } } - val filters by produceState(initialValue = initialFilters) { - filterSelectionStrategy.filterSelectionStates - .map { filters -> - value = filters.toImmutableList() - filters.mapNotNull { filterState -> - if (!filterState.isSelected) { - return@mapNotNull null - } - when (filterState.filter) { - RoomListFilter.Rooms -> MatrixRoomListFilter.Category.Group - RoomListFilter.People -> MatrixRoomListFilter.Category.People - RoomListFilter.Unread -> MatrixRoomListFilter.Unread - RoomListFilter.Favourites -> MatrixRoomListFilter.Favorite - RoomListFilter.Invites -> MatrixRoomListFilter.Invite - } - } - } - .collectLatest { filters -> - val result = MatrixRoomListFilter.All(filters) - roomListDataSource.updateFilter(result) - } - } - + val filters by filterSelectionStrategy.filterSelectionStates.collectAsState() return RoomListFiltersState( - filterSelectionStates = filters, + filterSelectionStates = filters.toImmutableList(), eventSink = ::handleEvent, ) } diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenter.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenter.kt index 86999b4103..f52e8dafc2 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenter.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenter.kt @@ -29,9 +29,11 @@ import io.element.android.features.announcement.api.Announcement import io.element.android.features.announcement.api.AnnouncementService import io.element.android.features.home.impl.datasource.RoomListDataSource import io.element.android.features.home.impl.filters.RoomListFiltersState +import io.element.android.features.home.impl.filters.into import io.element.android.features.home.impl.search.RoomListSearchEvent import io.element.android.features.home.impl.search.RoomListSearchState import io.element.android.features.home.impl.spacefilters.SpaceFiltersState +import io.element.android.features.home.impl.spacefilters.selectedFilter import io.element.android.features.invite.api.SeenInvitesStore import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents.AcceptInvite import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents.DeclineInvite @@ -45,6 +47,7 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.encryption.RecoveryState import io.element.android.libraries.matrix.api.roomlist.RoomList +import io.element.android.libraries.matrix.api.roomlist.RoomListFilter import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.ui.safety.rememberHideInvitesAvatar import io.element.android.libraries.preferences.api.store.AppPreferencesStore @@ -153,6 +156,21 @@ class RoomListPresenter( } } + LaunchedEffect(filtersState.filterSelectionStates, spaceFiltersState.selectedFilter()) { + val selectedFilters = filtersState.filterSelectionStates.mapNotNull { filterState -> + if (!filterState.isSelected) { + return@mapNotNull null + } + filterState.filter.into() + } + val selectedSpaceFilter = when (spaceFiltersState) { + is SpaceFiltersState.Selected -> RoomListFilter.Identifiers(spaceFiltersState.selectedFilter.descendants) + else -> null + } + val allFilters = RoomListFilter.All(selectedFilters + listOfNotNull(selectedSpaceFilter)) + roomListDataSource.updateFilter(allFilters) + } + val contentState = roomListContentState( securityBannerDismissed, showNewNotificationSoundBanner, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersState.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersState.kt index cecda8b381..2ff5517455 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersState.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersState.kt @@ -8,6 +8,7 @@ package io.element.android.features.home.impl.spacefilters import androidx.compose.foundation.text.input.TextFieldState +import io.element.android.features.home.impl.filters.RoomListFilter import io.element.android.libraries.matrix.api.spaces.SpaceServiceFilter import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList @@ -40,3 +41,10 @@ sealed interface SpaceFiltersState { val eventSink: (SpaceFiltersEvent.Selected) -> Unit, ) : SpaceFiltersState } + +fun SpaceFiltersState.selectedFilter(): SpaceServiceFilter? { + return when (this) { + is SpaceFiltersState.Selected -> this.selectedFilter + else -> null + } +} diff --git a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersPresenterTest.kt b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersPresenterTest.kt index c30e279b2e..bb00a9edac 100644 --- a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersPresenterTest.kt +++ b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersPresenterTest.kt @@ -9,9 +9,6 @@ package io.element.android.features.home.impl.filters import com.google.common.truth.Truth.assertThat -import io.element.android.features.home.impl.FakeDateTimeObserver -import io.element.android.features.home.impl.datasource.RoomListDataSource -import io.element.android.features.home.impl.datasource.aRoomListRoomSummaryFactory import io.element.android.features.home.impl.filters.selection.DefaultFilterSelectionStrategy import io.element.android.features.home.impl.filters.selection.FilterSelectionState import io.element.android.libraries.dateformatter.api.DateFormatter @@ -22,10 +19,8 @@ import io.element.android.libraries.matrix.api.notificationsettings.Notification import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService -import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.awaitLastSequentialItem import io.element.android.tests.testutils.test -import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceUntilIdle @@ -117,18 +112,6 @@ private fun TestScope.createRoomListFiltersPresenter( roomLatestEventFormatter: RoomLatestEventFormatter = FakeRoomLatestEventFormatter(), ): RoomListFiltersPresenter { return RoomListFiltersPresenter( - roomListDataSource = RoomListDataSource( - roomListService = roomListService, - roomListRoomSummaryFactory = aRoomListRoomSummaryFactory( - dateFormatter = dateFormatter, - roomLatestEventFormatter = roomLatestEventFormatter, - ), - coroutineDispatchers = testCoroutineDispatchers(), - notificationSettingsService = notificationSettingsService, - sessionCoroutineScope = backgroundScope, - dateTimeObserver = FakeDateTimeObserver(), - analyticsService = FakeAnalyticsService(), - ), filterSelectionStrategy = DefaultFilterSelectionStrategy(), ) } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListFilter.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListFilter.kt index 3c6e35d339..b4abedd75d 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListFilter.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListFilter.kt @@ -8,6 +8,8 @@ package io.element.android.libraries.matrix.api.roomlist +import io.element.android.libraries.matrix.api.core.RoomId + sealed interface RoomListFilter { companion object { /** @@ -41,6 +43,10 @@ sealed interface RoomListFilter { val filters: List ) : RoomListFilter + data class Identifiers( + val values : List, + ): RoomListFilter + /** * A filter that matches rooms that are unread. */ diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilterMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilterMapper.kt index fdee790b19..648376a9cc 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilterMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilterMapper.kt @@ -14,6 +14,7 @@ import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind.Any import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind.Category import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind.DeduplicateVersions import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind.Favourite +import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind.Identifiers import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind.Invite import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind.NonLeft import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind.NonSpace @@ -60,6 +61,7 @@ internal object RoomListFilterMapper { return when (filter) { is RoomListFilter.All -> All(filters = filter.filters.map { mapFilter(it) }) is RoomListFilter.Any -> Any(filters = filter.filters.map { mapFilter(it) }) + is RoomListFilter.Identifiers -> Identifiers(identifiers = filter.values.map { it.value }) RoomListFilter.None -> None RoomListFilter.Category.Group -> Category(RoomListFilterCategory.GROUP) RoomListFilter.Category.People -> Category(RoomListFilterCategory.PEOPLE)