diff --git a/changelog.d/95.feature b/changelog.d/95.feature new file mode 100644 index 0000000000..a17f72a80f --- /dev/null +++ b/changelog.d/95.feature @@ -0,0 +1 @@ +[Create and join rooms] Search for users to start a DM diff --git a/docs/_developer_onboarding.md b/docs/_developer_onboarding.md index c6c53dee9a..9198137577 100644 --- a/docs/_developer_onboarding.md +++ b/docs/_developer_onboarding.md @@ -426,6 +426,7 @@ Rageshake can be very useful to get logs from a release version of the applicati - When this is possible, prefer using `sealed interface` instead of `sealed class`; - When writing temporary code, using the string "DO NOT COMMIT" in a comment can help to avoid committing things by mistake. If committed and pushed, the CI will detect this String and will warn the user about it. (TODO Not supported yet!) +- Very occasionally the gradle cache misbehaves and causes problems with Dagger. Try building with `--no-build-cache` if Dagger isn't behaving how you expect. ## Happy coding! diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/AllMatrixUsersDataSource.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/AllMatrixUsersDataSource.kt index 87f465ecbb..4ca985134e 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/AllMatrixUsersDataSource.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/AllMatrixUsersDataSource.kt @@ -17,17 +17,37 @@ package io.element.android.features.createroom.impl import io.element.android.features.userlist.api.UserListDataSource +import io.element.android.libraries.designsystem.components.avatar.AvatarData +import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.usersearch.MatrixUserProfile import io.element.android.libraries.matrix.ui.model.MatrixUser import javax.inject.Inject -// TODO this is empty as we currently don't have an endpoint to perform user search -class AllMatrixUsersDataSource @Inject constructor() : UserListDataSource { +class AllMatrixUsersDataSource @Inject constructor( + private val client: MatrixClient +) : UserListDataSource { override suspend fun search(query: String): List { - return emptyList() + val res = client.searchUsers(query, MAX_SEARCH_RESULTS) + return res.getOrNull()?.results?.map(::toMatrixUser).orEmpty() } override suspend fun getProfile(userId: UserId): MatrixUser? { + // TODO hook up to matrix client return null } + + private fun toMatrixUser(matrixUserProfile: MatrixUserProfile) = MatrixUser( + id = matrixUserProfile.userId, + username = matrixUserProfile.displayName, + avatarData = AvatarData( + id = matrixUserProfile.userId.value, + name = matrixUserProfile.displayName, + url = matrixUserProfile.avatarUrl, + ) + ) + + companion object { + private const val MAX_SEARCH_RESULTS = 5L + } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleUserListStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleUserListStateProvider.kt index 4a4581539b..d005fe8dda 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleUserListStateProvider.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleUserListStateProvider.kt @@ -19,6 +19,7 @@ package io.element.android.features.createroom.impl.addpeople import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.userlist.api.SelectionMode import io.element.android.features.userlist.api.UserListState +import io.element.android.features.userlist.api.UserSearchResultState import io.element.android.features.userlist.api.aListOfSelectedUsers import io.element.android.features.userlist.api.aUserListState import io.element.android.libraries.matrix.ui.components.aMatrixUserList @@ -29,13 +30,13 @@ open class AddPeopleUserListStateProvider : PreviewParameterProvider = persistentListOf(), + searchResults: UserSearchResultState = UserSearchResultState.NotSearching, allUsers: Async> = Async.Uninitialized, ) = RoomMemberListState( diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt index a5798381c0..7eb650c937 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt @@ -26,15 +26,14 @@ import io.element.android.features.userlist.api.UserListDataSource import io.element.android.features.userlist.api.UserListDataStore import io.element.android.features.userlist.api.UserListPresenter import io.element.android.features.userlist.api.UserListPresenterArgs +import io.element.android.features.userlist.api.UserSearchResultState import io.element.android.features.userlist.impl.DefaultUserListPresenter import io.element.android.features.userlist.test.FakeUserListDataSource import io.element.android.libraries.architecture.Async -import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import okhttp3.internal.toImmutableList import org.junit.Test @@ -72,7 +71,7 @@ class RoomMemberListPresenterTests { val initialState = awaitItem() Truth.assertThat(initialState.allUsers).isInstanceOf(Async.Loading::class.java) Truth.assertThat(initialState.userListState.isSearchActive).isFalse() - Truth.assertThat(initialState.userListState.searchResults).isEmpty() + Truth.assertThat(initialState.userListState.searchResults).isEqualTo(UserSearchResultState.NotSearching) Truth.assertThat(initialState.userListState.selectionMode).isEqualTo(SelectionMode.Single) val loadedState = awaitItem() diff --git a/features/userlist/api/build.gradle.kts b/features/userlist/api/build.gradle.kts index af307c018f..fb994538f6 100644 --- a/features/userlist/api/build.gradle.kts +++ b/features/userlist/api/build.gradle.kts @@ -15,6 +15,7 @@ */ plugins { id("io.element.android-compose-library") + alias(libs.plugins.ksp) } android { @@ -27,4 +28,5 @@ dependencies { implementation(projects.libraries.uiStrings) implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrixui) + ksp(libs.showkase.processor) } diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListPresenterArgs.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListPresenterArgs.kt index 9c8a40504b..ffecb71ff0 100644 --- a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListPresenterArgs.kt +++ b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListPresenterArgs.kt @@ -18,6 +18,8 @@ package io.element.android.features.userlist.api data class UserListPresenterArgs( val selectionMode: SelectionMode, + val minimumSearchLength: Int = 1, + val searchDebouncePeriodMillis: Long = 0, ) enum class SelectionMode { diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListState.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListState.kt index dfbfddbcf5..4904a6ceab 100644 --- a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListState.kt +++ b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListState.kt @@ -21,7 +21,7 @@ import kotlinx.collections.immutable.ImmutableList data class UserListState( val searchQuery: String, - val searchResults: ImmutableList, + val searchResults: UserSearchResultState, val selectedUsers: ImmutableList, val isSearchActive: Boolean, val selectionMode: SelectionMode, @@ -29,3 +29,14 @@ data class UserListState( ) { val isMultiSelectionEnabled = selectionMode == SelectionMode.Multiple } + +sealed interface UserSearchResultState { + /** No search results are available yet (e.g. because the user hasn't entered a (long enough) search term). */ + object NotSearching : UserSearchResultState + + /** The search has completed, but no results were found. */ + object NoResults : UserSearchResultState + + /** The search has completed, and some matching users were found. */ + data class Results(val results: ImmutableList) : UserSearchResultState +} diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListStateProvider.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListStateProvider.kt index 7ea0ba0827..d0047e4717 100644 --- a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListStateProvider.kt +++ b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/UserListStateProvider.kt @@ -37,22 +37,27 @@ open class UserListStateProvider : PreviewParameterProvider { isSearchActive = true, searchQuery = "@someone:matrix.org", selectedUsers = aListOfSelectedUsers(), - searchResults = aMatrixUserList().toImmutableList(), + searchResults = UserSearchResultState.Results(aMatrixUserList().toImmutableList()), ), aUserListState().copy( isSearchActive = true, searchQuery = "@someone:matrix.org", selectionMode = SelectionMode.Multiple, selectedUsers = aListOfSelectedUsers(), - searchResults = aMatrixUserList().toImmutableList(), - ) + searchResults = UserSearchResultState.Results(aMatrixUserList().toImmutableList()), + ), + aUserListState().copy( + isSearchActive = true, + searchQuery = "something-with-no-results", + searchResults = UserSearchResultState.NoResults + ), ) } fun aUserListState() = UserListState( isSearchActive = false, searchQuery = "", - searchResults = persistentListOf(), + searchResults = UserSearchResultState.NotSearching, selectedUsers = persistentListOf(), selectionMode = SelectionMode.Single, eventSink = {} diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SearchUserBar.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SearchUserBar.kt index baee8c5b2a..c5f6afd2e5 100644 --- a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SearchUserBar.kt +++ b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/SearchUserBar.kt @@ -17,14 +17,17 @@ package io.element.android.features.userlist.api.components import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Search import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SearchBarDefaults import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -32,7 +35,9 @@ import androidx.compose.ui.draw.alpha import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import io.element.android.features.userlist.api.UserSearchResultState import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton @@ -46,7 +51,7 @@ import kotlinx.collections.immutable.ImmutableList @Composable fun SearchUserBar( query: String, - results: ImmutableList, + state: UserSearchResultState, selectedUsers: ImmutableList, active: Boolean, isMultiSelectionEnabled: Boolean, @@ -91,6 +96,7 @@ fun SearchUserBar( } } } + !active -> { { Icon( @@ -100,6 +106,7 @@ fun SearchUserBar( ) } } + else -> null }, colors = if (!active) SearchBarDefaults.colors() else SearchBarDefaults.colors(containerColor = Color.Transparent), @@ -113,31 +120,43 @@ fun SearchUserBar( ) } - LazyColumn { - if (isMultiSelectionEnabled) { - items(results) { matrixUser -> - SearchMultipleUsersResultItem( - modifier = Modifier.fillMaxWidth(), - matrixUser = matrixUser, - isUserSelected = selectedUsers.find { it.id == matrixUser.id } != null, - onCheckedChange = { checked -> - if (checked) { - onUserSelected(matrixUser) - } else { - onUserDeselected(matrixUser) + + if (state is UserSearchResultState.Results) { + LazyColumn { + if (isMultiSelectionEnabled) { + items(state.results) { matrixUser -> + SearchMultipleUsersResultItem( + modifier = Modifier.fillMaxWidth(), + matrixUser = matrixUser, + isUserSelected = selectedUsers.find { it.id == matrixUser.id } != null, + onCheckedChange = { checked -> + if (checked) { + onUserSelected(matrixUser) + } else { + onUserDeselected(matrixUser) + } } - } - ) - } - } else { - items(results) { matrixUser -> - SearchSingleUserResultItem( - modifier = Modifier.fillMaxWidth(), - matrixUser = matrixUser, - onClick = { onUserSelected(matrixUser) } - ) + ) + } + } else { + items(state.results) { matrixUser -> + SearchSingleUserResultItem( + modifier = Modifier.fillMaxWidth(), + matrixUser = matrixUser, + onClick = { onUserSelected(matrixUser) } + ) + } } } + } else if (state is UserSearchResultState.NoResults) { + Spacer(Modifier.size(80.dp)) + + Text( + text = stringResource(R.string.common_no_results), + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.tertiary, + modifier = Modifier.fillMaxWidth() + ) } }, ) diff --git a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/UserListView.kt b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/UserListView.kt index 6532dea2b6..7bfca1c216 100644 --- a/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/UserListView.kt +++ b/features/userlist/api/src/main/kotlin/io/element/android/features/userlist/api/components/UserListView.kt @@ -44,7 +44,7 @@ fun UserListView( SearchUserBar( modifier = Modifier.fillMaxWidth(), query = state.searchQuery, - results = state.searchResults, + state = state.searchResults, selectedUsers = state.selectedUsers, active = state.isSearchActive, isMultiSelectionEnabled = state.isMultiSelectionEnabled, diff --git a/features/userlist/impl/src/main/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenter.kt b/features/userlist/impl/src/main/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenter.kt index 06f4caed86..8b1f08b763 100644 --- a/features/userlist/impl/src/main/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenter.kt +++ b/features/userlist/impl/src/main/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenter.kt @@ -18,7 +18,6 @@ package io.element.android.features.userlist.impl import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.MutableState import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -35,13 +34,14 @@ import io.element.android.features.userlist.api.UserListEvents import io.element.android.features.userlist.api.UserListPresenter import io.element.android.features.userlist.api.UserListPresenterArgs import io.element.android.features.userlist.api.UserListState +import io.element.android.features.userlist.api.UserSearchResultState import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.MatrixPatterns import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.ui.model.MatrixUser -import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList +import kotlinx.coroutines.delay class DefaultUserListPresenter @AssistedInject constructor( @Assisted val args: UserListPresenterArgs, @@ -62,45 +62,47 @@ class DefaultUserListPresenter @AssistedInject constructor( @Composable override fun present(): UserListState { var isSearchActive by rememberSaveable { mutableStateOf(false) } - val selectedUsers = userListDataStore.selectedUsers().collectAsState(emptyList()) + val selectedUsers by userListDataStore.selectedUsers().collectAsState(emptyList()) var searchQuery by rememberSaveable { mutableStateOf("") } - val searchResults: MutableState> = remember { - mutableStateOf(persistentListOf()) - } - - fun handleEvents(event: UserListEvents) { - when (event) { - is UserListEvents.OnSearchActiveChanged -> isSearchActive = event.active - is UserListEvents.UpdateSearchQuery -> searchQuery = event.query - is UserListEvents.AddToSelection -> userListDataStore.selectUser(event.matrixUser) - is UserListEvents.RemoveFromSelection -> userListDataStore.removeUserFromSelection(event.matrixUser) - } + var searchResults: UserSearchResultState by remember { + mutableStateOf(UserSearchResultState.NotSearching) } LaunchedEffect(searchQuery) { // Clear the search results before performing the search, manually add a fake result with the matrixId, if any - searchResults.value = if (MatrixPatterns.isUserId(searchQuery)) { - persistentListOf(MatrixUser(UserId(searchQuery))) + searchResults = if (MatrixPatterns.isUserId(searchQuery)) { + UserSearchResultState.Results(persistentListOf(MatrixUser(UserId(searchQuery)))) } else { - persistentListOf() + UserSearchResultState.NotSearching } + + // Debounce + delay(args.searchDebouncePeriodMillis) + // Perform the search asynchronously - if (searchQuery.isNotEmpty()) { - searchResults.value = performSearch(searchQuery) + if (searchQuery.length >= args.minimumSearchLength) { + searchResults = performSearch(searchQuery) } } return UserListState( searchQuery = searchQuery, - searchResults = searchResults.value, - selectedUsers = selectedUsers.value.toImmutableList(), + searchResults = searchResults, + selectedUsers = selectedUsers.toImmutableList(), isSearchActive = isSearchActive, selectionMode = args.selectionMode, - eventSink = ::handleEvents, + eventSink = { event -> + when (event) { + is UserListEvents.OnSearchActiveChanged -> isSearchActive = event.active + is UserListEvents.UpdateSearchQuery -> searchQuery = event.query + is UserListEvents.AddToSelection -> userListDataStore.selectUser(event.matrixUser) + is UserListEvents.RemoveFromSelection -> userListDataStore.removeUserFromSelection(event.matrixUser) + } + }, ) } - private suspend fun performSearch(query: String): ImmutableList { + private suspend fun performSearch(query: String): UserSearchResultState { val isMatrixId = MatrixPatterns.isUserId(query) val results = userListDataSource.search(query).toMutableList() if (isMatrixId && results.none { it.id.value == query }) { @@ -108,6 +110,6 @@ class DefaultUserListPresenter @AssistedInject constructor( val profile = getProfileResult ?: MatrixUser(UserId(query)) results.add(0, profile) } - return results.toImmutableList() + return if (results.isEmpty()) UserSearchResultState.NoResults else UserSearchResultState.Results(results.toImmutableList()) } } diff --git a/features/userlist/impl/src/test/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenterTests.kt b/features/userlist/impl/src/test/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenterTests.kt index c5dbc933f9..39ceeba35c 100644 --- a/features/userlist/impl/src/test/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenterTests.kt +++ b/features/userlist/impl/src/test/kotlin/io/element/android/features/userlist/impl/DefaultUserListPresenterTests.kt @@ -25,12 +25,14 @@ import io.element.android.features.userlist.api.SelectionMode import io.element.android.features.userlist.api.UserListDataStore import io.element.android.features.userlist.api.UserListEvents import io.element.android.features.userlist.api.UserListPresenterArgs +import io.element.android.features.userlist.api.UserSearchResultState import io.element.android.features.userlist.test.FakeUserListDataSource import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.libraries.matrix.ui.model.MatrixUser import io.mockk.coJustRun import io.mockk.mockkConstructor +import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test @@ -55,7 +57,7 @@ class DefaultUserListPresenterTests { assertThat(initialState.isMultiSelectionEnabled).isFalse() assertThat(initialState.isSearchActive).isFalse() assertThat(initialState.selectedUsers).isEmpty() - assertThat(initialState.searchResults).isEmpty() + assertThat(initialState.searchResults).isEqualTo(UserSearchResultState.NotSearching) } } @@ -74,7 +76,7 @@ class DefaultUserListPresenterTests { assertThat(initialState.isMultiSelectionEnabled).isTrue() assertThat(initialState.isSearchActive).isFalse() assertThat(initialState.selectedUsers).isEmpty() - assertThat(initialState.searchResults).isEmpty() + assertThat(initialState.searchResults).isEqualTo(UserSearchResultState.NotSearching) } } @@ -96,18 +98,42 @@ class DefaultUserListPresenterTests { val matrixIdQuery = "@name:matrix.org" initialState.eventSink(UserListEvents.UpdateSearchQuery(matrixIdQuery)) assertThat(awaitItem().searchQuery).isEqualTo(matrixIdQuery) - assertThat(awaitItem().searchResults).containsExactly(MatrixUser(UserId(matrixIdQuery))) + assertThat(awaitItem().searchResults).isEqualTo(UserSearchResultState.Results(persistentListOf(MatrixUser(UserId(matrixIdQuery))))) val notMatrixIdQuery = "name" initialState.eventSink(UserListEvents.UpdateSearchQuery(notMatrixIdQuery)) assertThat(awaitItem().searchQuery).isEqualTo(notMatrixIdQuery) - assertThat(awaitItem().searchResults).isEmpty() + assertThat(awaitItem().searchResults).isEqualTo(UserSearchResultState.NoResults) initialState.eventSink(UserListEvents.OnSearchActiveChanged(false)) assertThat(awaitItem().isSearchActive).isFalse() } } + @Test + fun `present - searches when minimum length exceeded`() = runTest { + val presenter = DefaultUserListPresenter( + UserListPresenterArgs(selectionMode = SelectionMode.Single, minimumSearchLength = 3), + userListDataSource, + UserListDataStore(), + ) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + + // When the search term is too short, nothing happens + initialState.eventSink(UserListEvents.UpdateSearchQuery("al")) + assertThat(awaitItem().searchResults).isEqualTo(UserSearchResultState.NotSearching) + + // When it reaches the minimum length, a search is performed asynchronously + userListDataSource.givenSearchResult(listOf(aMatrixUser())) + initialState.eventSink(UserListEvents.UpdateSearchQuery("alice")) + assertThat(awaitItem().searchResults).isEqualTo(UserSearchResultState.NotSearching) + assertThat(awaitItem().searchResults).isEqualTo(UserSearchResultState.Results(persistentListOf(aMatrixUser()))) + } + } + @Test fun `present - select a user`() = runTest { mockkConstructor(LazyListState::class) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index 7bc9ba0797..c9adb6e50e 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -26,6 +26,7 @@ import io.element.android.libraries.matrix.api.pusher.PushersService import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource +import io.element.android.libraries.matrix.api.usersearch.MatrixSearchUserResults import io.element.android.libraries.matrix.api.verification.SessionVerificationService import java.io.Closeable @@ -58,4 +59,6 @@ interface MatrixClient : Closeable { fun onSlidingSyncUpdate() fun roomMembershipObserver(): RoomMembershipObserver + + suspend fun searchUsers(searchTerm: String, limit: Long): Result } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/usersearch/MatrixSearchUserResults.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/usersearch/MatrixSearchUserResults.kt new file mode 100644 index 0000000000..a37e28c197 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/usersearch/MatrixSearchUserResults.kt @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.matrix.api.usersearch + +data class MatrixSearchUserResults( + val results: List, + val limited: Boolean, +) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/usersearch/MatrixUserProfile.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/usersearch/MatrixUserProfile.kt new file mode 100644 index 0000000000..e9c7330f9b --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/usersearch/MatrixUserProfile.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.matrix.api.usersearch + +import io.element.android.libraries.matrix.api.core.UserId + +data class MatrixUserProfile( + val userId: UserId, + val displayName: String?, + val avatarUrl: String? +) 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 b710a5cf6d..6e5b46d447 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 @@ -29,6 +29,7 @@ import io.element.android.libraries.matrix.api.pusher.PushersService import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource +import io.element.android.libraries.matrix.api.usersearch.MatrixSearchUserResults import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.libraries.matrix.impl.media.RustMediaResolver import io.element.android.libraries.matrix.impl.notification.RustNotificationService @@ -36,6 +37,7 @@ import io.element.android.libraries.matrix.impl.pushers.RustPushersService import io.element.android.libraries.matrix.impl.room.RustMatrixRoom import io.element.android.libraries.matrix.impl.room.RustRoomSummaryDataSource import io.element.android.libraries.matrix.impl.sync.SlidingSyncObserverProxy +import io.element.android.libraries.matrix.impl.usersearch.UserSearchResultMapper import io.element.android.libraries.matrix.impl.verification.RustSessionVerificationService import io.element.android.libraries.sessionstorage.api.SessionStore import kotlinx.coroutines.CoroutineScope @@ -362,6 +364,13 @@ class RustMatrixClient constructor( override fun roomMembershipObserver(): RoomMembershipObserver = roomMembershipObserver + override suspend fun searchUsers(searchTerm: String, limit: Long): Result = + withContext(dispatchers.io) { + runCatching { + UserSearchResultMapper.map(client.searchUsers(searchTerm, limit.toULong())) + } + } + private fun File.deleteSessionDirectory(userID: String): Boolean { // Rust sanitises the user ID replacing invalid characters with an _ val sanitisedUserID = userID.replace(":", "_") diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/usersearch/UserSearchResultMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/usersearch/UserSearchResultMapper.kt new file mode 100644 index 0000000000..598e4cb889 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/usersearch/UserSearchResultMapper.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.matrix.impl.usersearch + +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.usersearch.MatrixSearchUserResults +import io.element.android.libraries.matrix.api.usersearch.MatrixUserProfile +import org.matrix.rustcomponents.sdk.SearchUsersResults +import org.matrix.rustcomponents.sdk.UserProfile + +object UserSearchResultMapper { + + fun map(result: SearchUsersResults): MatrixSearchUserResults { + return MatrixSearchUserResults( + results = result.results.map(::mapUserProfile), + limited = result.limited, + ) + } + + private fun mapUserProfile(userProfile: UserProfile): MatrixUserProfile { + return MatrixUserProfile( + userId = UserId(userProfile.userId), + displayName = userProfile.displayName, + avatarUrl = userProfile.avatarUrl, + ) + } +} diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt index c547ac8237..ca41b9d828 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt @@ -27,6 +27,7 @@ import io.element.android.libraries.matrix.api.pusher.PushersService import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource +import io.element.android.libraries.matrix.api.usersearch.MatrixSearchUserResults import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.libraries.matrix.test.media.FakeMediaResolver import io.element.android.libraries.matrix.test.notification.FakeNotificationService @@ -55,6 +56,7 @@ class FakeMatrixClient( private var findDmResult: MatrixRoom? = FakeMatrixRoom() private var logoutFailure: Throwable? = null private val getRoomResults = mutableMapOf() + private val searchUserResults = mutableMapOf>() override fun getRoom(roomId: RoomId): MatrixRoom? { return getRoomResults[roomId] @@ -126,6 +128,10 @@ class FakeMatrixClient( return RoomMembershipObserver() } + override suspend fun searchUsers(searchTerm: String, limit: Long): Result { + return searchUserResults[searchTerm] ?: Result.failure(IllegalStateException("No response defined for $searchTerm")) + } + // Mocks fun givenLogoutError(failure: Throwable?) { @@ -159,4 +165,8 @@ class FakeMatrixClient( fun givenGetRoomResult(roomId: RoomId, result: MatrixRoom) { getRoomResults[roomId] = result } + + fun givenSearchUsersResult(searchTerm: String, result: Result) { + searchUserResults[searchTerm] = result + } } diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_SearchMultipleUsersResultItemDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_SearchMultipleUsersResultItemDarkPreview_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a076ab948b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_SearchMultipleUsersResultItemDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4bd925ebe0257b400ed0b1cb956848622d1b42d8afcc71f915522c4878aaf94b +size 23447 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_SearchMultipleUsersResultItemLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_SearchMultipleUsersResultItemLightPreview_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..050457dd05 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_SearchMultipleUsersResultItemLightPreview_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:12b569ad80bb4a4df89d70d9ae4a92da0d9e96deb477e1e437efbef5ec4a1fdd +size 22302 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_SearchSingleUserResultItemDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_SearchSingleUserResultItemDarkPreview_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7833fbac38 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_SearchSingleUserResultItemDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:91de9d54216aac2bf2c4721e5e8ff5e74be43105846bdac16230cb7bea7427af +size 13777 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_SearchSingleUserResultItemLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_SearchSingleUserResultItemLightPreview_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..6b4f02c4d6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_SearchSingleUserResultItemLightPreview_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:38fbef57e6fb2860d05ab2b864bab16a30ebc98199eb18b19e422f946c52e163 +size 13096 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_SelectedUserDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_SelectedUserDarkPreview_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c95b573d31 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_SelectedUserDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3b357bc650466f045ff7a3d601b0a716f81af9fc55cd4f082a231747905e6860 +size 13146 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_SelectedUserLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_SelectedUserLightPreview_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..5915593f78 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_SelectedUserLightPreview_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:de04bcf3f3d053024f7f5e9e11da95b7d609cf3c1982db70d1ef734b0f21d729 +size 12577 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_SelectedUsersListDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_SelectedUsersListDarkPreview_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..86f069c5c0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_SelectedUsersListDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ffcc7edc0898f3afccf77679a477e76f9f9aef5c4f0309518b3025ed6ce17f96 +size 32696 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_SelectedUsersListLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_SelectedUsersListLightPreview_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..5bedeb65db --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_SelectedUsersListLightPreview_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cc5d0c10593401fbb674c2a27333e546db9b0c11aca9ae280a721aa0e2d16765 +size 31793 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..9eb19efb96 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:02d8edfee13ad4ca97dde238939b6166cccc28550d77c7b924c6aebd6e8d6a07 +size 9916 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..485dd62554 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6a212fb83b307da9ebd0681492286d7f19520c8140629f0a21bad0c43e3954d6 +size 36910 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f13a65f8e2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:89ea65099fb4981bbeb24e49afb7400b0e8da79e3b783e198519ba1e970404a8 +size 8317 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..74a7f599cd --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9a8ba207cf61c56b64c6855d04c8a30def0b3daf325adf57edd45b40981ed745 +size 7733 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..74a7f599cd --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9a8ba207cf61c56b64c6855d04c8a30def0b3daf325adf57edd45b40981ed745 +size 7733 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..58ece12677 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a57daa1da9d07e6c7d497c63eb0c9fda36f48ea28f4592eef62774034b86b5e3 +size 87856 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..0f474941cb --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0071d13454a9e8d75fc693d2f13a65c4d61910425217757804d92c086d56792f +size 102885 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..0f9a5a79cc --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d5b879b74654fdad0638f432a71adfc9186fbe00dafd5d963a3affb33ec8c5c8 +size 12841 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..bb88e7b49a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2fbafefd9aa66ac411836715ef57f39e05e4eeeb0f4601b8a1706d9a3a487527 +size 9692 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..05ec75c6c5 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18d25ee10a5deb2e7a81aadd65dad542ed1a40889d77e375412c66c30a834492 +size 34260 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..eb23dcec96 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b8de2f28cf9918a7eddcc1311c34ba0376242a4708724b57689df000b7480524 +size 8197 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..e2e4a33e67 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:687133e4729ea42382ac0b76af6ceaf8b20cbc65923412fb97b077d2475c70f7 +size 7514 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewLightPreview_0_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..e2e4a33e67 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:687133e4729ea42382ac0b76af6ceaf8b20cbc65923412fb97b077d2475c70f7 +size 7514 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewLightPreview_0_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2b1327fb96 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a708d195ffce2d542c494c6949456a1d341dff5c51f742e04212b72863c91988 +size 84136 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewLightPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewLightPreview_0_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b55aeefe8b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewLightPreview_0_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ff83f67a9fbcc5066547224c2f26d94c33bd0b60563acc062fde399ef9d681e1 +size 97631 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewLightPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewLightPreview_0_null_7,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..787c71d87e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.userlist.api.components_null_DefaultGroup_UserListViewLightPreview_0_null_7,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:534cba0c13aa52b3557ab8df854c521dd13b82e5a1550d73abcef12925e389da +size 11878