Rename UserSearchResults as UserSearchResultState

This commit is contained in:
ganfra 2024-01-05 15:28:18 +01:00
parent a790cca1b9
commit dbfa9b0dc6
6 changed files with 49 additions and 27 deletions

View file

@ -24,7 +24,7 @@ import io.element.android.libraries.designsystem.theme.components.SearchBarResul
import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.libraries.matrix.ui.components.aMatrixUser
import io.element.android.libraries.matrix.ui.components.aMatrixUserList import io.element.android.libraries.matrix.ui.components.aMatrixUserList
import io.element.android.libraries.usersearch.api.UserSearchResult import io.element.android.libraries.usersearch.api.UserSearchResult
import io.element.android.libraries.usersearch.api.UserSearchResults import io.element.android.libraries.usersearch.api.UserSearchResultState
import io.element.android.libraries.usersearch.test.FakeUserRepository import io.element.android.libraries.usersearch.test.FakeUserRepository
import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.WarmUpRule
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
@ -137,11 +137,11 @@ class DefaultUserListPresenterTests {
skipItems(2) skipItems(2)
// When the user repository emits a result, it's copied to the state // When the user repository emits a result, it's copied to the state
val result = UserSearchResults( val result = UserSearchResultState(
results = listOf(UserSearchResult(aMatrixUser())), results = listOf(UserSearchResult(aMatrixUser())),
isFetchingSearchResults = false, isFetchingSearchResults = false,
) )
userRepository.emitResult(result) userRepository.emitState(result)
awaitItem().also { state -> awaitItem().also { state ->
assertThat(state.searchResults).isEqualTo( assertThat(state.searchResults).isEqualTo(
SearchBarResultState.Results( SearchBarResultState.Results(
@ -151,11 +151,11 @@ class DefaultUserListPresenterTests {
assertThat(state.isFetchingSearchResults).isFalse() assertThat(state.isFetchingSearchResults).isFalse()
} }
// When the user repository emits another result, it replaces the previous value // When the user repository emits another result, it replaces the previous value
val newResult = UserSearchResults( val newResult = UserSearchResultState(
results = aMatrixUserList().map { UserSearchResult(it) }, results = aMatrixUserList().map { UserSearchResult(it) },
isFetchingSearchResults = false, isFetchingSearchResults = false,
) )
userRepository.emitResult(newResult) userRepository.emitState(newResult)
awaitItem().also { state -> awaitItem().also { state ->
assertThat(state.searchResults).isEqualTo( assertThat(state.searchResults).isEqualTo(
SearchBarResultState.Results( SearchBarResultState.Results(
@ -189,7 +189,7 @@ class DefaultUserListPresenterTests {
skipItems(2) skipItems(2)
// When the results list is empty, the state is set to NoResults // When the results list is empty, the state is set to NoResults
userRepository.emitResult(UserSearchResults(results = emptyList(), isFetchingSearchResults = false)) userRepository.emitState(UserSearchResultState(results = emptyList(), isFetchingSearchResults = false))
assertThat(awaitItem().searchResults).isInstanceOf(SearchBarResultState.NoResultsFound::class.java) assertThat(awaitItem().searchResults).isInstanceOf(SearchBarResultState.NoResultsFound::class.java)
} }
} }

View file

@ -29,13 +29,14 @@ import io.element.android.libraries.designsystem.theme.components.SearchBarResul
import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
import io.element.android.libraries.matrix.api.room.RoomMembershipState import io.element.android.libraries.matrix.api.room.RoomMembershipState
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.A_USER_ID_2 import io.element.android.libraries.matrix.test.A_USER_ID_2
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.libraries.matrix.ui.components.aMatrixUser
import io.element.android.libraries.matrix.ui.components.aMatrixUserList import io.element.android.libraries.matrix.ui.components.aMatrixUserList
import io.element.android.libraries.usersearch.api.UserSearchResult import io.element.android.libraries.usersearch.api.UserSearchResult
import io.element.android.libraries.usersearch.api.UserSearchResults import io.element.android.libraries.usersearch.api.UserSearchResultState
import io.element.android.libraries.usersearch.test.FakeUserRepository import io.element.android.libraries.usersearch.test.FakeUserRepository
import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.consumeItemsUntilPredicate import io.element.android.tests.testutils.consumeItemsUntilPredicate
@ -109,12 +110,12 @@ internal class RoomInviteMembersPresenterTest {
val initialState = awaitItem() val initialState = awaitItem()
initialState.eventSink(RoomInviteMembersEvents.UpdateSearchQuery("some query")) initialState.eventSink(RoomInviteMembersEvents.UpdateSearchQuery("some query"))
assertThat(repository.providedQuery).isEqualTo("some query") assertThat(repository.providedQuery).isEqualTo("some query")
repository.emitResult(UserSearchResults(results = emptyList(), isFetchingSearchResults = true)) repository.emitState(UserSearchResultState(results = emptyList(), isFetchingSearchResults = true))
consumeItemsUntilPredicate { it.isFetchingSearchResults }.last().also { state -> consumeItemsUntilPredicate { it.isFetchingSearchResults }.last().also { state ->
assertThat(state.searchResults).isInstanceOf(SearchBarResultState.Empty::class.java) assertThat(state.searchResults).isInstanceOf(SearchBarResultState.Empty::class.java)
assertThat(state.isFetchingSearchResults).isTrue() assertThat(state.isFetchingSearchResults).isTrue()
} }
repository.emitResult(UserSearchResults(results = emptyList(), isFetchingSearchResults = false)) repository.emitState(results = emptyList(), isFetchingSearchResults = false)
consumeItemsUntilPredicate { !it.isFetchingSearchResults }.last().also { state -> consumeItemsUntilPredicate { !it.isFetchingSearchResults }.last().also { state ->
assertThat(state.searchResults).isInstanceOf(SearchBarResultState.NoResultsFound::class.java) assertThat(state.searchResults).isInstanceOf(SearchBarResultState.NoResultsFound::class.java)
assertThat(state.isFetchingSearchResults).isFalse() assertThat(state.isFetchingSearchResults).isFalse()
@ -140,7 +141,7 @@ internal class RoomInviteMembersPresenterTest {
skipItems(1) skipItems(1)
assertThat(repository.providedQuery).isEqualTo("some query") assertThat(repository.providedQuery).isEqualTo("some query")
repository.emitResult(UserSearchResults(results =aMatrixUserList().map { UserSearchResult(it) })) repository.emitStateWithUsers(users = aMatrixUserList())
skipItems(1) skipItems(1)
val resultState = awaitItem() val resultState = awaitItem()
@ -192,7 +193,7 @@ internal class RoomInviteMembersPresenterTest {
skipItems(1) skipItems(1)
assertThat(repository.providedQuery).isEqualTo("some query") assertThat(repository.providedQuery).isEqualTo("some query")
repository.emitResult(UserSearchResults(results =aMatrixUserList().map { UserSearchResult(it) })) repository.emitStateWithUsers(users = aMatrixUserList())
skipItems(1) skipItems(1)
val resultState = awaitItem() val resultState = awaitItem()
@ -253,7 +254,7 @@ internal class RoomInviteMembersPresenterTest {
assertThat(repository.providedQuery).isEqualTo("some query") assertThat(repository.providedQuery).isEqualTo("some query")
val unresolvedUser = UserSearchResult(aMatrixUser(id = A_USER_ID.value), isUnresolved = true) val unresolvedUser = UserSearchResult(aMatrixUser(id = A_USER_ID.value), isUnresolved = true)
repository.emitResult(UserSearchResults(results = listOf(unresolvedUser) + aMatrixUserList().map { UserSearchResult(it) })) repository.emitState(listOf(unresolvedUser) + aMatrixUserList().map { UserSearchResult(it) })
skipItems(1) skipItems(1)
val resultState = awaitItem() val resultState = awaitItem()
@ -321,7 +322,7 @@ internal class RoomInviteMembersPresenterTest {
skipItems(1) skipItems(1)
assertThat(repository.providedQuery).isEqualTo("some query") assertThat(repository.providedQuery).isEqualTo("some query")
repository.emitResult(UserSearchResults(results = (aMatrixUserList() + selectedUser).map { UserSearchResult(it) })) repository.emitStateWithUsers(users = aMatrixUserList() + selectedUser)
skipItems(2) skipItems(2)
val resultState = awaitItem() val resultState = awaitItem()
@ -361,7 +362,7 @@ internal class RoomInviteMembersPresenterTest {
skipItems(1) skipItems(1)
assertThat(repository.providedQuery).isEqualTo("some query") assertThat(repository.providedQuery).isEqualTo("some query")
repository.emitResult(UserSearchResults(results =(aMatrixUserList() + selectedUser).map { UserSearchResult(it) })) repository.emitStateWithUsers(users = aMatrixUserList() + selectedUser)
skipItems(2) skipItems(2)
// And then a user is toggled // And then a user is toggled
@ -384,6 +385,27 @@ internal class RoomInviteMembersPresenterTest {
} }
} }
private suspend fun FakeUserRepository.emitStateWithUsers(
users: List<MatrixUser>,
isFetchingSearchResults: Boolean = false
) {
emitState(
results = users.map { UserSearchResult(it) },
isFetchingSearchResults = isFetchingSearchResults,
)
}
private suspend fun FakeUserRepository.emitState(
results: List<UserSearchResult>,
isFetchingSearchResults: Boolean = false
) {
val state = UserSearchResultState(
results = results,
isFetchingSearchResults = isFetchingSearchResults
)
emitState(state)
}
private fun TestScope.createDataSource( private fun TestScope.createDataSource(
matrixRoom: MatrixRoom = aMatrixRoom().apply { matrixRoom: MatrixRoom = aMatrixRoom().apply {
givenRoomMembersState(MatrixRoomMembersState.Ready(aRoomMemberList())) givenRoomMembersState(MatrixRoomMembersState.Ready(aRoomMemberList()))

View file

@ -20,5 +20,5 @@ import kotlinx.coroutines.flow.Flow
interface UserRepository { interface UserRepository {
fun search(query: String): Flow<UserSearchResults> fun search(query: String): Flow<UserSearchResultState>
} }

View file

@ -23,7 +23,7 @@ data class UserSearchResult(
val isUnresolved: Boolean = false, val isUnresolved: Boolean = false,
) )
data class UserSearchResults( data class UserSearchResultState(
val results: List<UserSearchResult>, val results: List<UserSearchResult>,
val isFetchingSearchResults: Boolean = false, val isFetchingSearchResults: Boolean,
) )

View file

@ -25,7 +25,7 @@ import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.usersearch.api.UserListDataSource import io.element.android.libraries.usersearch.api.UserListDataSource
import io.element.android.libraries.usersearch.api.UserRepository import io.element.android.libraries.usersearch.api.UserRepository
import io.element.android.libraries.usersearch.api.UserSearchResult import io.element.android.libraries.usersearch.api.UserSearchResult
import io.element.android.libraries.usersearch.api.UserSearchResults import io.element.android.libraries.usersearch.api.UserSearchResultState
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
@ -37,7 +37,7 @@ class MatrixUserRepository @Inject constructor(
private val dataSource: UserListDataSource private val dataSource: UserListDataSource
) : UserRepository { ) : UserRepository {
override fun search(query: String): Flow<UserSearchResults> = flow { override fun search(query: String): Flow<UserSearchResultState> = flow {
val shouldQueryProfile = MatrixPatterns.isUserId(query) && !client.isMe(UserId(query)) val shouldQueryProfile = MatrixPatterns.isUserId(query) && !client.isMe(UserId(query))
val shouldFetchSearchResults = query.length >= MINIMUM_SEARCH_LENGTH val shouldFetchSearchResults = query.length >= MINIMUM_SEARCH_LENGTH
// If the search term is a MXID that's not ours, we'll show a 'fake' result for that user, then update it when we get search results. // If the search term is a MXID that's not ours, we'll show a 'fake' result for that user, then update it when we get search results.
@ -47,7 +47,7 @@ class MatrixUserRepository @Inject constructor(
null null
} }
if (shouldQueryProfile || shouldFetchSearchResults) { if (shouldQueryProfile || shouldFetchSearchResults) {
emit(UserSearchResults(isFetchingSearchResults = shouldFetchSearchResults, results = listOfNotNull(fakeSearchResult))) emit(UserSearchResultState(isFetchingSearchResults = shouldFetchSearchResults, results = listOfNotNull(fakeSearchResult)))
} }
if (shouldFetchSearchResults) { if (shouldFetchSearchResults) {
val results = fetchSearchResults(query, shouldQueryProfile) val results = fetchSearchResults(query, shouldQueryProfile)
@ -55,7 +55,7 @@ class MatrixUserRepository @Inject constructor(
} }
} }
private suspend fun fetchSearchResults(query: String, shouldQueryProfile: Boolean): UserSearchResults { private suspend fun fetchSearchResults(query: String, shouldQueryProfile: Boolean): UserSearchResultState {
// Debounce // Debounce
delay(DEBOUNCE_TIME_MILLIS) delay(DEBOUNCE_TIME_MILLIS)
val results = dataSource val results = dataSource
@ -73,7 +73,7 @@ class MatrixUserRepository @Inject constructor(
?: UserSearchResult(MatrixUser(UserId(query)), isUnresolved = true)) ?: UserSearchResult(MatrixUser(UserId(query)), isUnresolved = true))
} }
return UserSearchResults(results = results, isFetchingSearchResults = false) return UserSearchResultState(results = results, isFetchingSearchResults = false)
} }
companion object { companion object {

View file

@ -17,7 +17,7 @@
package io.element.android.libraries.usersearch.test package io.element.android.libraries.usersearch.test
import io.element.android.libraries.usersearch.api.UserRepository import io.element.android.libraries.usersearch.api.UserRepository
import io.element.android.libraries.usersearch.api.UserSearchResults import io.element.android.libraries.usersearch.api.UserSearchResultState
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
@ -26,15 +26,15 @@ class FakeUserRepository : UserRepository {
var providedQuery: String? = null var providedQuery: String? = null
private set private set
private val flow = MutableSharedFlow<UserSearchResults>() private val flow = MutableSharedFlow<UserSearchResultState>()
override fun search(query: String): Flow<UserSearchResults> { override fun search(query: String): Flow<UserSearchResultState> {
providedQuery = query providedQuery = query
return flow return flow
} }
suspend fun emitResult(result: UserSearchResults) { suspend fun emitState(state: UserSearchResultState) {
flow.emit(result) flow.emit(state)
} }
} }