Add room list filter combining based on space selection

This commit is contained in:
ganfra 2026-02-02 21:46:33 +01:00
parent eb94015996
commit 915aca9c1d
7 changed files with 48 additions and 49 deletions

View file

@ -9,6 +9,7 @@
package io.element.android.features.home.impl.filters package io.element.android.features.home.impl.filters
import io.element.android.features.home.impl.R 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. * 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) 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
}
}

View file

@ -9,24 +9,17 @@
package io.element.android.features.home.impl.filters package io.element.android.features.home.impl.filters
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState
import dev.zacsweers.metro.Inject 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.features.home.impl.filters.selection.FilterSelectionStrategy
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import kotlinx.collections.immutable.toImmutableList 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 @Inject
class RoomListFiltersPresenter( class RoomListFiltersPresenter(
private val roomListDataSource: RoomListDataSource,
private val filterSelectionStrategy: FilterSelectionStrategy, private val filterSelectionStrategy: FilterSelectionStrategy,
) : Presenter<RoomListFiltersState> { ) : Presenter<RoomListFiltersState> {
private val initialFilters = filterSelectionStrategy.filterSelectionStates.value.toImmutableList()
@Composable @Composable
override fun present(): RoomListFiltersState { override fun present(): RoomListFiltersState {
fun handleEvent(event: RoomListFiltersEvent) { fun handleEvent(event: RoomListFiltersEvent) {
@ -40,31 +33,9 @@ class RoomListFiltersPresenter(
} }
} }
val filters by produceState(initialValue = initialFilters) { val filters by filterSelectionStrategy.filterSelectionStates.collectAsState()
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)
}
}
return RoomListFiltersState( return RoomListFiltersState(
filterSelectionStates = filters, filterSelectionStates = filters.toImmutableList(),
eventSink = ::handleEvent, eventSink = ::handleEvent,
) )
} }

View file

@ -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.announcement.api.AnnouncementService
import io.element.android.features.home.impl.datasource.RoomListDataSource 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.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.RoomListSearchEvent
import io.element.android.features.home.impl.search.RoomListSearchState 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.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.SeenInvitesStore
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents.AcceptInvite import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents.AcceptInvite
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents.DeclineInvite 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.core.RoomId
import io.element.android.libraries.matrix.api.encryption.RecoveryState 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.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.api.timeline.ReceiptType
import io.element.android.libraries.matrix.ui.safety.rememberHideInvitesAvatar import io.element.android.libraries.matrix.ui.safety.rememberHideInvitesAvatar
import io.element.android.libraries.preferences.api.store.AppPreferencesStore 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( val contentState = roomListContentState(
securityBannerDismissed, securityBannerDismissed,
showNewNotificationSoundBanner, showNewNotificationSoundBanner,

View file

@ -8,6 +8,7 @@
package io.element.android.features.home.impl.spacefilters package io.element.android.features.home.impl.spacefilters
import androidx.compose.foundation.text.input.TextFieldState 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 io.element.android.libraries.matrix.api.spaces.SpaceServiceFilter
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
@ -40,3 +41,10 @@ sealed interface SpaceFiltersState {
val eventSink: (SpaceFiltersEvent.Selected) -> Unit, val eventSink: (SpaceFiltersEvent.Selected) -> Unit,
) : SpaceFiltersState ) : SpaceFiltersState
} }
fun SpaceFiltersState.selectedFilter(): SpaceServiceFilter? {
return when (this) {
is SpaceFiltersState.Selected -> this.selectedFilter
else -> null
}
}

View file

@ -9,9 +9,6 @@
package io.element.android.features.home.impl.filters package io.element.android.features.home.impl.filters
import com.google.common.truth.Truth.assertThat 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.DefaultFilterSelectionStrategy
import io.element.android.features.home.impl.filters.selection.FilterSelectionState import io.element.android.features.home.impl.filters.selection.FilterSelectionState
import io.element.android.libraries.dateformatter.api.DateFormatter 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.api.roomlist.RoomListService
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService 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.awaitLastSequentialItem
import io.element.android.tests.testutils.test import io.element.android.tests.testutils.test
import io.element.android.tests.testutils.testCoroutineDispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.advanceUntilIdle
@ -117,18 +112,6 @@ private fun TestScope.createRoomListFiltersPresenter(
roomLatestEventFormatter: RoomLatestEventFormatter = FakeRoomLatestEventFormatter(), roomLatestEventFormatter: RoomLatestEventFormatter = FakeRoomLatestEventFormatter(),
): RoomListFiltersPresenter { ): RoomListFiltersPresenter {
return 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(), filterSelectionStrategy = DefaultFilterSelectionStrategy(),
) )
} }

View file

@ -8,6 +8,8 @@
package io.element.android.libraries.matrix.api.roomlist package io.element.android.libraries.matrix.api.roomlist
import io.element.android.libraries.matrix.api.core.RoomId
sealed interface RoomListFilter { sealed interface RoomListFilter {
companion object { companion object {
/** /**
@ -41,6 +43,10 @@ sealed interface RoomListFilter {
val filters: List<RoomListFilter> val filters: List<RoomListFilter>
) : RoomListFilter ) : RoomListFilter
data class Identifiers(
val values : List<RoomId>,
): RoomListFilter
/** /**
* A filter that matches rooms that are unread. * A filter that matches rooms that are unread.
*/ */

View file

@ -14,6 +14,7 @@ import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind.Any
import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind.Category import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind.Category
import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind.DeduplicateVersions import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind.DeduplicateVersions
import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind.Favourite 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.Invite
import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind.NonLeft import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind.NonLeft
import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind.NonSpace import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind.NonSpace
@ -60,6 +61,7 @@ internal object RoomListFilterMapper {
return when (filter) { return when (filter) {
is RoomListFilter.All -> All(filters = filter.filters.map { mapFilter(it) }) is RoomListFilter.All -> All(filters = filter.filters.map { mapFilter(it) })
is RoomListFilter.Any -> Any(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.None -> None
RoomListFilter.Category.Group -> Category(RoomListFilterCategory.GROUP) RoomListFilter.Category.Group -> Category(RoomListFilterCategory.GROUP)
RoomListFilter.Category.People -> Category(RoomListFilterCategory.PEOPLE) RoomListFilter.Category.People -> Category(RoomListFilterCategory.PEOPLE)