Let SearchBar/SearchField use TextFieldState

This commit is contained in:
ganfra
2026-01-22 16:34:22 +01:00
parent 87619e50e8
commit fa1b32f0ba
48 changed files with 197 additions and 298 deletions

View File

@@ -11,9 +11,7 @@ import io.element.android.libraries.matrix.ui.model.SelectRoomInfo
sealed interface AddRoomToSpaceEvent {
data class ToggleRoom(val room: SelectRoomInfo) : AddRoomToSpaceEvent
data class UpdateSearchQuery(val query: String) : AddRoomToSpaceEvent
data class OnSearchActiveChanged(val active: Boolean) : AddRoomToSpaceEvent
data object Save : AddRoomToSpaceEvent
data object CloseSearch : AddRoomToSpaceEvent
data object ResetSaveAction : AddRoomToSpaceEvent
}

View File

@@ -7,6 +7,8 @@
package io.element.android.features.space.impl.addroom
import androidx.compose.foundation.text.input.clearText
import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
@@ -43,7 +45,7 @@ class AddRoomToSpacePresenter(
@Composable
override fun present(): AddRoomToSpaceState {
var selectedRooms: ImmutableList<SelectRoomInfo> by remember { mutableStateOf(persistentListOf()) }
var searchQuery by remember { mutableStateOf("") }
var searchQuery = rememberTextFieldState()
var isSearchActive by remember { mutableStateOf(false) }
val saveAction = remember { mutableStateOf<AsyncAction<Unit>>(AsyncAction.Uninitialized) }
@@ -51,8 +53,8 @@ class AddRoomToSpacePresenter(
val dataSource = remember { dataSourceFactory.create(coroutineScope) }
// Update search query in data source
LaunchedEffect(searchQuery) {
dataSource.setSearchQuery(searchQuery)
LaunchedEffect(searchQuery.text) {
dataSource.setSearchQuery(searchQuery.text.toString())
}
LaunchedEffect(isSearchActive) {
dataSource.setIsActive(isSearchActive)
@@ -65,7 +67,7 @@ class AddRoomToSpacePresenter(
derivedStateOf {
when {
filteredRooms.isNotEmpty() -> SearchBarResultState.Results(filteredRooms)
isSearchActive && searchQuery.isNotEmpty() -> SearchBarResultState.NoResultsFound()
isSearchActive && searchQuery.text.isNotEmpty() -> SearchBarResultState.NoResultsFound()
else -> SearchBarResultState.Initial()
}
}
@@ -80,19 +82,12 @@ class AddRoomToSpacePresenter(
(selectedRooms + event.room).toImmutableList()
}
}
is AddRoomToSpaceEvent.UpdateSearchQuery -> {
searchQuery = event.query
}
is AddRoomToSpaceEvent.OnSearchActiveChanged -> {
isSearchActive = event.active
if (!event.active) {
searchQuery = ""
searchQuery.clearText()
}
}
AddRoomToSpaceEvent.CloseSearch -> {
isSearchActive = false
searchQuery = ""
}
AddRoomToSpaceEvent.Save -> {
coroutineScope.addRoomsToSpace(
selectedRooms = selectedRooms,

View File

@@ -7,13 +7,14 @@
package io.element.android.features.space.impl.addroom
import androidx.compose.foundation.text.input.TextFieldState
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
import io.element.android.libraries.matrix.ui.model.SelectRoomInfo
import kotlinx.collections.immutable.ImmutableList
data class AddRoomToSpaceState(
val searchQuery: String,
val searchQuery: TextFieldState,
val isSearchActive: Boolean,
val searchResults: SearchBarResultState<ImmutableList<SelectRoomInfo>>,
val selectedRooms: ImmutableList<SelectRoomInfo>,

View File

@@ -7,6 +7,7 @@
package io.element.android.features.space.impl.addroom
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
@@ -69,7 +70,7 @@ internal fun anAddRoomToSpaceState(
eventSink: (AddRoomToSpaceEvent) -> Unit = {},
): AddRoomToSpaceState {
return AddRoomToSpaceState(
searchQuery = searchQuery,
searchQuery = TextFieldState(searchQuery),
searchResults = searchResults,
selectedRooms = selectedRooms,
isSearchActive = isSearchActive,

View File

@@ -64,7 +64,7 @@ fun AddRoomToSpaceView(
fun onBack() {
if (state.isSearchActive) {
state.eventSink(AddRoomToSpaceEvent.CloseSearch)
state.eventSink(AddRoomToSpaceEvent.OnSearchActiveChanged(false))
} else {
onBackClick()
}
@@ -105,12 +105,11 @@ fun AddRoomToSpaceView(
SearchBar(
modifier = Modifier.fillMaxWidth(),
placeHolderTitle = stringResource(CommonStrings.action_search),
query = state.searchQuery,
onQueryChange = { state.eventSink(AddRoomToSpaceEvent.UpdateSearchQuery(it)) },
queryState = state.searchQuery,
active = state.isSearchActive,
onActiveChange = { state.eventSink(AddRoomToSpaceEvent.OnSearchActiveChanged(it)) },
resultState = state.searchResults,
showBackButton = false,
resultState = state.searchResults,
contentPrefix = {
if (state.selectedRooms.isNotEmpty()) {
SelectedRoomsRow(

View File

@@ -9,6 +9,7 @@
package io.element.android.features.space.impl.addroom
import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
@@ -38,7 +39,7 @@ class AddRoomToSpacePresenterTest {
presenter.test {
val state = awaitItem()
assertThat(state.selectedRooms).isEmpty()
assertThat(state.searchQuery).isEmpty()
assertThat(state.searchQuery.text.toString()).isEmpty()
assertThat(state.isSearchActive).isFalse()
assertThat(state.saveAction).isEqualTo(AsyncAction.Uninitialized)
assertThat(state.canSave).isFalse()
@@ -77,17 +78,6 @@ class AddRoomToSpacePresenterTest {
}
}
@Test
fun `present - UpdateSearchQuery updates query`() = runTest {
val presenter = createAddRoomToSpacePresenter()
presenter.test {
val state = awaitItem()
state.eventSink(AddRoomToSpaceEvent.UpdateSearchQuery("test"))
val updatedState = awaitItem()
assertThat(updatedState.searchQuery).isEqualTo("test")
}
}
@Test
fun `present - OnSearchActiveChanged activates search`() = runTest {
val presenter = createAddRoomToSpacePresenter()
@@ -107,33 +97,14 @@ class AddRoomToSpacePresenterTest {
// Activate search and set query
state.eventSink(AddRoomToSpaceEvent.OnSearchActiveChanged(true))
awaitItem()
state.eventSink(AddRoomToSpaceEvent.UpdateSearchQuery("test"))
state.searchQuery.setTextAndPlaceCursorAtEnd("test")
awaitItem()
// Deactivate search
state.eventSink(AddRoomToSpaceEvent.OnSearchActiveChanged(false))
advanceUntilIdle()
val finalState = expectMostRecentItem()
assertThat(finalState.isSearchActive).isFalse()
assertThat(finalState.searchQuery).isEmpty()
}
}
@Test
fun `present - CloseSearch deactivates and clears query`() = runTest {
val presenter = createAddRoomToSpacePresenter()
presenter.test {
val state = awaitItem()
// Activate search and set query
state.eventSink(AddRoomToSpaceEvent.OnSearchActiveChanged(true))
awaitItem()
state.eventSink(AddRoomToSpaceEvent.UpdateSearchQuery("test"))
awaitItem()
// Close search
state.eventSink(AddRoomToSpaceEvent.CloseSearch)
advanceUntilIdle()
val finalState = expectMostRecentItem()
assertThat(finalState.isSearchActive).isFalse()
assertThat(finalState.searchQuery).isEmpty()
assertThat(finalState.searchQuery.text.toString()).isEmpty()
}
}
@@ -168,11 +139,11 @@ class AddRoomToSpacePresenterTest {
val state = awaitItem()
state.eventSink(AddRoomToSpaceEvent.OnSearchActiveChanged(true))
awaitItem()
state.eventSink(AddRoomToSpaceEvent.UpdateSearchQuery("nonexistent"))
state.searchQuery.setTextAndPlaceCursorAtEnd("nonexistent")
advanceUntilIdle()
val finalState = expectMostRecentItem()
assertThat(finalState.isSearchActive).isTrue()
assertThat(finalState.searchQuery).isEqualTo("nonexistent")
assertThat(finalState.searchQuery.text).isEqualTo("nonexistent")
assertThat(finalState.searchResults).isInstanceOf(SearchBarResultState.NoResultsFound::class.java)
}
}

View File

@@ -54,7 +54,7 @@ class AddRoomToSpaceViewTest {
),
)
rule.pressBack()
eventsRecorder.assertSingle(AddRoomToSpaceEvent.CloseSearch)
eventsRecorder.assertSingle(AddRoomToSpaceEvent.OnSearchActiveChanged(false))
}
@Test
@@ -67,12 +67,7 @@ class AddRoomToSpaceViewTest {
),
)
rule.clickOn(CommonStrings.action_save)
eventsRecorder.assertList(
listOf(
AddRoomToSpaceEvent.UpdateSearchQuery(""), // SearchBar initialization
AddRoomToSpaceEvent.Save,
)
)
eventsRecorder.assertSingle(AddRoomToSpaceEvent.Save)
}
@Config(qualifiers = "h1024dp")
@@ -87,12 +82,7 @@ class AddRoomToSpaceViewTest {
),
)
rule.onNodeWithText(suggestions.first().name!!).performClick()
eventsRecorder.assertList(
listOf(
AddRoomToSpaceEvent.UpdateSearchQuery(""), // SearchBar initialization
AddRoomToSpaceEvent.ToggleRoom(suggestions.first()),
)
)
eventsRecorder.assertSingle(AddRoomToSpaceEvent.ToggleRoom(suggestions.first()))
}
@Test