diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt index 6d1b1e5757..86437d2872 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersState.kt @@ -16,6 +16,7 @@ package io.element.android.features.selectusers.api +import androidx.compose.foundation.lazy.LazyListState import io.element.android.libraries.matrix.ui.model.MatrixUser import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableSet @@ -24,6 +25,7 @@ data class SelectUsersState( val searchQuery: String, val searchResults: ImmutableList, val selectedUsers: ImmutableSet, + val selectedUsersListState: LazyListState, val isSearchActive: Boolean, val isMultiSelectionEnabled: Boolean, val eventSink: (SelectUsersEvents) -> Unit, diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt index 59405004fe..e85b68e986 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersStateProvider.kt @@ -16,6 +16,7 @@ package io.element.android.features.selectusers.api +import androidx.compose.foundation.lazy.LazyListState import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.ui.model.MatrixUser @@ -55,6 +56,10 @@ fun aSelectUsersState() = SelectUsersState( searchQuery = "", searchResults = persistentListOf(), selectedUsers = persistentSetOf(), + selectedUsersListState = LazyListState( + firstVisibleItemIndex = 0, + firstVisibleItemScrollOffset = 0, + ), isMultiSelectionEnabled = false, eventSink = {} ) diff --git a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt index 8d2c3ae71d..4e07ebd0ca 100644 --- a/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt +++ b/features/selectusers/api/src/main/kotlin/io/element/android/features/selectusers/api/SelectUsersView.kt @@ -25,6 +25,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape @@ -80,6 +81,7 @@ fun SelectUsersView( query = state.searchQuery, results = state.searchResults, selectedUsers = state.selectedUsers, + selectedUsersListState = state.selectedUsersListState, active = state.isSearchActive, isMultiSelectionEnabled = state.isMultiSelectionEnabled, onActiveChanged = { eventSink.invoke(SelectUsersEvents.OnSearchActiveChanged(it)) }, @@ -96,6 +98,7 @@ fun SelectUsersView( if (state.isMultiSelectionEnabled && !state.isSearchActive && state.selectedUsers.isNotEmpty()) { SelectedUsersList( + listState = state.selectedUsersListState, modifier = Modifier.padding(16.dp), selectedUsers = state.selectedUsers, onUserRemoved = { @@ -113,6 +116,7 @@ fun SearchUserBar( query: String, results: ImmutableList, selectedUsers: ImmutableSet, + selectedUsersListState: LazyListState, active: Boolean, isMultiSelectionEnabled: Boolean, modifier: Modifier = Modifier, @@ -171,6 +175,7 @@ fun SearchUserBar( content = { if (isMultiSelectionEnabled && active && selectedUsers.isNotEmpty()) { SelectedUsersList( + listState = selectedUsersListState, modifier = Modifier.padding(16.dp), selectedUsers = selectedUsers, onUserRemoved = onUserDeselected, @@ -239,11 +244,13 @@ fun SearchSingleUserResultItem( @Composable fun SelectedUsersList( + listState: LazyListState, selectedUsers: ImmutableSet, modifier: Modifier = Modifier, onUserRemoved: (MatrixUser) -> Unit = {}, ) { LazyRow( + state = listState, modifier = modifier, horizontalArrangement = Arrangement.spacedBy(24.dp), ) { diff --git a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt index f1c8978402..5e194adcc8 100644 --- a/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt +++ b/features/selectusers/impl/src/main/kotlin/io/element/android/features/selectusers/impl/DefaultSelectUsersPresenter.kt @@ -16,12 +16,15 @@ 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.LaunchedEffect import androidx.compose.runtime.MutableState 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.selectusers.api.SelectUsersEvents @@ -36,15 +39,19 @@ import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentSetOf import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableSet +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch class DefaultSelectUsersPresenter(private val isMultiSelectionEnabled: Boolean) : SelectUsersPresenter { @Composable override fun present(): SelectUsersState { + val localCoroutineScope = rememberCoroutineScope() var isSearchActive by rememberSaveable { mutableStateOf(false) } val selectedUsers: MutableState> = remember { mutableStateOf(persistentSetOf()) } + val selectedUsersListState = rememberLazyListState() var searchQuery by rememberSaveable { mutableStateOf("") } val searchResults: MutableState> = remember { mutableStateOf(persistentListOf()) @@ -54,7 +61,10 @@ class DefaultSelectUsersPresenter(private val isMultiSelectionEnabled: Boolean) when (event) { is SelectUsersEvents.OnSearchActiveChanged -> isSearchActive = event.active 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() } } @@ -76,6 +86,7 @@ class DefaultSelectUsersPresenter(private val isMultiSelectionEnabled: Boolean) searchQuery = searchQuery, searchResults = searchResults.value, selectedUsers = selectedUsers.value.reversed().toImmutableSet(), + selectedUsersListState = selectedUsersListState, isSearchActive = isSearchActive, isMultiSelectionEnabled = isMultiSelectionEnabled, eventSink = ::handleEvents, @@ -92,4 +103,8 @@ class DefaultSelectUsersPresenter(private val isMultiSelectionEnabled: Boolean) } return results.toImmutableList() } + + private fun CoroutineScope.scrollToFirstSelectedUser(listState: LazyListState) = launch { + listState.scrollToItem(index = 0) + } }