Skip to content

Commit ec8e302

Browse files
authored
Merge pull request #13209 from woocommerce/issue/13206-handle-empty-variations
[Woo POS][Non-Simple Products] Handle empty state on variation screen
2 parents 4cfa309 + 6cddac3 commit ec8e302

File tree

12 files changed

+181
-65
lines changed

12 files changed

+181
-65
lines changed

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/variations/selector/VariationListHandler.kt

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.woocommerce.android.ui.products.variations.selector
22

33
import kotlinx.coroutines.sync.Mutex
44
import kotlinx.coroutines.sync.withLock
5+
import org.wordpress.android.fluxc.store.WCProductStore
56
import javax.inject.Inject
67

78
class VariationListHandler @Inject constructor(private val repository: VariationSelectorRepository) {
@@ -26,24 +27,39 @@ class VariationListHandler @Inject constructor(private val repository: Variation
2627
return canLoadMore || (offset + PAGE_SIZE < numOfVariations)
2728
}
2829

29-
suspend fun fetchVariations(productId: Long, forceRefresh: Boolean = false): Result<Unit> = mutex.withLock {
30+
suspend fun fetchVariations(
31+
productId: Long,
32+
forceRefresh: Boolean = false,
33+
filterOptions: Map<WCProductStore.VariationFilterOption, String>? = null
34+
): Result<Unit> = mutex.withLock {
3035
// Reset the offset
3136
offset = 0
3237

3338
if (forceRefresh) {
34-
loadVariations(productId)
39+
loadVariations(productId, filterOptions)
3540
} else {
3641
Result.success(Unit)
3742
}
3843
}
3944

40-
suspend fun loadMore(productId: Long): Result<Unit> = mutex.withLock {
45+
suspend fun loadMore(
46+
productId: Long,
47+
filterOptions: Map<WCProductStore.VariationFilterOption, String>? = null
48+
): Result<Unit> = mutex.withLock {
4149
if (!canLoadMore) return@withLock Result.success(Unit)
42-
loadVariations(productId)
50+
loadVariations(productId, filterOptions)
4351
}
4452

45-
private suspend fun loadVariations(productId: Long): Result<Unit> {
46-
return repository.fetchVariations(productId, offset, PAGE_SIZE).onSuccess {
53+
private suspend fun loadVariations(
54+
productId: Long,
55+
filterOptions: Map<WCProductStore.VariationFilterOption, String>? = null
56+
): Result<Unit> {
57+
return repository.fetchVariations(
58+
productId,
59+
offset,
60+
PAGE_SIZE,
61+
filterOptions
62+
).onSuccess {
4763
canLoadMore = it
4864
offset += PAGE_SIZE
4965
}.map { }

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/variations/selector/VariationSelectorRepository.kt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,16 @@ class VariationSelectorRepository @Inject constructor(
2828
suspend fun fetchVariations(
2929
productId: Long,
3030
offset: Int,
31-
pageSize: Int
31+
pageSize: Int,
32+
filterOptions: Map<WCProductStore.VariationFilterOption, String>? = null
3233
): Result<Boolean> {
33-
return productStore.fetchProductVariations(selectedSite.get(), productId, offset, pageSize)
34+
return productStore.fetchProductVariations(
35+
selectedSite.get(),
36+
productId,
37+
offset,
38+
pageSize,
39+
filterOptions = filterOptions,
40+
)
3441
.let { result ->
3542
if (result.isError) {
3643
WooLog.w(

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

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.woocommerce.android.ui.woopos.home.items
22

33
import androidx.compose.foundation.ExperimentalFoundationApi
4+
import androidx.compose.foundation.Image
45
import androidx.compose.foundation.background
56
import androidx.compose.foundation.clickable
67
import androidx.compose.foundation.layout.Arrangement
@@ -10,14 +11,17 @@ import androidx.compose.foundation.layout.PaddingValues
1011
import androidx.compose.foundation.layout.Row
1112
import androidx.compose.foundation.layout.Spacer
1213
import androidx.compose.foundation.layout.fillMaxHeight
14+
import androidx.compose.foundation.layout.fillMaxSize
1315
import androidx.compose.foundation.layout.fillMaxWidth
1416
import androidx.compose.foundation.layout.height
1517
import androidx.compose.foundation.layout.padding
1618
import androidx.compose.foundation.layout.size
1719
import androidx.compose.foundation.layout.width
1820
import androidx.compose.foundation.lazy.LazyListState
1921
import androidx.compose.foundation.lazy.items
22+
import androidx.compose.foundation.rememberScrollState
2023
import androidx.compose.foundation.shape.RoundedCornerShape
24+
import androidx.compose.foundation.verticalScroll
2125
import androidx.compose.material.MaterialTheme
2226
import androidx.compose.material.Text
2327
import androidx.compose.runtime.Composable
@@ -29,12 +33,15 @@ import androidx.compose.ui.Alignment
2933
import androidx.compose.ui.Modifier
3034
import androidx.compose.ui.draw.clip
3135
import androidx.compose.ui.graphics.painter.ColorPainter
36+
import androidx.compose.ui.graphics.vector.ImageVector
3237
import androidx.compose.ui.layout.ContentScale
3338
import androidx.compose.ui.platform.LocalContext
3439
import androidx.compose.ui.res.stringResource
40+
import androidx.compose.ui.res.vectorResource
3541
import androidx.compose.ui.semantics.contentDescription
3642
import androidx.compose.ui.semantics.semantics
3743
import androidx.compose.ui.text.font.FontWeight
44+
import androidx.compose.ui.text.style.TextAlign
3845
import androidx.compose.ui.text.style.TextOverflow
3946
import androidx.compose.ui.unit.dp
4047
import coil.compose.AsyncImage
@@ -45,6 +52,7 @@ import com.woocommerce.android.ui.woopos.common.composeui.WooPosCard
4552
import com.woocommerce.android.ui.woopos.common.composeui.WooPosTheme
4653
import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosLazyColumn
4754
import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosShimmerBox
55+
import com.woocommerce.android.ui.woopos.common.composeui.toAdaptivePadding
4856
import com.woocommerce.android.ui.woopos.home.items.WooPosItem.SimpleProduct
4957
import com.woocommerce.android.ui.woopos.home.items.WooPosItem.VariableProduct
5058
import com.woocommerce.android.ui.woopos.home.items.WooPosItem.Variation
@@ -325,6 +333,51 @@ fun ItemsLoadingItem() {
325333
}
326334
}
327335

336+
@Composable
337+
fun ItemsEmptyList(
338+
title: String,
339+
message: String,
340+
contentDescription: String,
341+
) {
342+
Box(
343+
modifier = Modifier
344+
.fillMaxSize()
345+
.verticalScroll(rememberScrollState()),
346+
contentAlignment = Alignment.Center
347+
) {
348+
Column(
349+
horizontalAlignment = Alignment.CenterHorizontally,
350+
verticalArrangement = Arrangement.Center
351+
) {
352+
Image(
353+
modifier = Modifier.size(104.dp),
354+
imageVector = ImageVector.vectorResource(id = R.drawable.ic_woo_pos_empty_products),
355+
contentDescription = contentDescription,
356+
)
357+
358+
Spacer(modifier = Modifier.height(40.dp.toAdaptivePadding()))
359+
360+
Text(
361+
text = title,
362+
style = MaterialTheme.typography.h4,
363+
fontWeight = FontWeight.Bold,
364+
color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f),
365+
)
366+
367+
Spacer(modifier = Modifier.height(16.dp.toAdaptivePadding()))
368+
369+
Text(
370+
text = message,
371+
style = MaterialTheme.typography.h5,
372+
color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f),
373+
textAlign = TextAlign.Center
374+
)
375+
376+
Spacer(modifier = Modifier.height(8.dp.toAdaptivePadding()))
377+
}
378+
}
379+
}
380+
328381
@Composable
329382
private fun InfiniteListHandler(
330383
listState: LazyListState,

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

Lines changed: 5 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package com.woocommerce.android.ui.woopos.home.items
22

33
import androidx.compose.animation.AnimatedVisibility
44
import androidx.compose.animation.shrinkVertically
5-
import androidx.compose.foundation.Image
65
import androidx.compose.foundation.layout.Arrangement
76
import androidx.compose.foundation.layout.Box
87
import androidx.compose.foundation.layout.Column
@@ -17,8 +16,6 @@ import androidx.compose.foundation.layout.size
1716
import androidx.compose.foundation.layout.width
1817
import androidx.compose.foundation.lazy.LazyListState
1918
import androidx.compose.foundation.lazy.rememberLazyListState
20-
import androidx.compose.foundation.rememberScrollState
21-
import androidx.compose.foundation.verticalScroll
2219
import androidx.compose.material.ContentAlpha
2320
import androidx.compose.material.ExperimentalMaterialApi
2421
import androidx.compose.material.Icon
@@ -35,12 +32,9 @@ import androidx.compose.runtime.collectAsState
3532
import androidx.compose.ui.Alignment
3633
import androidx.compose.ui.Modifier
3734
import androidx.compose.ui.graphics.Color
38-
import androidx.compose.ui.graphics.vector.ImageVector
3935
import androidx.compose.ui.res.painterResource
4036
import androidx.compose.ui.res.stringResource
41-
import androidx.compose.ui.res.vectorResource
4237
import androidx.compose.ui.text.font.FontWeight
43-
import androidx.compose.ui.text.style.TextAlign
4438
import androidx.compose.ui.unit.dp
4539
import androidx.hilt.navigation.compose.hiltViewModel
4640
import com.woocommerce.android.R
@@ -182,7 +176,11 @@ private fun MainItemsList(
182176

183177
is WooPosItemsViewState.Loading -> ItemsLoadingIndicator()
184178

185-
is WooPosItemsViewState.Empty -> ProductsEmptyList()
179+
is WooPosItemsViewState.Empty -> ItemsEmptyList(
180+
title = stringResource(id = R.string.woopos_products_empty_list_title),
181+
message = stringResource(id = R.string.woopos_products_empty_list_message),
182+
contentDescription = stringResource(id = R.string.woopos_products_empty_list_image_description),
183+
)
186184

187185
is WooPosItemsViewState.Error -> ProductsError { onRetryClicked() }
188186
}
@@ -265,47 +263,6 @@ private fun SimpleProductsBanner(
265263
}
266264
}
267265

268-
@Composable
269-
fun ProductsEmptyList() {
270-
Box(
271-
modifier = Modifier
272-
.fillMaxSize()
273-
.verticalScroll(rememberScrollState()),
274-
contentAlignment = Alignment.Center
275-
) {
276-
Column(
277-
horizontalAlignment = Alignment.CenterHorizontally,
278-
verticalArrangement = Arrangement.Center
279-
) {
280-
Image(
281-
modifier = Modifier.size(104.dp),
282-
imageVector = ImageVector.vectorResource(id = R.drawable.ic_woo_pos_empty_products),
283-
contentDescription = stringResource(id = R.string.woopos_products_empty_list_image_description),
284-
)
285-
286-
Spacer(modifier = Modifier.height(40.dp.toAdaptivePadding()))
287-
288-
Text(
289-
text = stringResource(id = R.string.woopos_products_empty_list_title),
290-
style = MaterialTheme.typography.h4,
291-
fontWeight = FontWeight.Bold,
292-
color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f),
293-
)
294-
295-
Spacer(modifier = Modifier.height(16.dp.toAdaptivePadding()))
296-
297-
Text(
298-
text = stringResource(id = R.string.woopos_products_empty_list_message),
299-
style = MaterialTheme.typography.h5,
300-
color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f),
301-
textAlign = TextAlign.Center
302-
)
303-
304-
Spacer(modifier = Modifier.height(8.dp.toAdaptivePadding()))
305-
}
306-
}
307-
}
308-
309266
@Composable
310267
fun ProductsError(onRetryClicked: () -> Unit) {
311268
Box(

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/products/WooPosProductsDataSource.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.woocommerce.android.ui.woopos.home.items.products
22

33
import com.woocommerce.android.model.Product
44
import com.woocommerce.android.ui.products.ProductStatus
5+
import com.woocommerce.android.ui.products.ProductType.VARIABLE
56
import com.woocommerce.android.ui.products.selector.ProductListHandler
67
import com.woocommerce.android.util.WooLog
78
import kotlinx.coroutines.Dispatchers
@@ -83,7 +84,7 @@ class WooPosProductsDataSource @Inject constructor(
8384
}
8485

8586
private fun List<Product>.applyPosProductFilter() = this.filter { product ->
86-
isProductHasAPrice(product)
87+
isProductHasAPrice(product) || product.productType == VARIABLE
8788
}
8889

8990
private fun isProductHasAPrice(product: Product) =

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/variations/WooPosVariationsDataSource.kt

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import kotlinx.coroutines.flow.firstOrNull
1010
import kotlinx.coroutines.flow.flow
1111
import kotlinx.coroutines.flow.flowOn
1212
import kotlinx.coroutines.withContext
13+
import org.wordpress.android.fluxc.store.WCProductStore
1314
import javax.inject.Inject
1415
import javax.inject.Singleton
1516

@@ -47,7 +48,13 @@ class WooPosVariationsDataSource @Inject constructor(
4748
emit(FetchResult.Cached(cachedVariations))
4849
}
4950

50-
val result = handler.fetchVariations(productId, forceRefresh = true)
51+
val result = handler.fetchVariations(
52+
productId,
53+
forceRefresh = true,
54+
filterOptions = mapOf(
55+
WCProductStore.VariationFilterOption.STATUS to "publish"
56+
)
57+
)
5158
if (result.isSuccess) {
5259
val remoteVariations = handler.getVariationsFlow(productId).firstOrNull()?.applyFilter() ?: emptyList()
5360
updateCache(productId, remoteVariations)
@@ -64,7 +71,12 @@ class WooPosVariationsDataSource @Inject constructor(
6471
}.flowOn(Dispatchers.IO)
6572

6673
suspend fun loadMore(productId: Long): Result<List<ProductVariation>> = withContext(Dispatchers.IO) {
67-
val result = handler.loadMore(productId)
74+
val result = handler.loadMore(
75+
productId,
76+
filterOptions = mapOf(
77+
WCProductStore.VariationFilterOption.STATUS to VARIATION_STATUS_PUBLISH
78+
)
79+
)
6880
if (result.isSuccess) {
6981
val fetchedVariations = handler.getVariationsFlow(productId).first().applyFilter()
7082
Result.success(fetchedVariations)
@@ -75,6 +87,10 @@ class WooPosVariationsDataSource @Inject constructor(
7587
)
7688
}
7789
}
90+
91+
companion object {
92+
private const val VARIATION_STATUS_PUBLISH = "publish"
93+
}
7894
}
7995

8096
private fun Result<Unit>.logFailure() {

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/variations/WooPosVariationsScreen.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import com.woocommerce.android.ui.woopos.common.composeui.component.Button
3939
import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosErrorScreen
4040
import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosPaginationErrorIndicator
4141
import com.woocommerce.android.ui.woopos.common.composeui.toAdaptivePadding
42+
import com.woocommerce.android.ui.woopos.home.items.ItemsEmptyList
4243
import com.woocommerce.android.ui.woopos.home.items.ItemsLoadingIndicator
4344
import com.woocommerce.android.ui.woopos.home.items.WooPosItem
4445
import com.woocommerce.android.ui.woopos.home.items.WooPosItemList
@@ -157,7 +158,15 @@ private fun WooPosVariationsScreens(
157158
}
158159
}
159160

160-
else -> {}
161+
is WooPosVariationsViewState.Empty -> {
162+
ItemsEmptyList(
163+
title = stringResource(id = R.string.woopos_variations_empty_list_title),
164+
message = stringResource(id = R.string.woopos_variations_empty_list_message),
165+
contentDescription = stringResource(
166+
id = R.string.woopos_variations_empty_list_image_description
167+
)
168+
)
169+
}
161170
}
162171
}
163172
PullRefreshIndicator(

WooCommerce/src/main/res/values/strings.xml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4321,7 +4321,7 @@
43214321
<string name="woopos_cart_empty_content_description">Cart is empty</string>
43224322
<string name="woopos_products_empty_list_image_description">No products</string>
43234323
<string name="woopos_products_empty_list_title">No supported products found</string>
4324-
<string name="woopos_products_empty_list_message">POS currently only supports simple and variable products – \ncreate one to get started.</string>
4324+
<string name="woopos_products_empty_list_message">POS currently only supports simple, variable, and virtual products – \ncreate one to get started.</string>
43254325
<string name="woopos_products_empty_list_message_two">POS currently only supports simple products</string>
43264326
<string name="woopos_products_loading_error_title">Error loading products</string>
43274327
<string name="woopos_products_loading_error_message">Give it another go?</string>
@@ -4331,6 +4331,10 @@
43314331
<string name="woopos_totals_success_payment_cash">A cash payment of %1$s was successfully made</string>
43324332
<string name="woopos_totals_success_payment_card">A card payment of %1$s was successfully made</string>
43334333

4334+
<string name="woopos_variations_empty_list_title">No supported variations found</string>
4335+
<string name="woopos_variations_empty_list_message">POS currently only supports simple, variable, and virtual products – \ncreate one to get started.</string>
4336+
<string name="woopos_variations_empty_list_image_description">No variations</string>
4337+
43344338
<string name="woopos_success_totals_error_reader_not_connected_title">Reader not connected</string>
43354339
<string name="woopos_success_totals_error_reader_not_connected_subtitle">To process this payment, please connect your reader.</string>
43364340
<string name="woopos_success_totals_error_reader_not_connected_cta_button_label">Connect to reader</string>

WooCommerce/src/test/kotlin/com/woocommerce/android/ui/products/ProductTestUtils.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ object ProductTestUtils {
120120
amount: String = "10.00",
121121
isVirtual: Boolean = false,
122122
isDownloadable: Boolean = false,
123+
isPurchasable: Boolean = true,
123124
): ProductVariation {
124125
return WCProductVariationModel(2).apply {
125126
dateCreated = "2018-01-05T05:14:30Z"
@@ -131,6 +132,7 @@ object ProductTestUtils {
131132
attributes = ""
132133
virtual = isVirtual
133134
downloadable = isDownloadable
135+
purchasable = isPurchasable
134136
}.toAppModel().also { it.priceWithCurrency = "$10.00" }
135137
}
136138

0 commit comments

Comments
 (0)