From 85a0ef3677857f8891b153102dd1c5669e1404df Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 16 Feb 2024 19:22:06 +0100 Subject: [PATCH 1/7] RoomList : rework how search is done to prepare for later filtering --- .../features/roomlist/impl/RoomListEvents.kt | 1 - .../roomlist/impl/RoomListPresenter.kt | 19 +-- .../features/roomlist/impl/RoomListState.kt | 5 +- .../roomlist/impl/RoomListStateProvider.kt | 8 +- .../features/roomlist/impl/RoomListView.kt | 9 +- .../impl/components/RoomListTopBar.kt | 11 -- .../impl/datasource/RoomListDataSource.kt | 26 --- .../impl/search/RoomListSearchEvents.kt | 23 +++ .../impl/search/RoomListSearchPresenter.kt | 120 ++++++++++++++ .../impl/search/RoomListSearchState.kt | 27 ++++ .../search/RoomListSearchStateProvider.kt | 46 ++++++ ...rchResultView.kt => RoomListSearchView.kt} | 77 +++------ .../roomlist/impl/RoomListPresenterTests.kt | 71 +------- .../search/RoomListSearchPresenterTests.kt | 151 ++++++++++++++++++ .../libraries/matrix/api/roomlist/RoomList.kt | 5 + .../matrix/api/roomlist/RoomListFilter.kt | 30 ++++ .../matrix/api/roomlist/RoomListService.kt | 15 ++ .../libraries/matrix/impl/RustMatrixClient.kt | 4 +- .../matrix/impl/roomlist/RoomListFactory.kt | 14 +- .../impl/roomlist/RoomSummaryListProcessor.kt | 6 +- .../impl/roomlist/RustRoomListService.kt | 20 ++- .../roomlist/RoomSummaryListProcessorTests.kt | 2 +- .../matrix/test/room/RoomSummaryFixture.kt | 4 + .../test/roomlist/FakeRoomListService.kt | 12 +- .../test/roomlist/SimplePagedRoomList.kt | 2 +- 25 files changed, 512 insertions(+), 196 deletions(-) create mode 100644 features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchEvents.kt create mode 100644 features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchPresenter.kt create mode 100644 features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchState.kt create mode 100644 features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchStateProvider.kt rename features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/{RoomListSearchResultView.kt => RoomListSearchView.kt} (73%) create mode 100644 features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchPresenterTests.kt diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListEvents.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListEvents.kt index c808ae689b..cad5dd3311 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListEvents.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListEvents.kt @@ -20,7 +20,6 @@ import io.element.android.features.roomlist.impl.model.RoomListRoomSummary import io.element.android.libraries.matrix.api.core.RoomId sealed interface RoomListEvents { - data class UpdateFilter(val newFilter: String) : RoomListEvents data class UpdateVisibleRange(val range: IntRange) : RoomListEvents data object DismissRequestVerificationPrompt : RoomListEvents data object DismissRecoveryKeyPrompt : RoomListEvents diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt index 68ef53703a..7f53f90e92 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt @@ -37,6 +37,8 @@ import io.element.android.features.preferences.api.store.SessionPreferencesStore import io.element.android.features.roomlist.impl.datasource.InviteStateDataSource import io.element.android.features.roomlist.impl.datasource.RoomListDataSource import io.element.android.features.roomlist.impl.migration.MigrationScreenPresenter +import io.element.android.features.roomlist.impl.search.RoomListSearchEvents +import io.element.android.features.roomlist.impl.search.RoomListSearchPresenter import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher @@ -76,6 +78,7 @@ class RoomListPresenter @Inject constructor( private val encryptionService: EncryptionService, private val featureFlagService: FeatureFlagService, private val indicatorService: IndicatorService, + private val searchPresenter: RoomListSearchPresenter, private val migrationScreenPresenter: MigrationScreenPresenter, private val sessionPreferencesStore: SessionPreferencesStore, ) : Presenter { @@ -89,9 +92,8 @@ class RoomListPresenter @Inject constructor( val roomList by produceState(initialValue = AsyncData.Loading()) { roomListDataSource.allRooms.collect { value = AsyncData.Success(it) } } - val filteredRoomList by roomListDataSource.filteredRooms.collectAsState() - val filter by roomListDataSource.filter.collectAsState() val networkConnectionStatus by networkMonitor.connectivity.collectAsState() + val searchState = searchPresenter.present() LaunchedEffect(Unit) { roomListDataSource.launchIn(this) @@ -122,21 +124,14 @@ class RoomListPresenter @Inject constructor( // Avatar indicator val showAvatarIndicator by indicatorService.showRoomListTopBarIndicator() - var displaySearchResults by rememberSaveable { mutableStateOf(false) } val contextMenu = remember { mutableStateOf(RoomListState.ContextMenu.Hidden) } fun handleEvents(event: RoomListEvents) { when (event) { - is RoomListEvents.UpdateFilter -> roomListDataSource.updateFilter(event.newFilter) is RoomListEvents.UpdateVisibleRange -> updateVisibleRange(event.range) RoomListEvents.DismissRequestVerificationPrompt -> verificationPromptDismissed = true RoomListEvents.DismissRecoveryKeyPrompt -> recoveryKeyPromptDismissed = true - RoomListEvents.ToggleSearchResults -> { - if (displaySearchResults) { - roomListDataSource.updateFilter("") - } - displaySearchResults = !displaySearchResults - } + RoomListEvents.ToggleSearchResults -> searchState.eventSink(RoomListSearchEvents.ToggleSearchVisibility) is RoomListEvents.ShowContextMenu -> { coroutineScope.showContextMenu(event, contextMenu) } @@ -175,16 +170,14 @@ class RoomListPresenter @Inject constructor( matrixUser = matrixUser.value, showAvatarIndicator = showAvatarIndicator, roomList = roomList, - filter = filter, - filteredRoomList = filteredRoomList, displayVerificationPrompt = displayVerificationPrompt, displayRecoveryKeyPrompt = displayRecoveryKeyPrompt, snackbarMessage = snackbarMessage, hasNetworkConnection = networkConnectionStatus == NetworkStatus.Online, invitesState = inviteStateDataSource.inviteState(), - displaySearchResults = displaySearchResults, contextMenu = contextMenu.value, leaveRoomState = leaveRoomState, + searchState = searchState, displayMigrationStatus = isMigrating, eventSink = ::handleEvents ) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt index e97ab7c073..3fffd522ae 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt @@ -19,6 +19,7 @@ package io.element.android.features.roomlist.impl import androidx.compose.runtime.Immutable import io.element.android.features.leaveroom.api.LeaveRoomState import io.element.android.features.roomlist.impl.model.RoomListRoomSummary +import io.element.android.features.roomlist.impl.search.RoomListSearchState import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.matrix.api.core.RoomId @@ -30,16 +31,14 @@ data class RoomListState( val matrixUser: MatrixUser?, val showAvatarIndicator: Boolean, val roomList: AsyncData>, - val filter: String?, - val filteredRoomList: ImmutableList, val displayVerificationPrompt: Boolean, val displayRecoveryKeyPrompt: Boolean, val hasNetworkConnection: Boolean, val snackbarMessage: SnackbarMessage?, val invitesState: InvitesState, - val displaySearchResults: Boolean, val contextMenu: ContextMenu, val leaveRoomState: LeaveRoomState, + val searchState: RoomListSearchState, val displayMigrationStatus: Boolean, val eventSink: (RoomListEvents) -> Unit, ) { diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt index c54360d3d3..ed1a948400 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt @@ -21,6 +21,7 @@ import io.element.android.features.leaveroom.api.aLeaveRoomState import io.element.android.features.roomlist.impl.datasource.RoomListRoomSummaryFactory import io.element.android.features.roomlist.impl.model.RoomListRoomSummary import io.element.android.features.roomlist.impl.model.aRoomListRoomSummary +import io.element.android.features.roomlist.impl.search.aRoomListSearchState import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize @@ -41,14 +42,13 @@ open class RoomListStateProvider : PreviewParameterProvider { aRoomListState().copy(hasNetworkConnection = false), aRoomListState().copy(invitesState = InvitesState.SeenInvites), aRoomListState().copy(invitesState = InvitesState.NewInvites), - aRoomListState().copy(displaySearchResults = true, filter = "", filteredRoomList = persistentListOf()), - aRoomListState().copy(displaySearchResults = true), aRoomListState().copy(contextMenu = aContextMenuShown(roomName = "A nice room name")), aRoomListState().copy(contextMenu = aContextMenuShown(isFavorite = true)), aRoomListState().copy(displayRecoveryKeyPrompt = true), aRoomListState().copy(roomList = AsyncData.Success(persistentListOf())), aRoomListState().copy(roomList = AsyncData.Loading(prevData = RoomListRoomSummaryFactory.createFakeList())), aRoomListState().copy(matrixUser = null, displayMigrationStatus = true), + aRoomListState().copy(searchState = aRoomListSearchState(isSearchActive = true, query = "Test")), ) } @@ -56,16 +56,14 @@ internal fun aRoomListState() = RoomListState( matrixUser = MatrixUser(userId = UserId("@id:domain"), displayName = "User#1"), showAvatarIndicator = false, roomList = AsyncData.Success(aRoomListRoomSummaryList()), - filter = "filter", - filteredRoomList = aRoomListRoomSummaryList(), hasNetworkConnection = true, snackbarMessage = null, displayVerificationPrompt = false, displayRecoveryKeyPrompt = false, invitesState = InvitesState.NoInvites, - displaySearchResults = false, contextMenu = RoomListState.ContextMenu.Hidden, leaveRoomState = aLeaveRoomState(), + searchState = aRoomListSearchState(), displayMigrationStatus = false, eventSink = {} ) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt index a9fcda5428..fd8c4a7ccc 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt @@ -57,7 +57,7 @@ import io.element.android.features.roomlist.impl.components.RoomListTopBar import io.element.android.features.roomlist.impl.components.RoomSummaryRow import io.element.android.features.roomlist.impl.migration.MigrationScreenView import io.element.android.features.roomlist.impl.model.RoomListRoomSummary -import io.element.android.features.roomlist.impl.search.RoomListSearchResultView +import io.element.android.features.roomlist.impl.search.RoomListSearchView import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -118,8 +118,8 @@ fun RoomListView( onMenuActionClicked = onMenuActionClicked, ) // This overlaid view will only be visible when state.displaySearchResults is true - RoomListSearchResultView( - state = state, + RoomListSearchView( + state = state.searchState, onRoomClicked = onRoomClicked, onRoomLongClicked = { onRoomLongClicked(it) }, modifier = Modifier @@ -207,8 +207,7 @@ private fun RoomListContent( RoomListTopBar( matrixUser = state.matrixUser, showAvatarIndicator = state.showAvatarIndicator, - areSearchResultsDisplayed = state.displaySearchResults, - onFilterChanged = { state.eventSink(RoomListEvents.UpdateFilter(it)) }, + areSearchResultsDisplayed = state.searchState.isSearchActive, onToggleSearch = { state.eventSink(RoomListEvents.ToggleSearchResults) }, onMenuActionClicked = onMenuActionClicked, onOpenSettings = onOpenSettings, diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt index 871ca846c7..0d0a750a9f 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt @@ -16,7 +16,6 @@ package io.element.android.features.roomlist.impl.components -import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxWidth @@ -87,7 +86,6 @@ fun RoomListTopBar( matrixUser: MatrixUser?, showAvatarIndicator: Boolean, areSearchResultsDisplayed: Boolean, - onFilterChanged: (String) -> Unit, onToggleSearch: () -> Unit, onMenuActionClicked: (RoomListMenuAction) -> Unit, onOpenSettings: () -> Unit, @@ -95,15 +93,6 @@ fun RoomListTopBar( displayMenuItems: Boolean, modifier: Modifier = Modifier, ) { - fun closeFilter() { - onFilterChanged("") - } - - BackHandler(enabled = areSearchResultsDisplayed) { - closeFilter() - onToggleSearch() - } - DefaultRoomListTopBar( matrixUser = matrixUser, showAvatarIndicator = showAvatarIndicator, diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt index c55a47596b..da03122134 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt @@ -25,15 +25,11 @@ import io.element.android.libraries.matrix.api.roomlist.RoomList import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.roomlist.RoomSummary import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -54,9 +50,7 @@ class RoomListDataSource @Inject constructor( observeNotificationSettings() } - private val _filter = MutableStateFlow("") private val _allRooms = MutableSharedFlow>(replay = 1) - private val _filteredRooms = MutableStateFlow>(persistentListOf()) private val lock = Mutex() private val diffCache = MutableListDiffCache() @@ -72,29 +66,9 @@ class RoomListDataSource @Inject constructor( replaceWith(roomSummaries) } .launchIn(coroutineScope) - - combine( - _filter, - _allRooms - ) { filterValue, allRoomsValue -> - when { - filterValue.isEmpty() -> emptyList() - else -> allRoomsValue.filter { it.name.contains(filterValue, ignoreCase = true) } - }.toImmutableList() - } - .onEach { - _filteredRooms.value = it - } - .launchIn(coroutineScope) } - fun updateFilter(filterValue: String) { - _filter.value = filterValue - } - - val filter: StateFlow = _filter val allRooms: SharedFlow> = _allRooms - val filteredRooms: StateFlow> = _filteredRooms @OptIn(FlowPreview::class) private fun observeNotificationSettings() { diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchEvents.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchEvents.kt new file mode 100644 index 0000000000..6c99a4e0d0 --- /dev/null +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchEvents.kt @@ -0,0 +1,23 @@ +/* + * 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.roomlist.impl.search + +sealed interface RoomListSearchEvents { + data object ToggleSearchVisibility : RoomListSearchEvents + data class QueryChanged(val query: String) : RoomListSearchEvents + data object ClearQuery : RoomListSearchEvents +} diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchPresenter.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchPresenter.kt new file mode 100644 index 0000000000..5beaa8077a --- /dev/null +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchPresenter.kt @@ -0,0 +1,120 @@ +/* + * 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.roomlist.impl.search + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import io.element.android.features.roomlist.impl.datasource.RoomListRoomSummaryFactory +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.core.coroutine.CoroutineDispatchers +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.roomlist.RoomListService +import io.element.android.libraries.matrix.api.roomlist.RoomSummary +import io.element.android.libraries.matrix.api.roomlist.loadAllIncrementally +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toPersistentList +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import javax.inject.Inject + +private const val PAGE_SIZE = 50 + +class RoomListSearchPresenter @Inject constructor( + private val roomListService: RoomListService, + private val roomSummaryFactory: RoomListRoomSummaryFactory, + private val coroutineDispatchers: CoroutineDispatchers, +) : Presenter { + + @Composable + override fun present(): RoomListSearchState { + + var isSearchActive by rememberSaveable { + mutableStateOf(false) + } + var searchQuery by rememberSaveable { + mutableStateOf("") + } + val coroutineScope = rememberCoroutineScope() + + val roomList = remember { + roomListService.createRoomList( + coroutineScope = coroutineScope, + pageSize = PAGE_SIZE, + initialFilter = RoomListFilter.all(RoomListFilter.None), + source = RoomList.Source.All, + ) + } + + LaunchedEffect(Unit) { + roomList.loadAllIncrementally(this) + } + LaunchedEffect(key1 = searchQuery) { + val filter = if (searchQuery.isBlank()) { + RoomListFilter.all(RoomListFilter.None) + } else { + RoomListFilter.all(RoomListFilter.NonLeft, RoomListFilter.NormalizedMatchRoomName(searchQuery)) + } + roomList.updateFilter(filter) + } + + fun handleEvents(event: RoomListSearchEvents) { + when (event) { + RoomListSearchEvents.ClearQuery -> { + searchQuery = "" + } + is RoomListSearchEvents.QueryChanged -> { + searchQuery = event.query + } + RoomListSearchEvents.ToggleSearchVisibility -> { + isSearchActive = !isSearchActive + searchQuery = "" + } + } + } + + val searchResults by roomList + .rememberMappedSummaries() + .collectAsState(initial = persistentListOf()) + + return RoomListSearchState( + isSearchActive = isSearchActive, + query = searchQuery, + results = searchResults, + eventSink = ::handleEvents + ) + } + + @Composable + private fun RoomList.rememberMappedSummaries() = remember { + summaries + .map { roomSummaries -> + roomSummaries + .filterIsInstance() + .map(roomSummaryFactory::create) + .toPersistentList() + } + .flowOn(coroutineDispatchers.computation) + } +} diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchState.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchState.kt new file mode 100644 index 0000000000..c4b24dc798 --- /dev/null +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchState.kt @@ -0,0 +1,27 @@ +/* + * 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.roomlist.impl.search + +import io.element.android.features.roomlist.impl.model.RoomListRoomSummary +import kotlinx.collections.immutable.ImmutableList + +data class RoomListSearchState( + val isSearchActive: Boolean, + val query: String, + val results: ImmutableList, + val eventSink: (RoomListSearchEvents) -> Unit +) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchStateProvider.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchStateProvider.kt new file mode 100644 index 0000000000..b846354b6f --- /dev/null +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchStateProvider.kt @@ -0,0 +1,46 @@ +/* + * 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.roomlist.impl.search + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.roomlist.impl.aRoomListRoomSummaryList +import io.element.android.features.roomlist.impl.model.RoomListRoomSummary +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf + +class RoomListSearchStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aRoomListSearchState(), + aRoomListSearchState( + isSearchActive = true, + query = "Test", + results = aRoomListRoomSummaryList() + ), + ) +} + +fun aRoomListSearchState( + isSearchActive: Boolean = false, + query: String = "", + results: ImmutableList = persistentListOf(), +) = RoomListSearchState( + isSearchActive = isSearchActive, + query = query, + results = results, + eventSink = { }, +) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchResultView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchView.kt similarity index 73% rename from features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchResultView.kt rename to features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchView.kt index 2cf63393f9..eff6449449 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchResultView.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchView.kt @@ -16,6 +16,7 @@ package io.element.android.features.roomlist.impl.search +import androidx.activity.compose.BackHandler import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut @@ -25,32 +26,23 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.material3.TextFieldDefaults import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color -import androidx.compose.ui.input.nestedscroll.NestedScrollConnection -import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.Velocity +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.tokens.generated.CompoundIcons -import io.element.android.features.roomlist.impl.RoomListEvents -import io.element.android.features.roomlist.impl.RoomListState -import io.element.android.features.roomlist.impl.aRoomListState import io.element.android.features.roomlist.impl.components.RoomSummaryRow import io.element.android.features.roomlist.impl.contentType import io.element.android.features.roomlist.impl.model.RoomListRoomSummary @@ -68,26 +60,30 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.ui.strings.CommonStrings @Composable -internal fun RoomListSearchResultView( - state: RoomListState, +internal fun RoomListSearchView( + state: RoomListSearchState, onRoomClicked: (RoomId) -> Unit, onRoomLongClicked: (RoomListRoomSummary) -> Unit, modifier: Modifier = Modifier, ) { + BackHandler(enabled = state.isSearchActive) { + state.eventSink(RoomListSearchEvents.ToggleSearchVisibility) + } + AnimatedVisibility( - visible = state.displaySearchResults, + visible = state.isSearchActive, enter = fadeIn(), exit = fadeOut(), ) { Column( modifier = modifier - .applyIf(state.displaySearchResults, ifTrue = { + .applyIf(state.isSearchActive, ifTrue = { // Disable input interaction to underlying views pointerInput(Unit) {} }) ) { - if (state.displaySearchResults) { - RoomListSearchResultContent( + if (state.isSearchActive) { + RoomListSearchContent( state = state, onRoomClicked = onRoomClicked, onRoomLongClicked = onRoomLongClicked, @@ -99,15 +95,15 @@ internal fun RoomListSearchResultView( @OptIn(ExperimentalMaterial3Api::class) @Composable -private fun RoomListSearchResultContent( - state: RoomListState, +private fun RoomListSearchContent( + state: RoomListSearchState, onRoomClicked: (RoomId) -> Unit, onRoomLongClicked: (RoomListRoomSummary) -> Unit, ) { val borderColor = MaterialTheme.colorScheme.tertiary val strokeWidth = 1.dp fun onBackButtonPressed() { - state.eventSink(RoomListEvents.ToggleSearchResults) + state.eventSink(RoomListSearchEvents.ToggleSearchVisibility) } fun onRoomClicked(room: RoomListRoomSummary) { @@ -126,7 +122,7 @@ private fun RoomListSearchResultContent( }, navigationIcon = { BackButton(onClick = ::onBackButtonPressed) }, title = { - val filter = state.filter.orEmpty() + val filter = state.query val focusRequester = FocusRequester() TextField( modifier = Modifier @@ -134,7 +130,7 @@ private fun RoomListSearchResultContent( .focusRequester(focusRequester), value = filter, singleLine = true, - onValueChange = { state.eventSink(RoomListEvents.UpdateFilter(it)) }, + onValueChange = { state.eventSink(RoomListSearchEvents.QueryChanged(it)) }, colors = TextFieldDefaults.colors( focusedContainerColor = Color.Transparent, unfocusedContainerColor = Color.Transparent, @@ -147,7 +143,7 @@ private fun RoomListSearchResultContent( trailingIcon = { if (filter.isNotEmpty()) { IconButton(onClick = { - state.eventSink(RoomListEvents.UpdateFilter("")) + state.eventSink(RoomListSearchEvents.ClearQuery) }) { Icon( imageVector = CompoundIcons.Close(), @@ -158,8 +154,8 @@ private fun RoomListSearchResultContent( } ) - LaunchedEffect(state.displaySearchResults) { - if (state.displaySearchResults) { + LaunchedEffect(state.isSearchActive) { + if (state.isSearchActive) { focusRequester.requestFocus() } } @@ -168,39 +164,16 @@ private fun RoomListSearchResultContent( ) } ) { padding -> - val lazyListState = rememberLazyListState() - val visibleRange by remember { - derivedStateOf { - val layoutInfo = lazyListState.layoutInfo - val firstItemIndex = layoutInfo.visibleItemsInfo.firstOrNull()?.index ?: 0 - val size = layoutInfo.visibleItemsInfo.size - firstItemIndex until firstItemIndex + size - } - } - val nestedScrollConnection = remember { - object : NestedScrollConnection { - override suspend fun onPostFling( - consumed: Velocity, - available: Velocity - ): Velocity { - state.eventSink(RoomListEvents.UpdateVisibleRange(visibleRange)) - return super.onPostFling(consumed, available) - } - } - } Column( modifier = Modifier .padding(padding) .consumeWindowInsets(padding) ) { LazyColumn( - modifier = Modifier - .weight(1f) - .nestedScroll(nestedScrollConnection), - state = lazyListState, + modifier = Modifier.weight(1f), ) { items( - items = state.filteredRoomList, + items = state.results, contentType = { room -> room.contentType() }, ) { room -> RoomSummaryRow( @@ -216,9 +189,9 @@ private fun RoomListSearchResultContent( @PreviewsDayNight @Composable -internal fun RoomListSearchResultContentPreview() = ElementPreview { - RoomListSearchResultContent( - state = aRoomListState(), +internal fun RoomListSearchResultContentPreview(@PreviewParameter(RoomListSearchStateProvider::class) state: RoomListSearchState) = ElementPreview { + RoomListSearchContent( + state = state, onRoomClicked = {}, onRoomLongClicked = {} ) diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt index f106b72fb9..b2ad2fee23 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt @@ -33,6 +33,8 @@ import io.element.android.features.roomlist.impl.datasource.RoomListRoomSummaryF import io.element.android.features.roomlist.impl.migration.InMemoryMigrationScreenStore import io.element.android.features.roomlist.impl.migration.MigrationScreenPresenter import io.element.android.features.roomlist.impl.model.createRoomListRoomSummary +import io.element.android.features.roomlist.impl.search.RoomListSearchPresenter +import io.element.android.features.roomlist.impl.search.createRoomListSearchPresenter import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormatter import io.element.android.libraries.dateformatter.test.A_FORMATTED_DATE import io.element.android.libraries.dateformatter.test.FakeLastMessageTimestampFormatter @@ -54,7 +56,6 @@ import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatu import io.element.android.libraries.matrix.test.AN_AVATAR_URL 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_NAME import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.A_USER_NAME @@ -145,24 +146,6 @@ class RoomListPresenterTests { } } - @Test - fun `present - should filter room with success`() = runTest { - val scope = CoroutineScope(coroutineContext + SupervisorJob()) - val presenter = createRoomListPresenter(coroutineScope = scope) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - skipItems(1) - val withUserState = awaitItem() - assertThat(withUserState.filter).isEqualTo("") - withUserState.eventSink.invoke(RoomListEvents.UpdateFilter("t")) - val withFilterState = awaitItem() - assertThat(withFilterState.filter).isEqualTo("t") - cancelAndIgnoreRemainingEvents() - scope.cancel() - } - } - @Test fun `present - load 1 room with success`() = runTest { val roomListService = FakeRoomListService() @@ -196,51 +179,7 @@ class RoomListPresenterTests { numberOfUnreadMessages = 2, ) ) - scope.cancel() - } - } - - @Test - fun `present - load 1 room with success and filter rooms`() = runTest { - val roomListService = FakeRoomListService() - val matrixClient = FakeMatrixClient( - roomListService = roomListService - ) - val scope = CoroutineScope(coroutineContext + SupervisorJob()) - val presenter = createRoomListPresenter(client = matrixClient, coroutineScope = scope) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - roomListService.postAllRooms( - listOf( - aRoomSummaryFilled( - numUnreadMentions = 1, - numUnreadMessages = 2, - ) - ) - ) - skipItems(3) - val loadedState = awaitItem() - // Test filtering with result - assertThat(loadedState.roomList.dataOrNull().orEmpty().size).isEqualTo(1) - loadedState.eventSink.invoke(RoomListEvents.UpdateFilter(A_ROOM_NAME.substring(0, 3))) - skipItems(1) - val withFilteredRoomState = awaitItem() - assertThat(withFilteredRoomState.filteredRoomList.size).isEqualTo(1) - assertThat(withFilteredRoomState.filter).isEqualTo(A_ROOM_NAME.substring(0, 3)) - assertThat(withFilteredRoomState.filteredRoomList.size).isEqualTo(1) - assertThat(withFilteredRoomState.filteredRoomList.first()).isEqualTo( - createRoomListRoomSummary( - numberOfUnreadMentions = 1, - numberOfUnreadMessages = 2, - ) - ) - // Test filtering without result - withFilteredRoomState.eventSink.invoke(RoomListEvents.UpdateFilter("tada")) - skipItems(1) - val withNotFilteredRoomState = awaitItem() - assertThat(withNotFilteredRoomState.filter).isEqualTo("tada") - assertThat(withNotFilteredRoomState.filteredRoomList).isEmpty() + cancelAndIgnoreRemainingEvents() scope.cancel() } } @@ -572,7 +511,8 @@ class RoomListPresenterTests { migrationScreenPresenter: MigrationScreenPresenter = MigrationScreenPresenter( matrixClient = client, migrationScreenStore = InMemoryMigrationScreenStore(), - ) + ), + searchPresenter: RoomListSearchPresenter = createRoomListSearchPresenter(roomListService = client.roomListService) ) = RoomListPresenter( client = client, sessionVerificationService = sessionVerificationService, @@ -598,6 +538,7 @@ class RoomListPresenterTests { featureFlagService = FakeFeatureFlagService(mapOf(FeatureFlags.SecureStorage.key to true)), ), migrationScreenPresenter = migrationScreenPresenter, + searchPresenter = searchPresenter, sessionPreferencesStore = sessionPreferencesStore, ) } diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchPresenterTests.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchPresenterTests.kt new file mode 100644 index 0000000000..fb124356e1 --- /dev/null +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchPresenterTests.kt @@ -0,0 +1,151 @@ +/* + * 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.roomlist.impl.search + +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.roomlist.impl.datasource.RoomListRoomSummaryFactory +import io.element.android.libraries.dateformatter.test.FakeLastMessageTimestampFormatter +import io.element.android.libraries.eventformatter.test.FakeRoomLastMessageFormatter +import io.element.android.libraries.matrix.api.roomlist.RoomListFilter +import io.element.android.libraries.matrix.api.roomlist.RoomListService +import io.element.android.libraries.matrix.api.roomlist.RoomSummary +import io.element.android.libraries.matrix.test.room.aRoomSummaryFilled +import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService +import io.element.android.tests.testutils.testCoroutineDispatchers +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class RoomListSearchPresenterTests { + @Test + fun `present - initial state`() = runTest { + val presenter = createRoomListSearchPresenter() + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem().let { state -> + assertThat(state.isSearchActive).isFalse() + assertThat(state.query).isEmpty() + assertThat(state.results).isEmpty() + } + } + } + + @Test + fun `present - toggle search visibility`() = runTest { + val presenter = createRoomListSearchPresenter() + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem().let { state -> + assertThat(state.isSearchActive).isFalse() + state.eventSink(RoomListSearchEvents.ToggleSearchVisibility) + } + awaitItem().let { state -> + assertThat(state.isSearchActive).isTrue() + state.eventSink(RoomListSearchEvents.ToggleSearchVisibility) + } + awaitItem().let { state -> + assertThat(state.isSearchActive).isFalse() + } + } + } + + @Test + fun `present - query search changes`() = runTest { + val roomListService = FakeRoomListService() + val presenter = createRoomListSearchPresenter(roomListService) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem().let { state -> + assertThat( + roomListService.allRooms.currentFilter.value + ).isEqualTo( + RoomListFilter.all( + RoomListFilter.None, + ) + ) + state.eventSink(RoomListSearchEvents.QueryChanged("Search")) + } + awaitItem().let { state -> + assertThat(state.query).isEqualTo("Search") + assertThat( + roomListService.allRooms.currentFilter.value + ).isEqualTo( + RoomListFilter.all( + RoomListFilter.NonLeft, + RoomListFilter.NormalizedMatchRoomName("Search") + ) + ) + state.eventSink(RoomListSearchEvents.ClearQuery) + } + awaitItem().let { state -> + assertThat(state.query).isEmpty() + assertThat( + roomListService.allRooms.currentFilter.value + ).isEqualTo( + RoomListFilter.all( + RoomListFilter.None, + ) + ) + } + } + } + + @Test + fun `present - room list changes`() = runTest { + val roomListService = FakeRoomListService() + val presenter = createRoomListSearchPresenter(roomListService) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem().let { state -> + assertThat(state.results).isEmpty() + } + roomListService.postAllRooms( + listOf( + RoomSummary.Empty("1"), + aRoomSummaryFilled() + ) + ) + awaitItem().let { state -> + assertThat(state.results).hasSize(1) + } + roomListService.postAllRooms(emptyList()) + awaitItem().let { state -> + assertThat(state.results).isEmpty() + } + } + } +} + +fun TestScope.createRoomListSearchPresenter( + roomListService: RoomListService = FakeRoomListService(), +): RoomListSearchPresenter { + return RoomListSearchPresenter( + roomListService = roomListService, + roomSummaryFactory = RoomListRoomSummaryFactory( + lastMessageTimestampFormatter = FakeLastMessageTimestampFormatter(), + roomLastMessageFormatter = FakeRoomLastMessageFormatter(), + ), + coroutineDispatchers = testCoroutineDispatchers(), + ) +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomList.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomList.kt index 5ffc58c332..9b42fbc70b 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomList.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomList.kt @@ -33,6 +33,11 @@ interface RoomList { data class Loaded(val numberOfRooms: Int) : LoadingState } + enum class Source { + All, + Invites, + } + /** * The list of room summaries as a flow. */ 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 99ba4531e2..41f5240a19 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 @@ -18,38 +18,68 @@ package io.element.android.libraries.matrix.api.roomlist sealed interface RoomListFilter { companion object { + /** + * Create a filter that matches all the given filters. + */ fun all(vararg filters: RoomListFilter): RoomListFilter { return All(filters.toList()) } + /** + * Create a filter that matches any of the given filters. + */ fun any(vararg filters: RoomListFilter): RoomListFilter { return Any(filters.toList()) } } + /** + * A filter that matches all the given filters. + */ data class All( val filters: List ) : RoomListFilter + /** + * A filter that matches any of the given filters. + */ data class Any( val filters: List ) : RoomListFilter + /** + * A filter that matches rooms that are not left. + */ data object NonLeft : RoomListFilter + /** + * A filter that matches rooms that are unread. + */ data object Unread : RoomListFilter + /** + * A filter that matches either Group or People rooms. + */ sealed interface Category : RoomListFilter { data object Group : Category data object People : Category } + /** + * A filter that matches no room. + */ data object None : RoomListFilter + /** + * A filter that matches rooms with a name using a normalized match. + */ data class NormalizedMatchRoomName( val pattern: String ) : RoomListFilter + /** + * A filter that matches rooms with a name using a fuzzy match. + */ data class FuzzyMatchRoomName( val pattern: String ) : RoomListFilter diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListService.kt index c13e6ecad9..04018b7bea 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListService.kt @@ -17,6 +17,7 @@ package io.element.android.libraries.matrix.api.roomlist import androidx.compose.runtime.Immutable +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.StateFlow /** @@ -39,6 +40,20 @@ interface RoomListService { data object Hide : SyncIndicator } + /** + * Creates a room list that can be used to load more rooms and filter them dynamically. + * @param coroutineScope the scope to use for the room list. When the scope will be closed, the room list will be closed too. + * @param pageSize the number of rooms to load at once. + * @param initialFilter the initial filter to apply to the rooms. + * @param source the source of the rooms, either all rooms or invites. + */ + fun createRoomList( + coroutineScope: CoroutineScope, + pageSize: Int, + initialFilter: RoomListFilter, + source: RoomList.Source, + ): DynamicRoomList + /** * returns a [DynamicRoomList] object of all rooms we want to display. * This will exclude some rooms like the invites, or spaces. diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index f7213becc4..34d479e394 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -199,8 +199,8 @@ class RustMatrixClient( sessionCoroutineScope = sessionCoroutineScope, roomListFactory = RoomListFactory( innerRoomListService = innerRoomListService, - coroutineScope = sessionCoroutineScope, - dispatcher = sessionDispatcher, + defaultCoroutineScope = sessionCoroutineScope, + defaultCoroutineContext = sessionDispatcher, ), ) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt index f0d9c7e2a4..1c77c5c5c5 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt @@ -20,7 +20,6 @@ import io.element.android.libraries.matrix.api.roomlist.DynamicRoomList 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.roomlist.RoomSummary -import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -30,13 +29,15 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.matrix.rustcomponents.sdk.RoomListLoadingState +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext import org.matrix.rustcomponents.sdk.RoomList as InnerRoomList import org.matrix.rustcomponents.sdk.RoomListService as InnerRoomListService internal class RoomListFactory( private val innerRoomListService: InnerRoomListService, - private val coroutineScope: CoroutineScope, - private val dispatcher: CoroutineDispatcher, + private val defaultCoroutineScope: CoroutineScope, + private val defaultCoroutineContext: CoroutineContext = EmptyCoroutineContext, private val roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory(), ) { /** @@ -44,18 +45,21 @@ internal class RoomListFactory( */ fun createRoomList( pageSize: Int, + coroutineScope: CoroutineScope = defaultCoroutineScope, + coroutineContext: CoroutineContext = defaultCoroutineContext, initialFilter: RoomListFilter = RoomListFilter.all(), innerProvider: suspend () -> InnerRoomList ): DynamicRoomList { val loadingStateFlow: MutableStateFlow = MutableStateFlow(RoomList.LoadingState.NotLoaded) val summariesFlow = MutableStateFlow>(emptyList()) - val processor = RoomSummaryListProcessor(summariesFlow, innerRoomListService, dispatcher, roomSummaryDetailsFactory) + val processor = RoomSummaryListProcessor(summariesFlow, innerRoomListService, coroutineContext, roomSummaryDetailsFactory) // Makes sure we don't miss any events val dynamicEvents = MutableSharedFlow(replay = 100) val currentFilter = MutableStateFlow(initialFilter) val loadedPages = MutableStateFlow(1) var innerRoomList: InnerRoomList? = null - coroutineScope.launch(dispatcher) { + + coroutineScope.launch(coroutineContext) { innerRoomList = innerProvider() innerRoomList?.let { innerRoomList -> innerRoomList.entriesFlow( diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessor.kt index 5525d802bf..a5028bb614 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessor.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessor.kt @@ -17,7 +17,6 @@ package io.element.android.libraries.matrix.impl.roomlist import io.element.android.libraries.matrix.api.roomlist.RoomSummary -import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock @@ -28,11 +27,12 @@ import org.matrix.rustcomponents.sdk.RoomListServiceInterface import org.matrix.rustcomponents.sdk.use import timber.log.Timber import java.util.UUID +import kotlin.coroutines.CoroutineContext class RoomSummaryListProcessor( private val roomSummaries: MutableStateFlow>, private val roomListService: RoomListServiceInterface, - private val dispatcher: CoroutineDispatcher, + private val coroutineContext: CoroutineContext, private val roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory(), ) { private val roomSummariesByIdentifier = HashMap() @@ -130,7 +130,7 @@ class RoomSummaryListProcessor( return builtRoomSummary } - private suspend fun updateRoomSummaries(block: suspend MutableList.() -> Unit) = withContext(dispatcher) { + private suspend fun updateRoomSummaries(block: suspend MutableList.() -> Unit) = withContext(coroutineContext) { mutex.withLock { val mutableRoomSummaries = roomSummaries.value.toMutableList() block(mutableRoomSummaries) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt index 4fef34b571..0c48dc2a2f 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt @@ -42,8 +42,26 @@ private const val DEFAULT_PAGE_SIZE = 20 internal class RustRoomListService( private val innerRoomListService: InnerRustRoomListService, private val sessionCoroutineScope: CoroutineScope, - roomListFactory: RoomListFactory, + private val roomListFactory: RoomListFactory, ) : RoomListService { + override fun createRoomList( + coroutineScope: CoroutineScope, + pageSize: Int, + initialFilter: RoomListFilter, + source: RoomList.Source + ): DynamicRoomList { + return roomListFactory.createRoomList( + pageSize = pageSize, + initialFilter = initialFilter, + coroutineScope = coroutineScope, + ) { + when (source) { + RoomList.Source.All -> innerRoomListService.allRooms() + RoomList.Source.Invites -> innerRoomListService.invites() + } + } + } + override val allRooms: DynamicRoomList = roomListFactory.createRoomList( pageSize = DEFAULT_PAGE_SIZE, initialFilter = RoomListFilter.all(RoomListFilter.NonLeft), diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessorTests.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessorTests.kt index a9b0fea454..3812605546 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessorTests.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessorTests.kt @@ -158,7 +158,7 @@ class RoomSummaryListProcessorTests { private fun TestScope.createProcessor() = RoomSummaryListProcessor( summaries, fakeRoomListService, - dispatcher = StandardTestDispatcher(testScheduler), + coroutineContext = StandardTestDispatcher(testScheduler), roomSummaryDetailsFactory = RoomSummaryDetailsFactory(), ) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt index a1ef41b742..e0b0c38d4d 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt @@ -53,6 +53,10 @@ fun aRoomSummaryFilled( ) ) +fun aRoomSummaryFilled( + details: RoomSummaryDetails = aRoomSummaryDetails(), +) = RoomSummary.Filled(details) + fun aRoomSummaryDetails( roomId: RoomId = A_ROOM_ID, name: String = A_ROOM_NAME, diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/FakeRoomListService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/FakeRoomListService.kt index 7540d6cee8..d53596e462 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/FakeRoomListService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/FakeRoomListService.kt @@ -21,6 +21,7 @@ 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.roomlist.RoomListService import io.element.android.libraries.matrix.api.roomlist.RoomSummary +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -59,13 +60,20 @@ class FakeRoomListService : RoomListService { var latestSlidingSyncRange: IntRange? = null private set - override val allRooms: DynamicRoomList = SimplePagedRoomList( + override fun createRoomList(coroutineScope: CoroutineScope, pageSize: Int, initialFilter: RoomListFilter, source: RoomList.Source): DynamicRoomList { + return when (source) { + RoomList.Source.All -> allRooms + RoomList.Source.Invites -> invites + } + } + + override val allRooms = SimplePagedRoomList( allRoomSummariesFlow, allRoomsLoadingStateFlow, MutableStateFlow(RoomListFilter.all()) ) - override val invites: RoomList = SimplePagedRoomList( + override val invites = SimplePagedRoomList( inviteRoomSummariesFlow, inviteRoomsLoadingStateFlow, MutableStateFlow(RoomListFilter.all()) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/SimplePagedRoomList.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/SimplePagedRoomList.kt index 4f1b07ce69..5ff9ed08bf 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/SimplePagedRoomList.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/SimplePagedRoomList.kt @@ -25,7 +25,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.getAndUpdate data class SimplePagedRoomList( - override val summaries: StateFlow>, + override val summaries: MutableStateFlow>, override val loadingState: StateFlow, override val currentFilter: MutableStateFlow ) : DynamicRoomList { From 3983670f8d5f55d3a9facf9271c15b74a02f2df2 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 16 Feb 2024 19:45:11 +0100 Subject: [PATCH 2/7] RoomList : fix sample compilation --- .../android/samples/minimal/RoomListScreen.kt | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt index 218dfdd697..58981000f0 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt @@ -30,6 +30,7 @@ import io.element.android.features.roomlist.impl.datasource.RoomListDataSource import io.element.android.features.roomlist.impl.datasource.RoomListRoomSummaryFactory import io.element.android.features.roomlist.impl.migration.MigrationScreenPresenter import io.element.android.features.roomlist.impl.migration.SharedPrefsMigrationScreenStore +import io.element.android.features.roomlist.impl.search.RoomListSearchPresenter import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.dateformatter.impl.DateFormatters import io.element.android.libraries.dateformatter.impl.DefaultLastMessageTimestampFormatter @@ -71,6 +72,21 @@ class RoomListScreen( private val featureFlagService = DefaultFeatureFlagService( providers = setOf(StaticFeatureFlagProvider()) ) + private val roomListRoomSummaryFactory = RoomListRoomSummaryFactory( + lastMessageTimestampFormatter = DefaultLastMessageTimestampFormatter( + localDateTimeProvider = dateTimeProvider, + dateFormatters = dateFormatters + ), + roomLastMessageFormatter = DefaultRoomLastMessageFormatter( + sp = stringProvider, + roomMembershipContentFormatter = RoomMembershipContentFormatter( + matrixClient = matrixClient, + sp = stringProvider + ), + profileChangeContentFormatter = ProfileChangeContentFormatter(stringProvider), + stateContentFormatter = StateContentFormatter(stringProvider), + ), + ) private val presenter = RoomListPresenter( client = matrixClient, sessionVerificationService = sessionVerificationService, @@ -80,21 +96,7 @@ class RoomListScreen( leaveRoomPresenter = LeaveRoomPresenterImpl(matrixClient, RoomMembershipObserver(), coroutineDispatchers), roomListDataSource = RoomListDataSource( roomListService = matrixClient.roomListService, - roomListRoomSummaryFactory = RoomListRoomSummaryFactory( - lastMessageTimestampFormatter = DefaultLastMessageTimestampFormatter( - localDateTimeProvider = dateTimeProvider, - dateFormatters = dateFormatters - ), - roomLastMessageFormatter = DefaultRoomLastMessageFormatter( - sp = stringProvider, - roomMembershipContentFormatter = RoomMembershipContentFormatter( - matrixClient = matrixClient, - sp = stringProvider - ), - profileChangeContentFormatter = ProfileChangeContentFormatter(stringProvider), - stateContentFormatter = StateContentFormatter(stringProvider), - ), - ), + roomListRoomSummaryFactory = roomListRoomSummaryFactory, coroutineDispatchers = coroutineDispatchers, notificationSettingsService = matrixClient.notificationSettingsService(), appScope = Singleton.appScope @@ -110,6 +112,11 @@ class RoomListScreen( matrixClient = matrixClient, migrationScreenStore = SharedPrefsMigrationScreenStore(context.getSharedPreferences("migration", Context.MODE_PRIVATE)) ), + searchPresenter = RoomListSearchPresenter( + roomListService = matrixClient.roomListService, + roomSummaryFactory = roomListRoomSummaryFactory, + coroutineDispatchers = coroutineDispatchers, + ), sessionPreferencesStore = DefaultSessionPreferencesStore( context = context, sessionId = matrixClient.sessionId, From 93328579784cff53a6dbcf740947450a2848230b Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 16 Feb 2024 19:46:56 +0100 Subject: [PATCH 3/7] RoomList : fix formatting --- .../features/roomlist/impl/search/RoomListSearchPresenter.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchPresenter.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchPresenter.kt index 5beaa8077a..6917a505c3 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchPresenter.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchPresenter.kt @@ -46,10 +46,8 @@ class RoomListSearchPresenter @Inject constructor( private val roomSummaryFactory: RoomListRoomSummaryFactory, private val coroutineDispatchers: CoroutineDispatchers, ) : Presenter { - @Composable override fun present(): RoomListSearchState { - var isSearchActive by rememberSaveable { mutableStateOf(false) } From 9e57156d34c47b3def55b5307bd39a3d8a82cf6c Mon Sep 17 00:00:00 2001 From: ElementBot Date: Fri, 16 Feb 2024 20:18:28 +0000 Subject: [PATCH 4/7] Update screenshots --- ...ListSearchResultContent-Day-11_12_null,NEXUS_5,1.0,en].png | 3 --- ...stSearchResultContent-Day-11_12_null_0,NEXUS_5,1.0,en].png | 3 +++ ...stSearchResultContent-Day-11_12_null_1,NEXUS_5,1.0,en].png | 3 +++ ...stSearchResultContent-Night-11_13_null,NEXUS_5,1.0,en].png | 3 --- ...SearchResultContent-Night-11_13_null_0,NEXUS_5,1.0,en].png | 3 +++ ...SearchResultContent-Night-11_13_null_1,NEXUS_5,1.0,en].png | 3 +++ ...View_null_RoomListView-Day-3_4_null_10,NEXUS_5,1.0,en].png | 4 ++-- ...View_null_RoomListView-Day-3_4_null_11,NEXUS_5,1.0,en].png | 4 ++-- ...View_null_RoomListView-Day-3_4_null_12,NEXUS_5,1.0,en].png | 4 ++-- ...View_null_RoomListView-Day-3_4_null_13,NEXUS_5,1.0,en].png | 3 --- ...tView_null_RoomListView-Day-3_4_null_6,NEXUS_5,1.0,en].png | 4 ++-- ...tView_null_RoomListView-Day-3_4_null_7,NEXUS_5,1.0,en].png | 4 ++-- ...tView_null_RoomListView-Day-3_4_null_8,NEXUS_5,1.0,en].png | 4 ++-- ...tView_null_RoomListView-Day-3_4_null_9,NEXUS_5,1.0,en].png | 4 ++-- ...ew_null_RoomListView-Night-3_5_null_10,NEXUS_5,1.0,en].png | 4 ++-- ...ew_null_RoomListView-Night-3_5_null_11,NEXUS_5,1.0,en].png | 4 ++-- ...ew_null_RoomListView-Night-3_5_null_12,NEXUS_5,1.0,en].png | 4 ++-- ...ew_null_RoomListView-Night-3_5_null_13,NEXUS_5,1.0,en].png | 3 --- ...iew_null_RoomListView-Night-3_5_null_6,NEXUS_5,1.0,en].png | 4 ++-- ...iew_null_RoomListView-Night-3_5_null_7,NEXUS_5,1.0,en].png | 4 ++-- ...iew_null_RoomListView-Night-3_5_null_8,NEXUS_5,1.0,en].png | 4 ++-- ...iew_null_RoomListView-Night-3_5_null_9,NEXUS_5,1.0,en].png | 4 ++-- 22 files changed, 40 insertions(+), 40 deletions(-) delete mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-11_12_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-11_12_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-11_12_null_1,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-11_13_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-11_13_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-11_13_null_1,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_13,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_13,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-11_12_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-11_12_null,NEXUS_5,1.0,en].png deleted file mode 100644 index adacc8d439..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-11_12_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b2dc223ad7ca556a74aaff1e0ec6a94c10b951457c7b9614804a4c477c4e6e8e -size 29974 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-11_12_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-11_12_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..8a996b32af --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-11_12_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f313e09baa93bb4ff9eda9a3126b2cf49867fac3c1d53f11ec027b4dad7fbff +size 4909 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-11_12_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-11_12_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..5cc0279077 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Day-11_12_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1625ac34428f660c235d1d64f5de867baa6c0ca296f0a93d81588d633d6a74bf +size 30082 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-11_13_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-11_13_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 2f2649c98c..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-11_13_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:199f1cf052e17b08810d026921b53ee35b1a942f9b661beb2286e6891843898c -size 29867 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-11_13_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-11_13_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..205774d78d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-11_13_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6e5a9aba28b5a7dfa41b8ca47e74ebe629f717d9bd51836d71c31adb436083af +size 4861 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-11_13_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-11_13_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f7230cc774 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.search_RoomListSearchResultContent_null_RoomListSearchResultContent-Night-11_13_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:264d9373767b6b59d0cea4bfa4d148a453058be70e7805581fe96d7448ef5232 +size 29978 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_10,NEXUS_5,1.0,en].png index 4e360c7666..06846f3231 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f8802121f779f48dd556a9a3a08e6ac1a63e86d5695f6f8c133fbb346d76ef6 -size 89779 +oid sha256:13dadbd502163a9bfe81cb1e67f9aa0933c4b7f4fd3c8f731e930a28709485d2 +size 51948 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_11,NEXUS_5,1.0,en].png index cdfb465771..f901e915e5 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_11,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_11,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2523a28a889fd1739fe1e0c94edce5d86a5ebcbe6c3973a3c49fbb8fb8a19f79 -size 55617 +oid sha256:64c4eb481f40871925405ae317cb80927caf31cb552a8aa9549bfb5658ca91e4 +size 137589 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_12,NEXUS_5,1.0,en].png index 06846f3231..3057c59ef7 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_12,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_12,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:13dadbd502163a9bfe81cb1e67f9aa0933c4b7f4fd3c8f731e930a28709485d2 -size 51948 +oid sha256:af42c9891a6670cdde07cf054e139ce1f83e877bb68f60463fcad6dd28d8e049 +size 6867 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_13,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_13,NEXUS_5,1.0,en].png deleted file mode 100644 index f901e915e5..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_13,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:64c4eb481f40871925405ae317cb80927caf31cb552a8aa9549bfb5658ca91e4 -size 137589 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_6,NEXUS_5,1.0,en].png index 8a996b32af..2f2ac0e0c0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f313e09baa93bb4ff9eda9a3126b2cf49867fac3c1d53f11ec027b4dad7fbff -size 4909 +oid sha256:3764d8bd7dc2783a8af43aad65a217d7e533ed17c4d4367b7994470bf35b62b0 +size 4462 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_7,NEXUS_5,1.0,en].png index adacc8d439..2f2ac0e0c0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b2dc223ad7ca556a74aaff1e0ec6a94c10b951457c7b9614804a4c477c4e6e8e -size 29974 +oid sha256:3764d8bd7dc2783a8af43aad65a217d7e533ed17c4d4367b7994470bf35b62b0 +size 4462 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_8,NEXUS_5,1.0,en].png index 2f2ac0e0c0..4e360c7666 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3764d8bd7dc2783a8af43aad65a217d7e533ed17c4d4367b7994470bf35b62b0 -size 4462 +oid sha256:5f8802121f779f48dd556a9a3a08e6ac1a63e86d5695f6f8c133fbb346d76ef6 +size 89779 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_9,NEXUS_5,1.0,en].png index 2f2ac0e0c0..cdfb465771 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-3_4_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3764d8bd7dc2783a8af43aad65a217d7e533ed17c4d4367b7994470bf35b62b0 -size 4462 +oid sha256:2523a28a889fd1739fe1e0c94edce5d86a5ebcbe6c3973a3c49fbb8fb8a19f79 +size 55617 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_10,NEXUS_5,1.0,en].png index a751592e9b..65b76802d2 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7cdd93c157565f7f8d3fa23a43c30e02531dab40578a315c5d0d10a99e74f259 -size 91394 +oid sha256:0a00391ef16a762a14dd7e348ce97b500ca1b42a32ee3ff926a10865f46cd06c +size 53590 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_11,NEXUS_5,1.0,en].png index a7fd32602e..56f7f133f3 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_11,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_11,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2b25e023153dd094299a0c794d5e488bdc6fc0c478abbfbce7337e52b233715d -size 57463 +oid sha256:a7bf47b0a25c455b108d7b3585a42849f03c6652af73a175fe2147cb1ad62a66 +size 161125 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_12,NEXUS_5,1.0,en].png index 65b76802d2..08cfca5c0b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_12,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_12,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0a00391ef16a762a14dd7e348ce97b500ca1b42a32ee3ff926a10865f46cd06c -size 53590 +oid sha256:6e95dac6e75d3f615ccf4d8e98f1780d8aeace1cddf0b33bba8b485860d3216b +size 6688 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_13,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_13,NEXUS_5,1.0,en].png deleted file mode 100644 index 56f7f133f3..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_13,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a7bf47b0a25c455b108d7b3585a42849f03c6652af73a175fe2147cb1ad62a66 -size 161125 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_6,NEXUS_5,1.0,en].png index 205774d78d..2f2ac0e0c0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6e5a9aba28b5a7dfa41b8ca47e74ebe629f717d9bd51836d71c31adb436083af -size 4861 +oid sha256:3764d8bd7dc2783a8af43aad65a217d7e533ed17c4d4367b7994470bf35b62b0 +size 4462 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_7,NEXUS_5,1.0,en].png index 2f2649c98c..2f2ac0e0c0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:199f1cf052e17b08810d026921b53ee35b1a942f9b661beb2286e6891843898c -size 29867 +oid sha256:3764d8bd7dc2783a8af43aad65a217d7e533ed17c4d4367b7994470bf35b62b0 +size 4462 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_8,NEXUS_5,1.0,en].png index 2f2ac0e0c0..a751592e9b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3764d8bd7dc2783a8af43aad65a217d7e533ed17c4d4367b7994470bf35b62b0 -size 4462 +oid sha256:7cdd93c157565f7f8d3fa23a43c30e02531dab40578a315c5d0d10a99e74f259 +size 91394 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_9,NEXUS_5,1.0,en].png index 2f2ac0e0c0..a7fd32602e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-3_5_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3764d8bd7dc2783a8af43aad65a217d7e533ed17c4d4367b7994470bf35b62b0 -size 4462 +oid sha256:2b25e023153dd094299a0c794d5e488bdc6fc0c478abbfbce7337e52b233715d +size 57463 From 9a528af65ba257f0b70dda025749edc89e840e82 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 16 Feb 2024 23:22:41 +0100 Subject: [PATCH 5/7] RoomList : remove direct dependency to the RoomListSearchPresenter --- .../roomlist/impl/RoomListPresenter.kt | 4 +-- .../roomlist/impl/di/RoomListModule.kt | 32 +++++++++++++++++++ .../roomlist/impl/RoomListPresenterTests.kt | 8 ++--- 3 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/di/RoomListModule.kt diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt index 7f53f90e92..6edc533476 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt @@ -38,7 +38,7 @@ import io.element.android.features.roomlist.impl.datasource.InviteStateDataSourc import io.element.android.features.roomlist.impl.datasource.RoomListDataSource import io.element.android.features.roomlist.impl.migration.MigrationScreenPresenter import io.element.android.features.roomlist.impl.search.RoomListSearchEvents -import io.element.android.features.roomlist.impl.search.RoomListSearchPresenter +import io.element.android.features.roomlist.impl.search.RoomListSearchState import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher @@ -78,7 +78,7 @@ class RoomListPresenter @Inject constructor( private val encryptionService: EncryptionService, private val featureFlagService: FeatureFlagService, private val indicatorService: IndicatorService, - private val searchPresenter: RoomListSearchPresenter, + private val searchPresenter: Presenter, private val migrationScreenPresenter: MigrationScreenPresenter, private val sessionPreferencesStore: SessionPreferencesStore, ) : Presenter { diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/di/RoomListModule.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/di/RoomListModule.kt new file mode 100644 index 0000000000..369da2fc75 --- /dev/null +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/di/RoomListModule.kt @@ -0,0 +1,32 @@ +/* + * 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.roomlist.impl.di + +import com.squareup.anvil.annotations.ContributesTo +import dagger.Binds +import dagger.Module +import io.element.android.features.roomlist.impl.search.RoomListSearchPresenter +import io.element.android.features.roomlist.impl.search.RoomListSearchState +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.di.SessionScope + +@ContributesTo(SessionScope::class) +@Module +interface RoomListModule { + @Binds + fun bindSearchPresenter(presenter: RoomListSearchPresenter): Presenter +} diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt index b2ad2fee23..971082a45f 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt @@ -33,8 +33,9 @@ import io.element.android.features.roomlist.impl.datasource.RoomListRoomSummaryF import io.element.android.features.roomlist.impl.migration.InMemoryMigrationScreenStore import io.element.android.features.roomlist.impl.migration.MigrationScreenPresenter import io.element.android.features.roomlist.impl.model.createRoomListRoomSummary -import io.element.android.features.roomlist.impl.search.RoomListSearchPresenter -import io.element.android.features.roomlist.impl.search.createRoomListSearchPresenter +import io.element.android.features.roomlist.impl.search.RoomListSearchState +import io.element.android.features.roomlist.impl.search.aRoomListSearchState +import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormatter import io.element.android.libraries.dateformatter.test.A_FORMATTED_DATE import io.element.android.libraries.dateformatter.test.FakeLastMessageTimestampFormatter @@ -454,7 +455,6 @@ class RoomListPresenterTests { // The migration screen is not shown anymore assertThat(awaitItem().displayMigrationStatus).isFalse() - cancelAndIgnoreRemainingEvents() scope.cancel() } } @@ -512,7 +512,7 @@ class RoomListPresenterTests { matrixClient = client, migrationScreenStore = InMemoryMigrationScreenStore(), ), - searchPresenter: RoomListSearchPresenter = createRoomListSearchPresenter(roomListService = client.roomListService) + searchPresenter: Presenter = Presenter { aRoomListSearchState() }, ) = RoomListPresenter( client = client, sessionVerificationService = sessionVerificationService, From 3d1665a73218c80d7fa1d9e1bb75abd5fcc07d05 Mon Sep 17 00:00:00 2001 From: ganfra Date: Sat, 17 Feb 2024 00:46:41 +0100 Subject: [PATCH 6/7] RoomList: try to fix the test in ci... --- .../android/features/roomlist/impl/RoomListPresenterTests.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt index 971082a45f..025caa3f96 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt @@ -79,6 +79,7 @@ import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test +import kotlin.time.Duration.Companion.seconds class RoomListPresenterTests { @get:Rule @@ -158,7 +159,7 @@ class RoomListPresenterTests { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = consumeItemsUntilPredicate { state -> state.roomList.dataOrNull()?.size == 16 }.last() + val initialState = consumeItemsUntilPredicate(timeout = 3.seconds) { state -> state.roomList.dataOrNull()?.size == 16 }.last() // Room list is loaded with 16 placeholders val initialItems = initialState.roomList.dataOrNull().orEmpty() assertThat(initialItems.size).isEqualTo(16) From 1c5f0389f74aa4987221caa82f963d9c5d16d12c Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 19 Feb 2024 12:08:11 +0100 Subject: [PATCH 7/7] RoomList : clean after PR review --- .../android/libraries/matrix/api/roomlist/RoomList.kt | 10 ++++++++++ .../android/libraries/matrix/impl/RustMatrixClient.kt | 4 ++-- .../libraries/matrix/impl/roomlist/RoomListFactory.kt | 11 +++++------ .../matrix/impl/roomlist/RustRoomListService.kt | 5 +++++ 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomList.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomList.kt index 9b42fbc70b..0132e35092 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomList.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomList.kt @@ -28,11 +28,21 @@ import kotlin.time.Duration * Can be retrieved from [RoomListService] methods. */ interface RoomList { + /** + * The loading state of the room list. + */ sealed interface LoadingState { data object NotLoaded : LoadingState data class Loaded(val numberOfRooms: Int) : LoadingState } + /** + * The source of the room list data. + * All: all rooms except invites. + * Invites: only invites. + * + * To apply some dynamic filtering on top of that, use [DynamicRoomList]. + */ enum class Source { All, Invites, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 34d479e394..a98596d227 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -197,10 +197,10 @@ class RustMatrixClient( RustRoomListService( innerRoomListService = innerRoomListService, sessionCoroutineScope = sessionCoroutineScope, + sessionDispatcher = sessionDispatcher, roomListFactory = RoomListFactory( innerRoomListService = innerRoomListService, - defaultCoroutineScope = sessionCoroutineScope, - defaultCoroutineContext = sessionDispatcher, + sessionCoroutineScope = sessionCoroutineScope, ), ) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt index 1c77c5c5c5..421a1296a8 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt @@ -29,15 +29,14 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.matrix.rustcomponents.sdk.RoomListLoadingState +import org.matrix.rustcomponents.sdk.RoomListService import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext import org.matrix.rustcomponents.sdk.RoomList as InnerRoomList -import org.matrix.rustcomponents.sdk.RoomListService as InnerRoomListService internal class RoomListFactory( - private val innerRoomListService: InnerRoomListService, - private val defaultCoroutineScope: CoroutineScope, - private val defaultCoroutineContext: CoroutineContext = EmptyCoroutineContext, + private val innerRoomListService: RoomListService, + private val sessionCoroutineScope: CoroutineScope, private val roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory(), ) { /** @@ -45,8 +44,8 @@ internal class RoomListFactory( */ fun createRoomList( pageSize: Int, - coroutineScope: CoroutineScope = defaultCoroutineScope, - coroutineContext: CoroutineContext = defaultCoroutineContext, + coroutineScope: CoroutineScope = sessionCoroutineScope, + coroutineContext: CoroutineContext = EmptyCoroutineContext, initialFilter: RoomListFilter = RoomListFilter.all(), innerProvider: suspend () -> InnerRoomList ): DynamicRoomList { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt index 0c48dc2a2f..413c38be6e 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt @@ -21,6 +21,7 @@ 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.roomlist.RoomListService import io.element.android.libraries.matrix.api.roomlist.loadAllIncrementally +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -42,6 +43,7 @@ private const val DEFAULT_PAGE_SIZE = 20 internal class RustRoomListService( private val innerRoomListService: InnerRustRoomListService, private val sessionCoroutineScope: CoroutineScope, + private val sessionDispatcher: CoroutineDispatcher, private val roomListFactory: RoomListFactory, ) : RoomListService { override fun createRoomList( @@ -54,6 +56,7 @@ internal class RustRoomListService( pageSize = pageSize, initialFilter = initialFilter, coroutineScope = coroutineScope, + coroutineContext = sessionDispatcher, ) { when (source) { RoomList.Source.All -> innerRoomListService.allRooms() @@ -64,6 +67,7 @@ internal class RustRoomListService( override val allRooms: DynamicRoomList = roomListFactory.createRoomList( pageSize = DEFAULT_PAGE_SIZE, + coroutineContext = sessionDispatcher, initialFilter = RoomListFilter.all(RoomListFilter.NonLeft), ) { innerRoomListService.allRooms() @@ -71,6 +75,7 @@ internal class RustRoomListService( override val invites: RoomList = roomListFactory.createRoomList( pageSize = Int.MAX_VALUE, + coroutineContext = sessionDispatcher, ) { innerRoomListService.invites() }