diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/assistant/AssistantScaffold.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/assistant/AssistantScaffold.kt index cd961a1e..c7cfd97a 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/assistant/AssistantScaffold.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/assistant/AssistantScaffold.kt @@ -147,7 +147,7 @@ fun AssistantScaffold( val density = LocalDensity.current val maxSearchBarOffset = with(density) { 128.dp.toPx() } var searchBarOffset by remember { - mutableStateOf(0f) + mutableFloatStateOf(0f) } val nestedScrollConnection = remember { @@ -159,7 +159,7 @@ fun AssistantScaffold( } } } - val actions by searchVM.searchActionResults + val actions = searchVM.searchActionResults val webSearchPadding by animateDpAsState( if (actions.isEmpty()) 0.dp else 48.dp ) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt index 831a93a8..99fa329e 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt @@ -51,7 +51,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier @@ -124,7 +124,7 @@ fun PagerScaffold( val isSearchOpen by viewModel.isSearchOpen val isWidgetEditMode by viewModel.isWidgetEditMode - val actions by searchVM.searchActionResults + val actions = searchVM.searchActionResults val widgetsScrollState = rememberScrollState() val searchState = rememberLazyListState() @@ -272,7 +272,7 @@ fun PagerScaffold( } } - val searchBarOffset = remember { mutableStateOf(0f) } + val searchBarOffset = remember { mutableFloatStateOf(0f) } val scope = rememberCoroutineScope() @@ -341,8 +341,8 @@ fun PagerScaffold( } val deltaSearchBarOffset = consumed.y * if (isSearchOpen && reverseSearchResults) 1 else -1 - searchBarOffset.value = - (searchBarOffset.value + deltaSearchBarOffset).coerceIn(0f, maxSearchBarOffset) + searchBarOffset.floatValue = + (searchBarOffset.floatValue + deltaSearchBarOffset).coerceIn(0f, maxSearchBarOffset) return super.onPostScroll(consumed, available, source) } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PullDownScaffold.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PullDownScaffold.kt index 9f043e4b..7e765b1b 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PullDownScaffold.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PullDownScaffold.kt @@ -47,6 +47,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope @@ -106,7 +107,7 @@ fun PullDownScaffold( val density = LocalDensity.current val context = LocalContext.current - val actions by searchVM.searchActionResults + val actions = searchVM.searchActionResults val isSearchOpen by viewModel.isSearchOpen val isWidgetEditMode by viewModel.isWidgetEditMode @@ -116,7 +117,7 @@ fun PullDownScaffold( val pagerState = rememberPagerState { 2 } - val offsetY = remember { mutableStateOf(0f) } + val offsetY = remember { mutableFloatStateOf(0f) } val maxOffset = with(density) { 64.dp.toPx() } val toggleSearchThreshold = with(density) { 48.dp.toPx() } @@ -153,13 +154,13 @@ fun PullDownScaffold( val isOverThreshold by remember { derivedStateOf { - offsetY.value.absoluteValue > toggleSearchThreshold + offsetY.floatValue.absoluteValue > toggleSearchThreshold } } val dragProgress by remember { derivedStateOf { - (offsetY.value.absoluteValue / toggleSearchThreshold).coerceAtMost(1f) + (offsetY.floatValue.absoluteValue / toggleSearchThreshold).coerceAtMost(1f) } } @@ -236,7 +237,7 @@ fun PullDownScaffold( } } - val searchBarOffset = remember { mutableStateOf(0f) } + val searchBarOffset = remember { mutableFloatStateOf(0f) } val maxSearchBarOffset = with(density) { 128.dp.toPx() } @@ -281,7 +282,7 @@ fun PullDownScaffold( } LaunchedEffect(isWidgetEditMode) { - if (!isWidgetEditMode) searchBarOffset.value = 0f + if (!isWidgetEditMode) searchBarOffset.floatValue = 0f } val handleBackOrHomeEvent = { @@ -321,7 +322,7 @@ fun PullDownScaffold( LaunchedEffect(isOverThreshold) { if (isOverThreshold) { view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY) - } else if (offsetY.value != 0f) { + } else if (offsetY.floatValue != 0f) { view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE) } } @@ -342,15 +343,15 @@ fun PullDownScaffold( val canPullUp = isSearchOpen && isSearchAtBottom val consumed = when { - canPullUp && available.y < 0 || offsetY.value < 0 -> { + canPullUp && available.y < 0 || offsetY.floatValue < 0 -> { val consumed = available.y - offsetY.value = (offsetY.value + (consumed * 0.5f)).coerceIn(-maxOffset, 0f) + offsetY.floatValue = (offsetY.floatValue + (consumed * 0.5f)).coerceIn(-maxOffset, 0f) consumed } - canPullDown && available.y > 0 || offsetY.value > 0 -> { + canPullDown && available.y > 0 || offsetY.floatValue > 0 -> { val consumed = available.y - offsetY.value = (offsetY.value + (consumed * 0.5f)).coerceIn(0f, maxOffset) + offsetY.floatValue = (offsetY.floatValue + (consumed * 0.5f)).coerceIn(0f, maxOffset) consumed } @@ -367,17 +368,17 @@ fun PullDownScaffold( ): Offset { val deltaSearchBarOffset = consumed.y * if (isSearchOpen && reverseSearchResults) 1 else -1 - searchBarOffset.value = - (searchBarOffset.value + deltaSearchBarOffset).coerceIn(0f, maxSearchBarOffset) + searchBarOffset.floatValue = + (searchBarOffset.floatValue + deltaSearchBarOffset).coerceIn(0f, maxSearchBarOffset) return super.onPostScroll(consumed, available, source) } override suspend fun onPreFling(available: Velocity): Velocity { - if (offsetY.value > toggleSearchThreshold || offsetY.value < -toggleSearchThreshold) { + if (offsetY.floatValue > toggleSearchThreshold || offsetY.floatValue < -toggleSearchThreshold) { viewModel.toggleSearch() } if (!isWidgetEditMode) gestureManager.dispatchDragEnd() - if (offsetY.value != 0f) { + if (offsetY.floatValue != 0f) { offsetY.animateTo(0f) return available } @@ -406,7 +407,7 @@ fun PullDownScaffold( } ) } - .offset { IntOffset(0, offsetY.value.toInt()) }, + .offset { IntOffset(0, offsetY.floatValue.toInt()) }, contentAlignment = Alignment.TopCenter ) { BoxWithConstraints( @@ -576,7 +577,7 @@ fun PullDownScaffold( val searchBarLevel by remember { derivedStateOf { when { - offsetY.value != 0f -> SearchBarLevel.Raised + offsetY.floatValue != 0f -> SearchBarLevel.Raised !isSearchOpen && isWidgetsAtStart && (fillClockHeight || !bottomSearchBar) -> SearchBarLevel.Resting isSearchOpen && isSearchAtTop && !bottomSearchBar -> SearchBarLevel.Active isSearchOpen && isSearchAtBottom && bottomSearchBar -> SearchBarLevel.Active @@ -613,7 +614,7 @@ fun PullDownScaffold( darkColors = LocalPreferDarkContentOverWallpaper.current && searchBarColor == SearchBarColors.Auto || searchBarColor == SearchBarColors.Dark, bottomSearchBar = bottomSearchBar, searchBarOffset = { - (if (searchBarFocused || fixedSearchBar) 0 else searchBarOffset.value.toInt() * (if (bottomSearchBar) 1 else -1)) + + (if (searchBarFocused || fixedSearchBar) 0 else searchBarOffset.floatValue.toInt() * (if (bottomSearchBar) 1 else -1)) + with(density) { (editModeSearchBarOffset - if (bottomSearchBar) keyboardFilterBarPadding else 0.dp) .toPx() diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchColumn.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchColumn.kt index 6d241c8a..ba6f10fa 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchColumn.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchColumn.kt @@ -1,6 +1,5 @@ package de.mm20.launcher2.ui.launcher.search -import android.util.Log import androidx.activity.compose.BackHandler import androidx.appcompat.app.AppCompatActivity import androidx.compose.animation.AnimatedContent @@ -75,22 +74,22 @@ fun SearchColumn( val hideFavs by viewModel.hideFavorites val favoritesEnabled by viewModel.favoritesEnabled.collectAsState(false) - val apps by viewModel.appResults - val workApps by viewModel.workAppResults - val privateApps by viewModel.privateSpaceAppResults + val apps = viewModel.appResults + val workApps = viewModel.workAppResults + val privateApps = viewModel.privateSpaceAppResults val profiles by viewModel.profiles.collectAsState(emptyList()) val profileStates by viewModel.profileStates.collectAsState(emptyList()) - val appShortcuts by viewModel.appShortcutResults - val contacts by viewModel.contactResults - val files by viewModel.fileResults - val events by viewModel.calendarResults - val unitConverter by viewModel.unitConverterResults - val calculator by viewModel.calculatorResults - val wikipedia by viewModel.articleResults - val locations by viewModel.locationResults - val website by viewModel.websiteResults - val hiddenResults by viewModel.hiddenResults + val appShortcuts = viewModel.appShortcutResults + val contacts = viewModel.contactResults + val files = viewModel.fileResults + val events = viewModel.calendarResults + val unitConverter = viewModel.unitConverterResults + val calculator = viewModel.calculatorResults + val wikipedia = viewModel.articleResults + val locations = viewModel.locationResults + val website = viewModel.websiteResults + val hiddenResults = viewModel.hiddenResults val bestMatch by viewModel.bestMatch diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchVM.kt index d96e6c11..54da56cf 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchVM.kt @@ -2,7 +2,9 @@ package de.mm20.launcher2.ui.launcher.search import android.content.Context import androidx.appcompat.app.AppCompatActivity +import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import de.mm20.launcher2.devicepose.DevicePoseProvider @@ -28,6 +30,7 @@ import de.mm20.launcher2.search.Location import de.mm20.launcher2.search.ResultScore import de.mm20.launcher2.search.SavableSearchable import de.mm20.launcher2.search.SearchFilters +import de.mm20.launcher2.search.SearchResults import de.mm20.launcher2.search.SearchService import de.mm20.launcher2.search.Searchable import de.mm20.launcher2.search.Website @@ -79,8 +82,6 @@ class SearchVM : ViewModel(), KoinComponent { val expandedCategory = mutableStateOf(null) - val locationResults = mutableStateOf>(emptyList()) - val profiles = profileManager.profiles.shareIn( viewModelScope, SharingStarted.WhileSubscribed(), @@ -104,22 +105,25 @@ class SearchVM : ViewModel(), KoinComponent { } } - val appResults = mutableStateOf>(emptyList()) - val workAppResults = mutableStateOf>(emptyList()) - val privateSpaceAppResults = mutableStateOf>(emptyList()) + val appResults = mutableStateListOf() + val workAppResults = mutableStateListOf() + val privateSpaceAppResults = mutableStateListOf() - val appShortcutResults = mutableStateOf>(emptyList()) - val fileResults = mutableStateOf>(emptyList()) - val contactResults = mutableStateOf>(emptyList()) - val calendarResults = mutableStateOf>(emptyList()) - val articleResults = mutableStateOf>(emptyList()) - val websiteResults = mutableStateOf>(emptyList()) - val calculatorResults = mutableStateOf>(emptyList()) - val unitConverterResults = mutableStateOf>(emptyList()) - val searchActionResults = mutableStateOf>(emptyList()) + val appShortcutResults = mutableStateListOf() + val fileResults = mutableStateListOf() + val contactResults = mutableStateListOf() + val calendarResults = mutableStateListOf() + val articleResults = mutableStateListOf
() + val websiteResults = mutableStateListOf() + val calculatorResults = mutableStateListOf() + val unitConverterResults = mutableStateListOf() + val searchActionResults = mutableStateListOf() + val locationResults = mutableStateListOf() + + var previousResults: SearchResults? = null val hiddenResultsButton = searchUiSettings.hiddenItemsButton - val hiddenResults = mutableStateOf>(emptyList()) + val hiddenResults = mutableStateListOf() val favoritesEnabled = searchUiSettings.favorites val hideFavorites = mutableStateOf(false) @@ -179,7 +183,6 @@ class SearchVM : ViewModel(), KoinComponent { } searchQuery.value = query isSearchEmpty.value = query.isEmpty() - hiddenResults.value = emptyList() val filters = filters.value @@ -218,15 +221,6 @@ class SearchVM : ViewModel(), KoinComponent { flowOf(emptyList()) } val allApps = searchService.getAllApps() - fileResults.value = emptyList() - contactResults.value = emptyList() - calendarResults.value = emptyList() - locationResults.value = emptyList() - articleResults.value = emptyList() - websiteResults.value = emptyList() - calculatorResults.value = emptyList() - unitConverterResults.value = emptyList() - searchActionResults.value = emptyList() allApps .combine(hiddenItemKeys) { results, hiddenKeys -> results to hiddenKeys } @@ -253,11 +247,13 @@ class SearchVM : ViewModel(), KoinComponent { ) } hiddenItems += hiddenPrivateApps + previousResults = SearchResults(apps = apps) - appResults.value = apps - workAppResults.value = workApps - privateSpaceAppResults.value = privateApps - hiddenResults.value = hiddenItems + searchActionResults.clear() + appResults.mergeWith(apps) + workAppResults.mergeWith(workApps) + privateSpaceAppResults.mergeWith(privateApps) + hiddenResults.mergeWith(hiddenItems) } } else { @@ -267,127 +263,65 @@ class SearchVM : ViewModel(), KoinComponent { searchService.search( query, filters = if (query.isEmpty()) filters.copy(apps = true) else filters, + previousResults, ) .combine(hiddenItemKeys) { results, hiddenKeys -> results to hiddenKeys } .collectLatest { (results, hiddenKeys) -> - val hiddenItems = mutableListOf() + previousResults = results - if (results.apps != null) { - val (hiddenApps, apps) = results.apps!!.partition { - hiddenKeys.contains( - it.key - ) - } - hiddenItems += hiddenApps - appResults.value = apps.applyRanking(query) - } else { - appResults.value = emptyList() - } - workAppResults.value = emptyList() - privateSpaceAppResults.value = emptyList() + hiddenResults.clear() + workAppResults.clear() + privateSpaceAppResults.clear() - if (results.shortcuts != null) { - val (hiddenShortcuts, shortcuts) = results.shortcuts!!.partition { - hiddenKeys.contains( - it.key - ) - } - hiddenItems += hiddenShortcuts - appShortcutResults.value = shortcuts.applyRanking(query) - } else { - appShortcutResults.value = emptyList() - } + appResults.mergeWith(results.apps, hiddenKeys, query) + appShortcutResults.mergeWith(results.shortcuts, hiddenKeys, query) + fileResults.mergeWith(results.files, hiddenKeys, query) - if (results.files != null) { - val (hiddenFiles, files) = results.files!!.partition { - hiddenKeys.contains( - it.key - ) - } - hiddenItems += hiddenFiles - fileResults.value = files.applyRanking(query) - } else { - fileResults.value = emptyList() - } - - if (results.contacts != null) { - val (hiddenContacts, contacts) = results.contacts!!.partition { - hiddenKeys.contains( - it.key - ) - } - hiddenItems += hiddenContacts - contactResults.value = contacts.applyRanking(query) - } else { - contactResults.value = emptyList() - } - - if (results.calendars != null) { - val (hiddenEvents, events) = results.calendars!!.partition { - hiddenKeys.contains( - it.key - ) - } - hiddenItems += hiddenEvents - calendarResults.value = events.applyRanking(query) - } else { - calendarResults.value = emptyList() - } - - if (results.locations != null && results.locations!!.isNotEmpty()) { - val (hiddenLocations, locations) = results.locations!!.partition { - hiddenKeys.contains( - it.key - ) - } - hiddenItems += hiddenLocations - val lastLocation = devicePoseProvider.lastLocation - if (lastLocation != null) { - locationResults.value = locations.asSequence() - .sortedWith { a, b -> - a.distanceTo(lastLocation) - .compareTo(b.distanceTo(lastLocation)) - } - .distinctBy { it.key } - .toList() - } else { - locationResults.value = locations.applyRanking(query) - } - } else { - locationResults.value = emptyList() - } - - if (results.wikipedia != null) { - articleResults.value = results.wikipedia!!.applyRanking(query) - } else { - articleResults.value = emptyList() - } - - if (results.websites != null) { - websiteResults.value = results.websites!!.applyRanking(query) - } else { - websiteResults.value = emptyList() - } - - - calculatorResults.value = results.calculators ?: emptyList() - unitConverterResults.value = results.unitConverters ?: emptyList() + contactResults.mergeWith( + results.contacts?.filterNot { hiddenKeys.contains(it.key) } + ?.applyRanking(query) + ) + calendarResults.mergeWith( + results.calendars?.filterNot { hiddenKeys.contains(it.key) } + ?.applyRanking(query) + ) + locationResults.mergeWith( + results.locations?.filterNot { hiddenKeys.contains(it.key) } + ?.let { locations -> + devicePoseProvider.lastLocation?.let { + locations.asSequence() + .sortedWith { a, b -> + a.distanceTo(it).compareTo(b.distanceTo(it)) + } + .distinctBy { it.key } + .toList() + } ?: locations.applyRanking(query) + } + ) + articleResults.mergeWith( + results.wikipedia?.applyRanking(query) + ) + websiteResults.mergeWith( + results.websites?.applyRanking(query) + ) + calculatorResults.mergeWith(results.calculators) + unitConverterResults.mergeWith(results.unitConverters) if (results.searchActions != null) { - searchActionResults.value = results.searchActions!! + searchActionResults.mergeWith(results.searchActions!!) } if (launchOnEnter.value) { bestMatch.value = when { - appResults.value.isNotEmpty() -> appResults.value.first() - appShortcutResults.value.isNotEmpty() -> appShortcutResults.value.first() - calendarResults.value.isNotEmpty() -> calendarResults.value.first() - locationResults.value.isNotEmpty() -> locationResults.value.first() - contactResults.value.isNotEmpty() -> contactResults.value.first() - articleResults.value.isNotEmpty() -> articleResults.value.first() - websiteResults.value.isNotEmpty() -> websiteResults.value.first() - fileResults.value.isNotEmpty() -> fileResults.value.first() - searchActionResults.value.isNotEmpty() -> searchActionResults.value.first() + appResults.isNotEmpty() -> appResults.first() + appShortcutResults.isNotEmpty() -> appShortcutResults.first() + calendarResults.isNotEmpty() -> calendarResults.first() + locationResults.isNotEmpty() -> locationResults.first() + contactResults.isNotEmpty() -> contactResults.first() + articleResults.isNotEmpty() -> articleResults.first() + websiteResults.isNotEmpty() -> websiteResults.first() + fileResults.isNotEmpty() -> fileResults.first() + searchActionResults.isNotEmpty() -> searchActionResults.first() else -> null } } else { @@ -494,6 +428,23 @@ class SearchVM : ViewModel(), KoinComponent { } return sorted.distinctBy { it.key }.toList() } + + private fun SnapshotStateList.mergeWith(newItems: List?) { + val items = newItems ?: emptyList() + val diff = toSet() subtract items.toSet() + removeAll(diff) + for ((i, item) in items.withIndex()) { + if (i < size) + set(i, item) + else + add(item) + } + } + private suspend fun SnapshotStateList.mergeWith( + newItems: List?, + hiddenKeys: List, + query: String + ) = this.mergeWith((newItems ?: emptyList()).filterNot { hiddenKeys.contains(it.key) }.applyRanking(query)) } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/searchbar/LauncherSearchBar.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/searchbar/LauncherSearchBar.kt index 57d6fe6b..864728a0 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/searchbar/LauncherSearchBar.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/searchbar/LauncherSearchBar.kt @@ -68,7 +68,7 @@ fun LauncherSearchBar( val searchVM: SearchVM = viewModel() val hiddenItemsButtonEnabled by searchVM.hiddenResultsButton.collectAsState(false) - val hiddenItems by searchVM.hiddenResults + val hiddenItems = searchVM.hiddenResults val sheetManager = LocalBottomSheetManager.current diff --git a/core/base/src/main/java/de/mm20/launcher2/search/Searchable.kt b/core/base/src/main/java/de/mm20/launcher2/search/Searchable.kt index d359192c..38eae738 100644 --- a/core/base/src/main/java/de/mm20/launcher2/search/Searchable.kt +++ b/core/base/src/main/java/de/mm20/launcher2/search/Searchable.kt @@ -1,7 +1,5 @@ package de.mm20.launcher2.search -import kotlinx.coroutines.Deferred - interface Searchable { val score: ResultScore get() = ResultScore.Unspecified diff --git a/core/base/src/main/java/de/mm20/launcher2/search/SearchableRepository.kt b/core/base/src/main/java/de/mm20/launcher2/search/SearchableRepository.kt index 65ffabbf..cd113809 100644 --- a/core/base/src/main/java/de/mm20/launcher2/search/SearchableRepository.kt +++ b/core/base/src/main/java/de/mm20/launcher2/search/SearchableRepository.kt @@ -1,6 +1,5 @@ package de.mm20.launcher2.search -import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.flow.Flow interface SearchableRepository { diff --git a/services/search/src/main/java/de/mm20/launcher2/search/SearchService.kt b/services/search/src/main/java/de/mm20/launcher2/search/SearchService.kt index 53350982..a55a43f6 100644 --- a/services/search/src/main/java/de/mm20/launcher2/search/SearchService.kt +++ b/services/search/src/main/java/de/mm20/launcher2/search/SearchService.kt @@ -30,6 +30,7 @@ interface SearchService { fun search( query: String, filters: SearchFilters, + initialResults: SearchResults? = null, ): Flow fun getAllApps(): Flow @@ -54,9 +55,10 @@ internal class SearchServiceImpl( override fun search( query: String, filters: SearchFilters, + initialResults: SearchResults?, ): Flow = flow { supervisorScope { - val results = MutableStateFlow(SearchResults()) + val results = MutableStateFlow(initialResults ?: SearchResults()) val customAttrResults = customAttributesRepository.search(query) .map { items ->