Skip to content

Commit fb05a4f

Browse files
authored
Merge pull request #13961 from woocommerce/woomob-350-woo-posproducts-search-crash-if-popular-product-selected
[WOOMOB-350][Woo POS][Product Search] Crash if popular product selected after pull to refresh
2 parents 508c9c6 + 024b916 commit fb05a4f

10 files changed

+55
-14
lines changed

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/data/WooPosPopularProductsProvider.kt

+4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ class WooPosPopularProductsProvider @Inject constructor(
2626

2727
suspend fun getPopularProducts(): List<Product> = mutex.withLock { popularProductsCache }
2828

29+
suspend fun addPopularItemsToCache() = mutex.withLock {
30+
productsCache.addAll(popularProductsCache)
31+
}
32+
2933
suspend fun fetchAndCachePopularProducts(): Result<Unit> = mutex.withLock {
3034
val result = productStore.fetchProducts(
3135
site = selectedSite.get(),

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosItemsEmptySearchQueryStateScreen.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ fun WooPosItemsEmptySearchQueryStateScreen(
6666
PopularItemsSection(
6767
popularItems = state.popularItems,
6868
onPopularItemClicked = { popularItem ->
69-
onUIEvent(WooPosItemsSearchUiEvent.ItemClicked(popularItem))
69+
onUIEvent(WooPosItemsSearchUiEvent.OnPopularItemClicked(popularItem))
7070
}
7171
)
7272
}

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosItemsSearchEmptyStateRepository.kt

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import com.woocommerce.android.ui.woopos.util.datastore.WooPosPreferencesReposit
66
import kotlinx.coroutines.flow.first
77
import javax.inject.Inject
88

9-
@Suppress("MagicNumber")
109
class WooPosItemsSearchEmptyStateRepository @Inject constructor(
1110
private val preferencesRepository: WooPosPreferencesRepository,
1211
private val popularProductsProvider: WooPosPopularProductsProvider,
@@ -18,4 +17,8 @@ class WooPosItemsSearchEmptyStateRepository @Inject constructor(
1817
suspend fun addRecentSearch(search: String) {
1918
preferencesRepository.addRecentProductSearch(search)
2019
}
20+
21+
suspend fun addPopularItemsToCache() {
22+
popularProductsProvider.addPopularItemsToCache()
23+
}
2124
}

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosItemsSearchScreen.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ private fun WooPosItemsSearchContent(
9999
WooPosItemList(
100100
state = state,
101101
listState = listState,
102-
onItemClicked = { onUIEvent(WooPosItemsSearchUiEvent.ItemClicked(it)) },
102+
onItemClicked = { onUIEvent(WooPosItemsSearchUiEvent.OnItemClicked(it)) },
103103
onEndOfProductsListReached = { onUIEvent(WooPosItemsSearchUiEvent.OnNextPageRequested) },
104104
onErrorWhilePaginating = {
105105
WooPosPaginationErrorIndicator(

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosItemsSearchUiEvent.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ package com.woocommerce.android.ui.woopos.home.items.search
33
import com.woocommerce.android.ui.woopos.home.items.WooPosItemSelectionViewState
44

55
sealed class WooPosItemsSearchUiEvent {
6-
data class ItemClicked(val item: WooPosItemSelectionViewState) : WooPosItemsSearchUiEvent()
6+
data class OnItemClicked(val item: WooPosItemSelectionViewState) : WooPosItemsSearchUiEvent()
77
data object OnNextPageRequested : WooPosItemsSearchUiEvent()
88
data object LoadingErrorRetryButtonClicked : WooPosItemsSearchUiEvent()
99
data class OnRecentSearchClicked(val recentSearch: String) : WooPosItemsSearchUiEvent()
10+
data class OnPopularItemClicked(val item: WooPosItemSelectionViewState) : WooPosItemsSearchUiEvent()
1011
}

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosItemsSearchViewModel.kt

+10-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope
55
import com.woocommerce.android.model.Product
66
import com.woocommerce.android.ui.products.ProductType
77
import com.woocommerce.android.ui.woopos.home.ChildToParentEvent
8+
import com.woocommerce.android.ui.woopos.home.ChildToParentEvent.SearchEvent.RecentSearchSelected
89
import com.woocommerce.android.ui.woopos.home.ParentToChildrenEvent
910
import com.woocommerce.android.ui.woopos.home.WooPosChildrenToParentEventSender
1011
import com.woocommerce.android.ui.woopos.home.WooPosParentToChildrenEventReceiver
@@ -76,7 +77,7 @@ class WooPosItemsSearchViewModel @Inject constructor(
7677
fun onUIEvent(event: WooPosItemsSearchUiEvent) {
7778
when (event) {
7879
WooPosItemsSearchUiEvent.OnNextPageRequested -> onEndOfListReached()
79-
is WooPosItemsSearchUiEvent.ItemClicked -> handleItemClicked(event.item)
80+
is WooPosItemsSearchUiEvent.OnItemClicked -> handleItemClicked(event.item)
8081
WooPosItemsSearchUiEvent.LoadingErrorRetryButtonClicked -> {
8182
val currentState = _viewState.value as? WooPosItemsSearchViewState.Error ?: return
8283
loadContent(currentState.searchQuery)
@@ -85,12 +86,19 @@ class WooPosItemsSearchViewModel @Inject constructor(
8586
is WooPosItemsSearchUiEvent.OnRecentSearchClicked -> {
8687
viewModelScope.launch {
8788
childToParentEventSender.sendToParent(
88-
ChildToParentEvent.SearchEvent.RecentSearchSelected(
89+
RecentSearchSelected(
8990
event.recentSearch
9091
)
9192
)
9293
}
9394
}
95+
96+
is WooPosItemsSearchUiEvent.OnPopularItemClicked -> {
97+
viewModelScope.launch {
98+
emptyStateRepository.addPopularItemsToCache()
99+
handleItemClicked(event.item)
100+
}
101+
}
94102
}
95103
}
96104

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/search/ProductSearchPredicate.kt renamed to WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosProductSearchPredicate.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import javax.inject.Inject
55
import javax.inject.Singleton
66

77
@Singleton
8-
class ProductSearchPredicate @Inject constructor() {
8+
class WooPosProductSearchPredicate @Inject constructor() {
99
private val whitespaceRegex = "\\s+".toRegex()
1010

1111
operator fun invoke(query: String): (Product) -> Boolean {

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosSearchProductsDataSource.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class WooPosSearchProductsDataSource @Inject constructor(
2424
private val selectedSite: SelectedSite,
2525
private val productsCache: WooPosProductsCache,
2626
private val searchResultsIndex: WooPosSearchResultsIndex,
27-
private val searchPredicate: ProductSearchPredicate,
27+
private val searchPredicate: WooPosProductSearchPredicate,
2828
private val productsTypesFilterConfig: WooPosProductsTypesFilterConfig
2929
) {
3030
companion object {

WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosItemsSearchViewModelTest.kt

+30-5
Original file line numberDiff line numberDiff line change
@@ -620,7 +620,7 @@ class WooPosItemsSearchViewModelTest {
620620

621621
// WHEN
622622
val viewModel = createViewModel()
623-
viewModel.onUIEvent(WooPosItemsSearchUiEvent.ItemClicked(simpleProduct))
623+
viewModel.onUIEvent(WooPosItemsSearchUiEvent.OnItemClicked(simpleProduct))
624624

625625
// THEN
626626
verify(mockChildToParentEventSender).sendToParent(
@@ -644,7 +644,7 @@ class WooPosItemsSearchViewModelTest {
644644

645645
// WHEN
646646
val viewModel = createViewModel()
647-
viewModel.onUIEvent(WooPosItemsSearchUiEvent.ItemClicked(variableProduct))
647+
viewModel.onUIEvent(WooPosItemsSearchUiEvent.OnItemClicked(variableProduct))
648648
advanceUntilIdle()
649649

650650
// THEN
@@ -675,7 +675,7 @@ class WooPosItemsSearchViewModelTest {
675675

676676
// THEN
677677
assertThrows(IllegalStateException::class.java) {
678-
viewModel.onUIEvent(WooPosItemsSearchUiEvent.ItemClicked(variation))
678+
viewModel.onUIEvent(WooPosItemsSearchUiEvent.OnItemClicked(variation))
679679
}
680680
}
681681

@@ -706,7 +706,7 @@ class WooPosItemsSearchViewModelTest {
706706
// WHEN
707707
val viewModel = createViewModel()
708708
advanceTimeBy(600)
709-
viewModel.onUIEvent(WooPosItemsSearchUiEvent.ItemClicked(simpleProduct))
709+
viewModel.onUIEvent(WooPosItemsSearchUiEvent.OnItemClicked(simpleProduct))
710710

711711
// THEN
712712
verify(mockEmptyStateProvider).addRecentSearch(query)
@@ -719,12 +719,37 @@ class WooPosItemsSearchViewModelTest {
719719

720720
// WHEN
721721
val viewModel = createViewModel()
722-
viewModel.onUIEvent(WooPosItemsSearchUiEvent.ItemClicked(simpleProduct))
722+
viewModel.onUIEvent(WooPosItemsSearchUiEvent.OnItemClicked(simpleProduct))
723723

724724
// THEN
725725
verify(mockEmptyStateProvider, never()).addRecentSearch(any())
726726
}
727727

728+
@Test
729+
fun `given popular item, when OnPopularItemClicked event is triggered, then add popular items to cache and handle item click`() =
730+
runTest {
731+
// GIVEN
732+
val simpleProduct = Product.Simple(
733+
id = 42L,
734+
name = "Popular Product",
735+
price = "$15.0",
736+
imageUrl = "https://example.com/image.jpg"
737+
)
738+
739+
// WHEN
740+
val viewModel = createViewModel()
741+
viewModel.onUIEvent(WooPosItemsSearchUiEvent.OnPopularItemClicked(simpleProduct))
742+
advanceUntilIdle()
743+
744+
// THEN
745+
verify(mockEmptyStateProvider).addPopularItemsToCache()
746+
verify(mockChildToParentEventSender).sendToParent(
747+
ChildToParentEvent.ItemClickedInProductSelector(
748+
ItemClickedData.Product.Simple(id = simpleProduct.id)
749+
)
750+
)
751+
}
752+
728753
private fun mockSuccessfulSearch(query: String, products: List<com.woocommerce.android.model.Product>) {
729754
whenever(mockDataSource.searchProducts(query)).thenReturn(
730755
flow {

WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosSearchProductsDataSourceTest.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class WooPosSearchProductsDataSourceTest {
3636
private val wooPosProductsCache: WooPosProductsCache = mock()
3737
private val searchResultsIndex: WooPosSearchResultsIndex = mock()
3838
private val selectedSite: SelectedSite = mock()
39-
private val searchPredicate: ProductSearchPredicate = mock()
39+
private val searchPredicate: WooPosProductSearchPredicate = mock()
4040
private val siteModel: SiteModel = mock()
4141

4242
private lateinit var sut: WooPosSearchProductsDataSource

0 commit comments

Comments
 (0)