Scrolling to first item of the selected users list after adding a user

This commit is contained in:
Maxime NATUREL 2023-03-16 15:43:06 +01:00 committed by Florian Renaud
parent 875b0f8f1f
commit 90276195a2
4 changed files with 30 additions and 1 deletions

View file

@ -16,6 +16,7 @@
package io.element.android.features.selectusers.api package io.element.android.features.selectusers.api
import androidx.compose.foundation.lazy.LazyListState
import io.element.android.libraries.matrix.ui.model.MatrixUser import io.element.android.libraries.matrix.ui.model.MatrixUser
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.ImmutableSet
@ -24,6 +25,7 @@ data class SelectUsersState(
val searchQuery: String, val searchQuery: String,
val searchResults: ImmutableList<MatrixUser>, val searchResults: ImmutableList<MatrixUser>,
val selectedUsers: ImmutableSet<MatrixUser>, val selectedUsers: ImmutableSet<MatrixUser>,
val selectedUsersListState: LazyListState,
val isSearchActive: Boolean, val isSearchActive: Boolean,
val isMultiSelectionEnabled: Boolean, val isMultiSelectionEnabled: Boolean,
val eventSink: (SelectUsersEvents) -> Unit, val eventSink: (SelectUsersEvents) -> Unit,

View file

@ -16,6 +16,7 @@
package io.element.android.features.selectusers.api package io.element.android.features.selectusers.api
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.ui.model.MatrixUser import io.element.android.libraries.matrix.ui.model.MatrixUser
@ -55,6 +56,10 @@ fun aSelectUsersState() = SelectUsersState(
searchQuery = "", searchQuery = "",
searchResults = persistentListOf(), searchResults = persistentListOf(),
selectedUsers = persistentSetOf(), selectedUsers = persistentSetOf(),
selectedUsersListState = LazyListState(
firstVisibleItemIndex = 0,
firstVisibleItemScrollOffset = 0,
),
isMultiSelectionEnabled = false, isMultiSelectionEnabled = false,
eventSink = {} eventSink = {}
) )

View file

@ -25,6 +25,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
@ -80,6 +81,7 @@ fun SelectUsersView(
query = state.searchQuery, query = state.searchQuery,
results = state.searchResults, results = state.searchResults,
selectedUsers = state.selectedUsers, selectedUsers = state.selectedUsers,
selectedUsersListState = state.selectedUsersListState,
active = state.isSearchActive, active = state.isSearchActive,
isMultiSelectionEnabled = state.isMultiSelectionEnabled, isMultiSelectionEnabled = state.isMultiSelectionEnabled,
onActiveChanged = { eventSink.invoke(SelectUsersEvents.OnSearchActiveChanged(it)) }, onActiveChanged = { eventSink.invoke(SelectUsersEvents.OnSearchActiveChanged(it)) },
@ -96,6 +98,7 @@ fun SelectUsersView(
if (state.isMultiSelectionEnabled && !state.isSearchActive && state.selectedUsers.isNotEmpty()) { if (state.isMultiSelectionEnabled && !state.isSearchActive && state.selectedUsers.isNotEmpty()) {
SelectedUsersList( SelectedUsersList(
listState = state.selectedUsersListState,
modifier = Modifier.padding(16.dp), modifier = Modifier.padding(16.dp),
selectedUsers = state.selectedUsers, selectedUsers = state.selectedUsers,
onUserRemoved = { onUserRemoved = {
@ -113,6 +116,7 @@ fun SearchUserBar(
query: String, query: String,
results: ImmutableList<MatrixUser>, results: ImmutableList<MatrixUser>,
selectedUsers: ImmutableSet<MatrixUser>, selectedUsers: ImmutableSet<MatrixUser>,
selectedUsersListState: LazyListState,
active: Boolean, active: Boolean,
isMultiSelectionEnabled: Boolean, isMultiSelectionEnabled: Boolean,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@ -171,6 +175,7 @@ fun SearchUserBar(
content = { content = {
if (isMultiSelectionEnabled && active && selectedUsers.isNotEmpty()) { if (isMultiSelectionEnabled && active && selectedUsers.isNotEmpty()) {
SelectedUsersList( SelectedUsersList(
listState = selectedUsersListState,
modifier = Modifier.padding(16.dp), modifier = Modifier.padding(16.dp),
selectedUsers = selectedUsers, selectedUsers = selectedUsers,
onUserRemoved = onUserDeselected, onUserRemoved = onUserDeselected,
@ -239,11 +244,13 @@ fun SearchSingleUserResultItem(
@Composable @Composable
fun SelectedUsersList( fun SelectedUsersList(
listState: LazyListState,
selectedUsers: ImmutableSet<MatrixUser>, selectedUsers: ImmutableSet<MatrixUser>,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onUserRemoved: (MatrixUser) -> Unit = {}, onUserRemoved: (MatrixUser) -> Unit = {},
) { ) {
LazyRow( LazyRow(
state = listState,
modifier = modifier, modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(24.dp), horizontalArrangement = Arrangement.spacedBy(24.dp),
) { ) {

View file

@ -16,12 +16,15 @@
package io.element.android.features.selectusers.impl package io.element.android.features.selectusers.impl
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import io.element.android.features.selectusers.api.SelectUsersEvents import io.element.android.features.selectusers.api.SelectUsersEvents
@ -36,15 +39,19 @@ import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentSetOf import kotlinx.collections.immutable.persistentSetOf
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toImmutableSet import kotlinx.collections.immutable.toImmutableSet
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
class DefaultSelectUsersPresenter(private val isMultiSelectionEnabled: Boolean) : SelectUsersPresenter { class DefaultSelectUsersPresenter(private val isMultiSelectionEnabled: Boolean) : SelectUsersPresenter {
@Composable @Composable
override fun present(): SelectUsersState { override fun present(): SelectUsersState {
val localCoroutineScope = rememberCoroutineScope()
var isSearchActive by rememberSaveable { mutableStateOf(false) } var isSearchActive by rememberSaveable { mutableStateOf(false) }
val selectedUsers: MutableState<ImmutableSet<MatrixUser>> = remember { val selectedUsers: MutableState<ImmutableSet<MatrixUser>> = remember {
mutableStateOf(persistentSetOf()) mutableStateOf(persistentSetOf())
} }
val selectedUsersListState = rememberLazyListState()
var searchQuery by rememberSaveable { mutableStateOf("") } var searchQuery by rememberSaveable { mutableStateOf("") }
val searchResults: MutableState<ImmutableList<MatrixUser>> = remember { val searchResults: MutableState<ImmutableList<MatrixUser>> = remember {
mutableStateOf(persistentListOf()) mutableStateOf(persistentListOf())
@ -54,7 +61,10 @@ class DefaultSelectUsersPresenter(private val isMultiSelectionEnabled: Boolean)
when (event) { when (event) {
is SelectUsersEvents.OnSearchActiveChanged -> isSearchActive = event.active is SelectUsersEvents.OnSearchActiveChanged -> isSearchActive = event.active
is SelectUsersEvents.UpdateSearchQuery -> searchQuery = event.query is SelectUsersEvents.UpdateSearchQuery -> searchQuery = event.query
is SelectUsersEvents.AddToSelection -> selectedUsers.value = selectedUsers.value.plus(event.matrixUser).toImmutableSet() is SelectUsersEvents.AddToSelection -> {
selectedUsers.value = selectedUsers.value.plus(event.matrixUser).toImmutableSet()
localCoroutineScope.scrollToFirstSelectedUser(selectedUsersListState)
}
is SelectUsersEvents.RemoveFromSelection -> selectedUsers.value = selectedUsers.value.minus(event.matrixUser).toImmutableSet() is SelectUsersEvents.RemoveFromSelection -> selectedUsers.value = selectedUsers.value.minus(event.matrixUser).toImmutableSet()
} }
} }
@ -76,6 +86,7 @@ class DefaultSelectUsersPresenter(private val isMultiSelectionEnabled: Boolean)
searchQuery = searchQuery, searchQuery = searchQuery,
searchResults = searchResults.value, searchResults = searchResults.value,
selectedUsers = selectedUsers.value.reversed().toImmutableSet(), selectedUsers = selectedUsers.value.reversed().toImmutableSet(),
selectedUsersListState = selectedUsersListState,
isSearchActive = isSearchActive, isSearchActive = isSearchActive,
isMultiSelectionEnabled = isMultiSelectionEnabled, isMultiSelectionEnabled = isMultiSelectionEnabled,
eventSink = ::handleEvents, eventSink = ::handleEvents,
@ -92,4 +103,8 @@ class DefaultSelectUsersPresenter(private val isMultiSelectionEnabled: Boolean)
} }
return results.toImmutableList() return results.toImmutableList()
} }
private fun CoroutineScope.scrollToFirstSelectedUser(listState: LazyListState) = launch {
listState.scrollToItem(index = 0)
}
} }