RoomListFilters : use kotlin filtering as rust one is slower and has more chance to bust the room list cache.
This commit is contained in:
parent
593a94b994
commit
bd87e99df1
13 changed files with 278 additions and 112 deletions
|
|
@ -23,11 +23,13 @@ import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.getAndUpdate
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind
|
||||
import org.matrix.rustcomponents.sdk.RoomListLoadingState
|
||||
import org.matrix.rustcomponents.sdk.RoomListService
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
|
@ -50,6 +52,7 @@ internal class RoomListFactory(
|
|||
innerProvider: suspend () -> InnerRoomList
|
||||
): DynamicRoomList {
|
||||
val loadingStateFlow: MutableStateFlow<RoomList.LoadingState> = MutableStateFlow(RoomList.LoadingState.NotLoaded)
|
||||
val filteredSummariesFlow = MutableSharedFlow<List<RoomSummary>>(replay = 1, extraBufferCapacity = 1)
|
||||
val summariesFlow = MutableSharedFlow<List<RoomSummary>>(replay = 1, extraBufferCapacity = 1)
|
||||
val processor = RoomSummaryListProcessor(summariesFlow, innerRoomListService, coroutineContext, roomSummaryDetailsFactory)
|
||||
// Makes sure we don't miss any events
|
||||
|
|
@ -63,8 +66,8 @@ internal class RoomListFactory(
|
|||
innerRoomList?.let { innerRoomList ->
|
||||
innerRoomList.entriesFlow(
|
||||
pageSize = pageSize,
|
||||
initialFilterKind = initialFilter.toRustFilter(),
|
||||
roomListDynamicEvents = dynamicEvents
|
||||
roomListDynamicEvents = dynamicEvents,
|
||||
initialFilterKind = RoomListEntriesDynamicFilterKind.NonLeft
|
||||
).onEach { update ->
|
||||
processor.postUpdate(update)
|
||||
}.launchIn(this)
|
||||
|
|
@ -75,12 +78,21 @@ internal class RoomListFactory(
|
|||
loadingStateFlow.value = it
|
||||
}
|
||||
.launchIn(this)
|
||||
|
||||
combine(
|
||||
currentFilter,
|
||||
summariesFlow
|
||||
) { filter, summaries ->
|
||||
summaries.filter(filter)
|
||||
}.onEach {
|
||||
filteredSummariesFlow.emit(it)
|
||||
}.launchIn(this)
|
||||
}
|
||||
}.invokeOnCompletion {
|
||||
innerRoomList?.destroy()
|
||||
}
|
||||
return RustDynamicRoomList(
|
||||
summaries = summariesFlow,
|
||||
summaries = filteredSummariesFlow,
|
||||
loadingState = loadingStateFlow,
|
||||
currentFilter = currentFilter,
|
||||
loadedPages = loadedPages,
|
||||
|
|
@ -106,8 +118,6 @@ private class RustDynamicRoomList(
|
|||
|
||||
override suspend fun updateFilter(filter: RoomListFilter) {
|
||||
currentFilter.emit(filter)
|
||||
val filterEvent = RoomListDynamicEvents.SetFilter(filter.toRustFilter())
|
||||
dynamicEvents.emit(filterEvent)
|
||||
}
|
||||
|
||||
override suspend fun loadMore() {
|
||||
|
|
|
|||
|
|
@ -17,20 +17,41 @@
|
|||
package io.element.android.libraries.matrix.impl.roomlist
|
||||
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListFilter
|
||||
import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind
|
||||
import org.matrix.rustcomponents.sdk.RoomListFilterCategory
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
|
||||
fun RoomListFilter.toRustFilter(): RoomListEntriesDynamicFilterKind {
|
||||
return when (this) {
|
||||
is RoomListFilter.All -> RoomListEntriesDynamicFilterKind.All(filters.map { it.toRustFilter() })
|
||||
is RoomListFilter.Any -> RoomListEntriesDynamicFilterKind.Any(filters.map { it.toRustFilter() })
|
||||
RoomListFilter.Category.Group -> RoomListEntriesDynamicFilterKind.Category(RoomListFilterCategory.GROUP)
|
||||
RoomListFilter.Category.People -> RoomListEntriesDynamicFilterKind.Category(RoomListFilterCategory.PEOPLE)
|
||||
is RoomListFilter.FuzzyMatchRoomName -> RoomListEntriesDynamicFilterKind.FuzzyMatchRoomName(pattern)
|
||||
RoomListFilter.NonLeft -> RoomListEntriesDynamicFilterKind.NonLeft
|
||||
RoomListFilter.None -> RoomListEntriesDynamicFilterKind.None
|
||||
is RoomListFilter.NormalizedMatchRoomName -> RoomListEntriesDynamicFilterKind.NormalizedMatchRoomName(pattern)
|
||||
RoomListFilter.Unread -> RoomListEntriesDynamicFilterKind.Unread
|
||||
RoomListFilter.Favorite -> RoomListEntriesDynamicFilterKind.Favourite
|
||||
val RoomListFilter.predicate
|
||||
get() = when (this) {
|
||||
is RoomListFilter.All -> { _: RoomSummary -> true }
|
||||
is RoomListFilter.Any -> { _: RoomSummary -> true }
|
||||
RoomListFilter.None -> { _: RoomSummary -> false }
|
||||
RoomListFilter.Category.Group -> { roomSummary: RoomSummary ->
|
||||
roomSummary is RoomSummary.Filled && !roomSummary.details.isDirect
|
||||
}
|
||||
RoomListFilter.Category.People -> { roomSummary: RoomSummary ->
|
||||
roomSummary is RoomSummary.Filled && roomSummary.details.isDirect
|
||||
}
|
||||
RoomListFilter.Favorite -> { roomSummary: RoomSummary ->
|
||||
roomSummary is RoomSummary.Filled && roomSummary.details.isFavorite
|
||||
}
|
||||
RoomListFilter.Unread -> { roomSummary: RoomSummary ->
|
||||
roomSummary is RoomSummary.Filled &&
|
||||
(roomSummary.details.numUnreadNotifications > 0 || roomSummary.details.isMarkedUnread)
|
||||
}
|
||||
is RoomListFilter.NormalizedMatchRoomName -> { roomSummary: RoomSummary ->
|
||||
roomSummary is RoomSummary.Filled && roomSummary.details.name.contains(pattern, ignoreCase = true)
|
||||
}
|
||||
}
|
||||
|
||||
fun List<RoomSummary>.filter(filter: RoomListFilter): List<RoomSummary> {
|
||||
return when (filter) {
|
||||
is RoomListFilter.All -> {
|
||||
val predicates = filter.filters.map { it.predicate }
|
||||
filter { roomSummary -> predicates.all { it(roomSummary) } }
|
||||
}
|
||||
is RoomListFilter.Any -> {
|
||||
val predicates = filter.filters.map { it.predicate }
|
||||
filter { roomSummary -> predicates.any { it(roomSummary) } }
|
||||
}
|
||||
else -> filter(filter.predicate)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ internal class RustRoomListService(
|
|||
private val roomListFactory: RoomListFactory,
|
||||
) : RoomListService {
|
||||
override fun createRoomList(
|
||||
coroutineScope: CoroutineScope,
|
||||
pageSize: Int,
|
||||
initialFilter: RoomListFilter,
|
||||
source: RoomList.Source
|
||||
|
|
@ -55,7 +54,6 @@ internal class RustRoomListService(
|
|||
return roomListFactory.createRoomList(
|
||||
pageSize = pageSize,
|
||||
initialFilter = initialFilter,
|
||||
coroutineScope = coroutineScope,
|
||||
coroutineContext = sessionDispatcher,
|
||||
) {
|
||||
when (source) {
|
||||
|
|
@ -68,7 +66,6 @@ internal class RustRoomListService(
|
|||
override val allRooms: DynamicRoomList = roomListFactory.createRoomList(
|
||||
pageSize = DEFAULT_PAGE_SIZE,
|
||||
coroutineContext = sessionDispatcher,
|
||||
initialFilter = RoomListFilter.all(RoomListFilter.NonLeft),
|
||||
) {
|
||||
innerRoomListService.allRooms()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* 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.impl.roomlist
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListFilter
|
||||
import io.element.android.libraries.matrix.test.room.aRoomSummaryDetails
|
||||
import io.element.android.libraries.matrix.test.room.aRoomSummaryFilled
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class RoomListFilterTests {
|
||||
private val regularRoom = aRoomSummaryFilled(
|
||||
aRoomSummaryDetails(
|
||||
isDirect = false
|
||||
)
|
||||
)
|
||||
private val directRoom = aRoomSummaryFilled(
|
||||
aRoomSummaryDetails(
|
||||
isDirect = true
|
||||
)
|
||||
)
|
||||
private val favoriteRoom = aRoomSummaryFilled(
|
||||
aRoomSummaryDetails(
|
||||
isFavorite = true
|
||||
)
|
||||
)
|
||||
private val markedAsUnreadRoom = aRoomSummaryFilled(
|
||||
aRoomSummaryDetails(
|
||||
isMarkedUnread = true
|
||||
)
|
||||
)
|
||||
private val unreadNotificationRoom = aRoomSummaryFilled(
|
||||
aRoomSummaryDetails(
|
||||
numUnreadNotifications = 1
|
||||
)
|
||||
)
|
||||
private val roomToSearch = aRoomSummaryFilled(
|
||||
aRoomSummaryDetails(
|
||||
name = "Room to search"
|
||||
)
|
||||
)
|
||||
|
||||
private val roomSummaries = listOf(
|
||||
regularRoom,
|
||||
directRoom,
|
||||
favoriteRoom,
|
||||
markedAsUnreadRoom,
|
||||
unreadNotificationRoom,
|
||||
roomToSearch
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `Room list filter all empty`() = runTest {
|
||||
val filter = RoomListFilter.all()
|
||||
assertThat(roomSummaries.filter(filter)).isEqualTo(roomSummaries)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Room list filter none`() = runTest {
|
||||
val filter = RoomListFilter.None
|
||||
assertThat(roomSummaries.filter(filter)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Room list filter people`() = runTest {
|
||||
val filter = RoomListFilter.Category.People
|
||||
assertThat(roomSummaries.filter(filter)).containsExactly(directRoom)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Room list filter group`() = runTest {
|
||||
val filter = RoomListFilter.Category.Group
|
||||
assertThat(roomSummaries.filter(filter)).containsExactly(regularRoom, favoriteRoom, markedAsUnreadRoom, unreadNotificationRoom, roomToSearch)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Room list filter favorite`() = runTest {
|
||||
val filter = RoomListFilter.Favorite
|
||||
assertThat(roomSummaries.filter(filter)).containsExactly(favoriteRoom)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Room list filter unread`() = runTest {
|
||||
val filter = RoomListFilter.Unread
|
||||
assertThat(roomSummaries.filter(filter)).containsExactly(markedAsUnreadRoom, unreadNotificationRoom)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Room list filter normalized match room name`() = runTest {
|
||||
val filter = RoomListFilter.NormalizedMatchRoomName("search")
|
||||
assertThat(roomSummaries.filter(filter)).containsExactly(roomToSearch)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Room list filter all with one match`() = runTest {
|
||||
val filter = RoomListFilter.all(
|
||||
RoomListFilter.Category.Group,
|
||||
RoomListFilter.Favorite
|
||||
)
|
||||
assertThat(roomSummaries.filter(filter)).containsExactly(favoriteRoom)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Room list filter all with no match`() = runTest {
|
||||
val filter = RoomListFilter.all(
|
||||
RoomListFilter.Category.People,
|
||||
RoomListFilter.Favorite
|
||||
)
|
||||
assertThat(roomSummaries.filter(filter)).isEmpty()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue