save the lazy list state and make it scroll in the search view model to avoid coming back on another feed position

This commit is contained in:
Vitor Pamplona
2026-03-10 16:56:31 -04:00
parent 26b7efafe8
commit a1e90b8c9f
2 changed files with 12 additions and 27 deletions
@@ -20,6 +20,7 @@
*/ */
package com.vitorpamplona.amethyst.ui.screen.loggedIn.search package com.vitorpamplona.amethyst.ui.screen.loggedIn.search
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@@ -87,8 +88,10 @@ class SearchBarViewModel(
val searchDataSourceState = SearchQueryState(MutableStateFlow(searchValue), account) val searchDataSourceState = SearchQueryState(MutableStateFlow(searchValue), account)
val listState: LazyListState = LazyListState(0, 0)
val directNip05Resolver: Flow<User?> = val directNip05Resolver: Flow<User?> =
searchValueFlow searchTerm
.debounce(400) .debounce(400)
.mapLatest { term -> .mapLatest { term ->
if (term.contains('@')) { if (term.contains('@')) {
@@ -248,11 +251,12 @@ class SearchBarViewModel(
fun clear() = updateSearchValue("") fun clear() = updateSearchValue("")
fun updateDataSource(searchTerm: String) { suspend fun updateDataSource(searchTerm: String) {
if (searchTerm.isBlank()) { if (searchTerm.isBlank()) {
searchDataSourceState.searchQuery.tryEmit("") searchDataSourceState.searchQuery.tryEmit("")
} else { } else {
searchDataSourceState.searchQuery.tryEmit(searchTerm) searchDataSourceState.searchQuery.tryEmit(searchTerm)
listState.scrollToItem(0, 0)
} }
} }
@@ -31,7 +31,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
@@ -58,9 +57,7 @@ import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.nip11RelayInfo.loadRelayInfo import com.vitorpamplona.amethyst.model.nip11RelayInfo.loadRelayInfo
import com.vitorpamplona.amethyst.service.relayClient.searchCommand.TextSearchDataSourceSubscription import com.vitorpamplona.amethyst.service.relayClient.searchCommand.TextSearchDataSourceSubscription
import com.vitorpamplona.amethyst.ui.feeds.ScrollStateKeys
import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel
import com.vitorpamplona.amethyst.ui.feeds.rememberForeverLazyListState
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
import com.vitorpamplona.amethyst.ui.navigation.bottombars.AppBottomBar import com.vitorpamplona.amethyst.ui.navigation.bottombars.AppBottomBar
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
@@ -109,16 +106,16 @@ fun SearchScreen(
) { ) {
WatchLifecycleAndUpdateModel(searchBarViewModel) WatchLifecycleAndUpdateModel(searchBarViewModel)
val listState = rememberForeverLazyListState(ScrollStateKeys.SEARCH_SCREEN)
LaunchedEffect(searchBarViewModel.focusRequester) { LaunchedEffect(searchBarViewModel.focusRequester) {
searchBarViewModel.focusRequester.requestFocus() if (searchBarViewModel.listState.firstVisibleItemIndex == 0) {
searchBarViewModel.focusRequester.requestFocus()
}
} }
DisappearingScaffold( DisappearingScaffold(
isInvertedLayout = false, isInvertedLayout = false,
topBar = { topBar = {
SearchBar(searchBarViewModel, listState, accountViewModel, nav) SearchBar(searchBarViewModel, accountViewModel, nav)
}, },
bottomBar = { bottomBar = {
AppBottomBar(Route.Search, accountViewModel) { route -> AppBottomBar(Route.Search, accountViewModel) { route ->
@@ -131,7 +128,7 @@ fun SearchScreen(
modifier = Modifier.padding(it).consumeWindowInsets(it), modifier = Modifier.padding(it).consumeWindowInsets(it),
) { ) {
ObserveRelayListForSearchAndDisplayIfNotFound(accountViewModel, nav) ObserveRelayListForSearchAndDisplayIfNotFound(accountViewModel, nav)
DisplaySearchResults(searchBarViewModel, listState, nav, accountViewModel) DisplaySearchResults(searchBarViewModel, nav, accountViewModel)
} }
} }
} }
@@ -140,7 +137,6 @@ fun SearchScreen(
@Composable @Composable
private fun SearchBar( private fun SearchBar(
searchBarViewModel: SearchBarViewModel, searchBarViewModel: SearchBarViewModel,
listState: LazyListState,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
@@ -164,23 +160,9 @@ private fun SearchBar(
} }
} }
AnimateOnNewSearch(searchBarViewModel, listState)
SearchTextField(searchBarViewModel, Modifier.statusBarsPadding()) SearchTextField(searchBarViewModel, Modifier.statusBarsPadding())
} }
@Composable
fun AnimateOnNewSearch(
searchBarViewModel: SearchBarViewModel,
listState: LazyListState,
) {
val searchTerm by searchBarViewModel.searchTerm.collectAsStateWithLifecycle()
LaunchedEffect(searchTerm) {
listState.animateScrollToItem(0)
}
}
@Composable @Composable
private fun SearchTextField( private fun SearchTextField(
searchBarViewModel: SearchBarViewModel, searchBarViewModel: SearchBarViewModel,
@@ -237,7 +219,6 @@ private fun SearchTextField(
@Composable @Composable
private fun DisplaySearchResults( private fun DisplaySearchResults(
searchBarViewModel: SearchBarViewModel, searchBarViewModel: SearchBarViewModel,
listState: LazyListState,
nav: INav, nav: INav,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
) { ) {
@@ -256,7 +237,7 @@ private fun DisplaySearchResults(
LazyColumn( LazyColumn(
modifier = Modifier.fillMaxHeight(), modifier = Modifier.fillMaxHeight(),
contentPadding = FeedPadding, contentPadding = FeedPadding,
state = listState, state = searchBarViewModel.listState,
) { ) {
itemsIndexed( itemsIndexed(
hashTags, hashTags,