Skip to content

Commit

Permalink
Merge pull request #12972 from woocommerce/issue/12967-retry-paginati…
Browse files Browse the repository at this point in the history
…on-error

[WooPos][Non Simple Product types] Retry UI for pagination errors on variations screens
  • Loading branch information
AnirudhBhat authored Dec 12, 2024
2 parents 9f2acf7 + 83b55b4 commit 7c2f5cc
Show file tree
Hide file tree
Showing 18 changed files with 819 additions and 626 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,29 @@ import javax.inject.Inject

class VariationListHandler @Inject constructor(private val repository: VariationSelectorRepository) {
companion object {
private const val PAGE_SIZE = 10
private const val PAGE_SIZE = 25
}

private val mutex = Mutex()
private var offset = 0
private var canLoadMore = true
private var canLoadMore = false

fun getVariationsFlow(productId: Long) = repository.observeVariations(productId)

fun canLoadMore(): Boolean {
return canLoadMore
suspend fun resetState() {
mutex.withLock {
offset = 0
canLoadMore = false
}
}

fun canLoadMore(numOfVariations: Int): Boolean {
return canLoadMore || (offset + PAGE_SIZE < numOfVariations)
}

suspend fun fetchVariations(productId: Long, forceRefresh: Boolean = false): Result<Unit> = mutex.withLock {
// Reset the offset
offset = 0
canLoadMore = true

if (forceRefresh) {
loadVariations(productId)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package com.woocommerce.android.ui.woopos.common.composeui.component

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.woocommerce.android.R
import com.woocommerce.android.ui.woopos.common.composeui.ShadowType
import com.woocommerce.android.ui.woopos.common.composeui.WooPosCard
import com.woocommerce.android.ui.woopos.common.composeui.WooPosPreview
import com.woocommerce.android.ui.woopos.common.composeui.WooPosTheme
import com.woocommerce.android.ui.woopos.common.composeui.toAdaptivePadding
import com.woocommerce.android.ui.woopos.home.items.PaginationState
import com.woocommerce.android.ui.woopos.home.items.WooPosItem.SimpleProduct
import com.woocommerce.android.ui.woopos.home.items.WooPosItem.VariableProduct
import com.woocommerce.android.ui.woopos.home.items.WooPosItemList
import com.woocommerce.android.ui.woopos.home.items.WooPosItemsViewState

@Composable
fun WooPosPaginationErrorIndicator(
modifier: Modifier = Modifier,
icon: Painter = painterResource(id = R.drawable.woo_pos_ic_error),
message: String,
primaryButton: Button,
) {
WooPosPaginationErrorIndicatorContent(
modifier = modifier,
icon = icon,
message = message,
primaryButton = primaryButton
)
}

@Composable
private fun WooPosPaginationErrorIndicatorContent(
modifier: Modifier,
icon: Painter,
message: String,
primaryButton: Button
) {
val itemContentDescription = stringResource(id = R.string.woopos_items_pagination_error_content_description)
WooPosCard(
modifier = modifier
.semantics { contentDescription = itemContentDescription },
shape = RoundedCornerShape(8.dp),
backgroundColor = MaterialTheme.colors.surface,
elevation = 6.dp,
shadowType = ShadowType.Soft,
) {
Row(
modifier = Modifier
.height(112.dp)
.fillMaxWidth()
.padding(16.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier.weight(1f)
) {
Icon(
modifier = Modifier.size(24.dp),
painter = icon,
contentDescription = stringResource(R.string.woopos_error_icon_content_description),
tint = Color.Unspecified,
)
Text(
text = message,
style = MaterialTheme.typography.h5,
fontWeight = FontWeight.SemiBold,
color = MaterialTheme.colors.error,
modifier = Modifier.padding(start = 24.dp.toAdaptivePadding())
)
}

WooPosButton(
text = primaryButton.text,
onClick = primaryButton.click,
modifier = Modifier
.weight(0.5f)
.height(50.dp)
.clip(RoundedCornerShape(16.dp))
)
}
}
}

@Composable
@WooPosPreview
fun WooPosPaginationErrorScreenPreview() {
val itemsState =
WooPosItemsViewState.Content(
items = listOf(
SimpleProduct(
1,
name = "Product 1, Product 1, Product 1, " +
"Product 1, Product 1, Product 1, Product 1, Product 1" +
"Product 1, Product 1, Product 1, Product 1, Product 1",
price = "10.0$",
imageUrl = null,
),
SimpleProduct(
2,
name = "Product 2",
price = "2000.00$",
imageUrl = null,
),
VariableProduct(
3,
name = "Product 3",
price = "2000.00$",
imageUrl = null,
numOfVariations = 20,
variationIds = listOf()
),
),
paginationState = PaginationState.Error,
reloadingProductsWithPullToRefresh = true,
bannerState = WooPosItemsViewState.Content.BannerState(
isBannerHiddenByUser = true,
title = R.string.woopos_banner_simple_products_only_title,
message = R.string.woopos_banner_simple_products_only_message,
icon = R.drawable.info,
),
)
WooPosTheme {
WooPosItemList(
state = itemsState,
listState = rememberLazyListState(),
onItemClicked = {},
onEndOfProductsListReached = {}
) {
WooPosPaginationErrorIndicator(
message = stringResource(id = R.string.woopos_items_pagination_error),
primaryButton = Button(
text = stringResource(id = R.string.woopos_items_pagination_load_more_label),
click = {}
)
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ sealed class WooPosBaseViewState(

interface ContentViewState {
val items: List<WooPosItem>
val loadingMore: Boolean
val reloadingProductsWithPullToRefresh: Boolean
val paginationState: PaginationState
}

sealed class PaginationState {
data object None : PaginationState()
data object Loading : PaginationState()
data object Error : PaginationState()
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,12 @@ import kotlinx.coroutines.flow.filter

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ItemList(
fun WooPosItemList(
state: ContentViewState,
listState: LazyListState,
onItemClicked: (item: WooPosItem) -> Unit,
onEndOfProductsListReached: () -> Unit,
onErrorWhilePaginating: @Composable () -> Unit,
) {
WooPosLazyColumn(
verticalArrangement = Arrangement.spacedBy(8.dp),
Expand Down Expand Up @@ -95,11 +96,21 @@ fun ItemList(
}
}

if (state.loadingMore) {
item {
ItemsLoadingItem()
when (state.paginationState) {
PaginationState.Error -> {
item {
onErrorWhilePaginating()
}
}
PaginationState.Loading -> {
item {
ItemsLoadingItem()
}
}
PaginationState.None -> {
}
}

item {
Spacer(modifier = Modifier.height(104.dp))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import com.woocommerce.android.ui.woopos.common.composeui.WooPosPreview
import com.woocommerce.android.ui.woopos.common.composeui.WooPosTheme
import com.woocommerce.android.ui.woopos.common.composeui.component.Button
import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosErrorScreen
import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosPaginationErrorIndicator
import com.woocommerce.android.ui.woopos.common.composeui.toAdaptivePadding
import com.woocommerce.android.ui.woopos.home.items.WooPosItem.SimpleProduct
import com.woocommerce.android.ui.woopos.home.items.WooPosItem.VariableProduct
Expand Down Expand Up @@ -164,12 +165,18 @@ private fun MainItemsList(
onSimpleProductsBannerLearnMoreClicked,
onSimpleProductsBannerClosed
)
ItemList(
WooPosItemList(
itemsState,
listState,
onItemClicked,
onEndOfItemListReached,
)
) {
ProductsPaginationError(
onRetryClicked = {
onEndOfItemListReached()
}
)
}
}
}

Expand Down Expand Up @@ -317,6 +324,17 @@ fun ProductsError(onRetryClicked: () -> Unit) {
}
}

@Composable
private fun ProductsPaginationError(onRetryClicked: () -> Unit) {
WooPosPaginationErrorIndicator(
message = stringResource(id = R.string.woopos_items_pagination_error),
primaryButton = Button(
text = stringResource(id = R.string.woopos_items_pagination_load_more_label),
click = onRetryClicked
),
)
}

@OptIn(ExperimentalMaterialApi::class)
@Composable
@WooPosPreview
Expand Down Expand Up @@ -353,7 +371,63 @@ fun WooPosItemsScreenPreview(modifier: Modifier = Modifier) {
imageUrl = null,
),
),
loadingMore = true,
paginationState = PaginationState.Loading,
reloadingProductsWithPullToRefresh = true,
bannerState = WooPosItemsViewState.Content.BannerState(
isBannerHiddenByUser = true,
title = R.string.woopos_banner_simple_products_only_title,
message = R.string.woopos_banner_simple_products_only_message,
icon = R.drawable.info,
),
)
)
WooPosTheme {
WooPosItemsScreen(
modifier = modifier,
itemsStateFlow = productState,
listState = rememberLazyListState(),
onItemClicked = {},
onEndOfItemListReached = {},
onPullToRefresh = {},
onRetryClicked = {},
onSimpleProductsBannerClosed = {},
onSimpleProductsBannerLearnMoreClicked = {},
onToolbarInfoIconClicked = {},
)
}
}

@OptIn(ExperimentalMaterialApi::class)
@Composable
@WooPosPreview
fun WooPosItemsScreenPaginationErrorPreview(modifier: Modifier = Modifier) {
val productState = MutableStateFlow(
WooPosItemsViewState.Content(
items = listOf(
SimpleProduct(
1,
name = "Product 1, Product 1, Product 1, " +
"Product 1, Product 1, Product 1, Product 1, Product 1" +
"Product 1, Product 1, Product 1, Product 1, Product 1",
price = "10.0$",
imageUrl = null,
),
SimpleProduct(
2,
name = "Product 2",
price = "2000.00$",
imageUrl = null,
),
VariableProduct(
3,
name = "Product 3",
price = "2000.00$",
imageUrl = null,
numOfVariations = 20,
variationIds = listOf()
),
),
paginationState = PaginationState.Error,
reloadingProductsWithPullToRefresh = true,
bannerState = WooPosItemsViewState.Content.BannerState(
isBannerHiddenByUser = true,
Expand Down Expand Up @@ -472,7 +546,6 @@ fun WooPosHomeScreenItemsWithSimpleProductsOnlyBannerPreview() {
imageUrl = null,
),
),
loadingMore = false,
reloadingProductsWithPullToRefresh = true,
bannerState = WooPosItemsViewState.Content.BannerState(
isBannerHiddenByUser = false,
Expand Down Expand Up @@ -525,7 +598,6 @@ fun WooPosHomeScreenItemsWithInfoIconInToolbarPreview() {
imageUrl = null,
),
),
loadingMore = false,
reloadingProductsWithPullToRefresh = false,
bannerState = WooPosItemsViewState.Content.BannerState(
isBannerHiddenByUser = true,
Expand Down
Loading

0 comments on commit 7c2f5cc

Please sign in to comment.