From 59afd7493f4d3eb149ae05beea3745229d632ba3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Fri, 25 Oct 2024 03:02:40 +0300 Subject: [PATCH] Debounce performing search depending on searchquery length --- .../quest/ui/register/RegisterViewModel.kt | 70 +++++++++++++------ .../ui/register/RegisterViewModelTest.kt | 15 ++-- 2 files changed, 60 insertions(+), 25 deletions(-) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModel.kt index 3d8aca437c..2a0ab63a80 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModel.kt @@ -33,10 +33,13 @@ import com.google.android.fhir.sync.CurrentSyncJobStatus import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject import kotlin.math.ceil +import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch @@ -78,10 +81,12 @@ import org.smartregister.fhircore.engine.util.SharedPreferencesHelper import org.smartregister.fhircore.engine.util.extension.encodeJson import org.smartregister.fhircore.quest.data.register.RegisterPagingSource import org.smartregister.fhircore.quest.data.register.model.RegisterPagingSourceState +import org.smartregister.fhircore.quest.ui.shared.models.SearchQuery import org.smartregister.fhircore.quest.util.extensions.referenceToBitmap import org.smartregister.fhircore.quest.util.extensions.toParamDataMap import timber.log.Timber +@OptIn(FlowPreview::class) @HiltViewModel class RegisterViewModel @Inject @@ -112,6 +117,27 @@ constructor( } private val decodedImageMap = mutableStateMapOf() + private val _searchQueryFlow: MutableSharedFlow = MutableSharedFlow() + + init { + viewModelScope.launch { + _searchQueryFlow + .debounce { + val searchText = it.query + when (searchText.length) { + 0 -> 2.milliseconds // when search is cleared + 1, + 2, -> 1000.milliseconds + else -> 500.milliseconds + } + } + .collect { + val registerId = registerUiState.value.registerId + performSearch(registerId, it) + } + } + } + /** * This function paginates the register data. An optional [clearCache] resets the data in the * cache (this is necessary after a questionnaire has been submitted to refresh the register with @@ -191,26 +217,7 @@ constructor( when (event) { // Search using name or patient logicalId or identifier. Modify to add more search params is RegisterEvent.SearchRegister -> { - if (event.searchQuery.isBlank()) { - val regConfig = retrieveRegisterConfiguration(registerId) - val searchByDynamicQueries = !regConfig.searchBar?.dataFilterFields.isNullOrEmpty() - if (searchByDynamicQueries) { - registerFilterState.value = RegisterFilterState() // Reset queries - } - when { - regConfig.infiniteScroll -> - registerData.value = retrieveCompleteRegisterData(registerId, searchByDynamicQueries) - else -> - retrieveRegisterUiState( - registerId = registerId, - screenTitle = registerUiState.value.screenTitle, - params = registerUiState.value.params.toTypedArray(), - clearCache = searchByDynamicQueries, - ) - } - } else { - filterRegisterData(event.searchQuery.query) - } + viewModelScope.launch { _searchQueryFlow.emit(event.searchQuery) } } is RegisterEvent.MoveToNextPage -> { currentPage.value = currentPage.value.plus(1) @@ -224,6 +231,29 @@ constructor( } } + private fun performSearch(registerId: String, searchQuery: SearchQuery) { + if (searchQuery.isBlank()) { + val regConfig = retrieveRegisterConfiguration(registerId) + val searchByDynamicQueries = !regConfig.searchBar?.dataFilterFields.isNullOrEmpty() + if (searchByDynamicQueries) { + registerFilterState.value = RegisterFilterState() // Reset queries + } + when { + regConfig.infiniteScroll -> + registerData.value = retrieveCompleteRegisterData(registerId, searchByDynamicQueries) + else -> + retrieveRegisterUiState( + registerId = registerId, + screenTitle = registerUiState.value.screenTitle, + params = registerUiState.value.params.toTypedArray(), + clearCache = searchByDynamicQueries, + ) + } + } else { + filterRegisterData(searchQuery.query) + } + } + fun filterRegisterData(searchText: String) { val searchBar = registerUiState.value.registerConfiguration?.searchBar val registerId = registerUiState.value.registerId diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModelTest.kt index 3746f047c9..f2ce20f729 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/register/RegisterViewModelTest.kt @@ -114,7 +114,7 @@ class RegisterViewModelTest : RobolectricTest() { @Test @kotlinx.coroutines.ExperimentalCoroutinesApi fun testRetrieveRegisterUiState() = runTest { - every { registerViewModel.retrieveRegisterConfiguration(any()) } returns + val registerConfig = RegisterConfiguration( appId = "app", id = registerId, @@ -122,6 +122,8 @@ class RegisterViewModelTest : RobolectricTest() { FhirResourceConfig(baseResource = ResourceConfig(resource = ResourceType.Patient)), pageSize = 10, ) + configurationRegistry.configCacheMap[registerId] = registerConfig + every { registerViewModel.paginateRegisterData(any(), any()) } just runs coEvery { registerRepository.countRegisterData(any()) } returns 200 registerViewModel.retrieveRegisterUiState( @@ -143,8 +145,8 @@ class RegisterViewModelTest : RobolectricTest() { } @Test - fun testOnEventSearchRegister() { - every { registerViewModel.retrieveRegisterConfiguration(any()) } returns + fun testOnEventSearchRegister() = runTest { + val registerConfig = RegisterConfiguration( appId = "app", id = registerId, @@ -152,8 +154,11 @@ class RegisterViewModelTest : RobolectricTest() { FhirResourceConfig(baseResource = ResourceConfig(resource = ResourceType.Patient)), pageSize = 10, ) - every { registerViewModel.registerUiState } returns - mutableStateOf(RegisterUiState(registerId = registerId)) + configurationRegistry.configCacheMap[registerId] = registerConfig + registerViewModel.registerUiState.value = + registerViewModel.registerUiState.value.copy(registerId = registerId) + coEvery { registerRepository.countRegisterData(any()) } returns 0L + // Search with empty string should paginate the data registerViewModel.onEvent(RegisterEvent.SearchRegister(SearchQuery.emptyText)) verify { registerViewModel.retrieveRegisterUiState(any(), any(), any(), any()) }