Skip to content

Commit 2500867

Browse files
authored
Merge pull request #14678 from woocommerce/woomob-1412-woo-poslocal-catalog-full-sync-overdue-handling
[Woo POS][Local Catalog] Catalog full sync required
2 parents aadb7b0 + 849a70f commit 2500867

29 files changed

+1024
-60
lines changed

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import com.woocommerce.android.ui.woopos.home.ParentToChildrenEvent.SearchEvent.
1414
import com.woocommerce.android.ui.woopos.home.ParentToChildrenEvent.SearchEvent.RecentSearchSelected
1515
import com.woocommerce.android.ui.woopos.home.WooPosHomeState.DialogState
1616
import com.woocommerce.android.ui.woopos.home.WooPosHomeState.ScreenPositionState
17+
import com.woocommerce.android.ui.woopos.localcatalog.WooPosIncrementalSyncReason
18+
import com.woocommerce.android.ui.woopos.localcatalog.WooPosPerformLocalCatalogIncrementalSync
1719
import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent
1820
import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.BackToCartTapped
1921
import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsTracker
@@ -31,6 +33,7 @@ class WooPosHomeViewModel @Inject constructor(
3133
private val parentToChildrenEventSender: WooPosParentToChildrenEventSender,
3234
private val analyticsTracker: WooPosAnalyticsTracker,
3335
private val soundHelper: WooPosSoundHelper,
36+
private val incrementalSync: WooPosPerformLocalCatalogIncrementalSync,
3437
savedStateHandle: SavedStateHandle,
3538
) : ViewModel() {
3639
private val _state = savedStateHandle.getStateFlow(
@@ -54,6 +57,13 @@ class WooPosHomeViewModel @Inject constructor(
5457
viewModelScope.launch {
5558
soundHelper.preloadChaChing()
5659
}
60+
performLocalCatalogIncrementalSync()
61+
}
62+
63+
private fun performLocalCatalogIncrementalSync() {
64+
viewModelScope.launch {
65+
incrementalSync.execute(WooPosIncrementalSyncReason.ON_POS_HOME)
66+
}
5767
}
5868

5969
override fun onCleared() {
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package com.woocommerce.android.ui.woopos.home.items
2+
3+
import androidx.compose.animation.AnimatedVisibility
4+
import androidx.compose.animation.core.tween
5+
import androidx.compose.animation.fadeIn
6+
import androidx.compose.animation.scaleIn
7+
import androidx.compose.foundation.layout.Arrangement
8+
import androidx.compose.foundation.layout.Column
9+
import androidx.compose.foundation.layout.Row
10+
import androidx.compose.foundation.layout.fillMaxWidth
11+
import androidx.compose.foundation.layout.padding
12+
import androidx.compose.foundation.layout.size
13+
import androidx.compose.foundation.shape.RoundedCornerShape
14+
import androidx.compose.material.icons.Icons
15+
import androidx.compose.material.icons.filled.Close
16+
import androidx.compose.material3.Icon
17+
import androidx.compose.material3.IconButton
18+
import androidx.compose.material3.MaterialTheme
19+
import androidx.compose.runtime.Composable
20+
import androidx.compose.ui.Alignment
21+
import androidx.compose.ui.Modifier
22+
import androidx.compose.ui.res.painterResource
23+
import androidx.compose.ui.res.stringResource
24+
import androidx.compose.ui.unit.dp
25+
import com.woocommerce.android.R
26+
import com.woocommerce.android.ui.woopos.common.composeui.WooPosPreview
27+
import com.woocommerce.android.ui.woopos.common.composeui.component.ShadowType
28+
import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosCard
29+
import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosText
30+
import com.woocommerce.android.ui.woopos.common.composeui.designsystem.WooPosCornerRadius
31+
import com.woocommerce.android.ui.woopos.common.composeui.designsystem.WooPosElevation
32+
import com.woocommerce.android.ui.woopos.common.composeui.designsystem.WooPosSpacing
33+
import com.woocommerce.android.ui.woopos.common.composeui.designsystem.WooPosTheme
34+
import com.woocommerce.android.ui.woopos.common.composeui.designsystem.WooPosTypography
35+
36+
@Composable
37+
fun WooPosCatalogSyncOverdueBanner(
38+
state: WooPosItemsViewModel.CatalogSyncOverdueBannerState,
39+
modifier: Modifier = Modifier,
40+
onDismiss: () -> Unit
41+
) {
42+
AnimatedVisibility(
43+
visible = state is WooPosItemsViewModel.CatalogSyncOverdueBannerState.Visible,
44+
enter = fadeIn(
45+
animationSpec = tween(durationMillis = 180)
46+
) + scaleIn(
47+
animationSpec = tween(durationMillis = 180)
48+
),
49+
modifier = modifier.padding(horizontal = WooPosSpacing.Small.value)
50+
) {
51+
WooPosCard(
52+
modifier = Modifier.fillMaxWidth(),
53+
shape = RoundedCornerShape(WooPosCornerRadius.Medium.value),
54+
backgroundColor = MaterialTheme.colorScheme.surfaceContainerLow,
55+
elevation = WooPosElevation.Medium,
56+
shadowType = ShadowType.Soft,
57+
) {
58+
Row(
59+
horizontalArrangement = Arrangement.spacedBy(WooPosSpacing.Medium.value),
60+
modifier = Modifier
61+
.fillMaxWidth()
62+
.padding(WooPosSpacing.Medium.value),
63+
) {
64+
Icon(
65+
painter = painterResource(id = R.drawable.ic_woo_pos_info_banner),
66+
tint = MaterialTheme.colorScheme.onSurface,
67+
contentDescription = null,
68+
modifier = Modifier
69+
.size(48.dp)
70+
.align(Alignment.CenterVertically)
71+
)
72+
Column(
73+
verticalArrangement = Arrangement.spacedBy(WooPosSpacing.Small.value),
74+
horizontalAlignment = Alignment.Start,
75+
modifier = Modifier.weight(1f)
76+
) {
77+
WooPosText(
78+
text = stringResource(R.string.woopos_refresh_catalog_banner_title),
79+
style = WooPosTypography.BodyLarge,
80+
color = MaterialTheme.colorScheme.onSurface,
81+
)
82+
WooPosText(
83+
text = stringResource(R.string.woopos_refresh_catalog_banner_message),
84+
style = WooPosTypography.BodySmall,
85+
color = MaterialTheme.colorScheme.onSurface,
86+
)
87+
}
88+
IconButton(
89+
onClick = onDismiss,
90+
modifier = Modifier.size(32.dp)
91+
) {
92+
Icon(
93+
imageVector = Icons.Filled.Close,
94+
contentDescription = stringResource(R.string.woopos_refresh_catalog_banner_dismiss),
95+
tint = MaterialTheme.colorScheme.onSurface,
96+
modifier = Modifier.size(24.dp)
97+
)
98+
}
99+
}
100+
}
101+
}
102+
}
103+
104+
@Composable
105+
@WooPosPreview
106+
fun WooPosRefreshCatalogBannerPreview() {
107+
WooPosTheme {
108+
WooPosCatalogSyncOverdueBanner(
109+
state = WooPosItemsViewModel.CatalogSyncOverdueBannerState.Visible,
110+
onDismiss = {}
111+
)
112+
}
113+
}

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

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import androidx.compose.animation.core.LinearEasing
55
import androidx.compose.animation.core.tween
66
import androidx.compose.foundation.layout.Box
77
import androidx.compose.foundation.layout.Column
8+
import androidx.compose.foundation.layout.Spacer
89
import androidx.compose.foundation.layout.fillMaxHeight
910
import androidx.compose.foundation.layout.fillMaxSize
11+
import androidx.compose.foundation.layout.height
1012
import androidx.compose.foundation.layout.padding
1113
import androidx.compose.foundation.layout.statusBarsPadding
1214
import androidx.compose.foundation.lazy.LazyListState
@@ -45,6 +47,7 @@ fun WooPosItemsScreen(modifier: Modifier = Modifier) {
4547
WooPosItemsScreen(
4648
modifier = modifier,
4749
itemsStateFlow = productsViewModel.viewState,
50+
catalogSyncOverdueBannerStateFlow = productsViewModel.catalogSyncOverdueBannerState,
4851
productsViewState = productsViewState,
4952
couponsListState = couponsListState,
5053
onUIEvent = { productsViewModel.onUIEvent(it) },
@@ -56,15 +59,18 @@ fun WooPosItemsScreen(modifier: Modifier = Modifier) {
5659
private fun WooPosItemsScreen(
5760
modifier: Modifier = Modifier,
5861
itemsStateFlow: StateFlow<WooPosItemsToolbarViewState>,
62+
catalogSyncOverdueBannerStateFlow: StateFlow<WooPosItemsViewModel.CatalogSyncOverdueBannerState>,
5963
productsViewState: LazyListState,
6064
couponsListState: LazyListState,
6165
onUIEvent: (WooPosItemsUIEvent) -> Unit,
6266
) {
6367
val state = itemsStateFlow.collectAsState()
68+
val catalogSyncOverdueBannerState = catalogSyncOverdueBannerStateFlow.collectAsState()
6469

6570
MainItemsList(
6671
modifier = modifier,
6772
state = state,
73+
bannerState = catalogSyncOverdueBannerState,
6874
productsViewState = productsViewState,
6975
couponsListState = couponsListState,
7076
onSearchEvent = {
@@ -86,6 +92,7 @@ private fun WooPosItemsScreen(
8692
},
8793
onTabClicked = { onUIEvent(WooPosItemsUIEvent.OnTabClicked(it)) },
8894
onBackClicked = { onUIEvent(WooPosItemsUIEvent.BackFromVariationsClicked) },
95+
onSyncWarningBannerDismissed = { onUIEvent(WooPosItemsUIEvent.SyncOverdueBannerDismissed) },
8996
)
9097
}
9198

@@ -94,20 +101,17 @@ private fun WooPosItemsScreen(
94101
private fun MainItemsList(
95102
modifier: Modifier,
96103
state: State<WooPosItemsToolbarViewState>,
104+
bannerState: State<WooPosItemsViewModel.CatalogSyncOverdueBannerState>,
97105
productsViewState: LazyListState,
98106
couponsListState: LazyListState,
99107
onSearchEvent: (WooPosSearchUIEvent) -> Unit,
100108
onTabClicked: (WooPosItemsToolbarViewState.Tab) -> Unit,
101109
onAddCouponEvent: () -> Unit,
102110
onBackClicked: () -> Unit,
111+
onSyncWarningBannerDismissed: () -> Unit,
103112
) {
104-
Box(
105-
modifier = modifier
106-
.fillMaxSize()
107-
) {
108-
Column(
109-
modifier.fillMaxHeight()
110-
) {
113+
Box(modifier = modifier.fillMaxSize()) {
114+
Column(modifier.fillMaxHeight()) {
111115
WooPosItemsToolbar(
112116
modifier = Modifier
113117
.statusBarsPadding()
@@ -121,6 +125,20 @@ private fun MainItemsList(
121125
onAddCouponEvent = onAddCouponEvent,
122126
)
123127

128+
Spacer(
129+
modifier =
130+
Modifier
131+
.height(WooPosSpacing.Small.value)
132+
.padding(horizontal = WooPosSpacing.Medium.value.toAdaptivePadding())
133+
)
134+
135+
WooPosCatalogSyncOverdueBanner(
136+
state = bannerState.value,
137+
onDismiss = onSyncWarningBannerDismissed
138+
)
139+
140+
Spacer(modifier = Modifier.height(WooPosSpacing.Small.value))
141+
124142
val currentState = state.value
125143

126144
Crossfade(
@@ -216,10 +234,12 @@ fun WooPosItemsScreenSearchVisiblePreview(modifier: Modifier = Modifier) {
216234
tabs = tabs()
217235
)
218236
)
237+
val bannerState = MutableStateFlow(WooPosItemsViewModel.CatalogSyncOverdueBannerState.Visible)
219238
WooPosTheme {
220239
WooPosItemsScreen(
221240
modifier = modifier,
222241
itemsStateFlow = productState,
242+
catalogSyncOverdueBannerStateFlow = bannerState,
223243
productsViewState = rememberLazyListState(),
224244
couponsListState = rememberLazyListState(),
225245
onUIEvent = {},
@@ -242,10 +262,12 @@ fun WooPosItemsScreenSearchHiddenPreview(modifier: Modifier = Modifier) {
242262
tabs = tabs()
243263
)
244264
)
265+
val bannerState = MutableStateFlow(WooPosItemsViewModel.CatalogSyncOverdueBannerState.Visible)
245266
WooPosTheme {
246267
WooPosItemsScreen(
247268
modifier = modifier,
248269
itemsStateFlow = productState,
270+
catalogSyncOverdueBannerStateFlow = bannerState,
249271
productsViewState = rememberLazyListState(),
250272
couponsListState = rememberLazyListState(),
251273
onUIEvent = {},

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ sealed class WooPosItemsUIEvent {
1212
data object CloseSearchClicked : WooPosItemsUIEvent()
1313
data object SearchIconClicked : WooPosItemsUIEvent()
1414
data object AddCouponIconClicked : WooPosItemsUIEvent()
15+
data object SyncOverdueBannerDismissed : WooPosItemsUIEvent()
1516
}

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import com.woocommerce.android.ui.woopos.home.items.WooPosItemsToolbarViewState.
1212
import com.woocommerce.android.ui.woopos.home.items.WooPosItemsToolbarViewState.Tab
1313
import com.woocommerce.android.ui.woopos.home.items.coupons.creation.WooPosCouponCreationFacade
1414
import com.woocommerce.android.ui.woopos.home.items.variations.WooPosVariationsNavigationData
15+
import com.woocommerce.android.ui.woopos.localcatalog.WooPosFullSyncRequirement
16+
import com.woocommerce.android.ui.woopos.localcatalog.WooPosFullSyncStatusChecker
1517
import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent
1618
import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.SearchButtonTapped
1719
import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEventConstant
@@ -35,6 +37,7 @@ class WooPosItemsViewModel @Inject constructor(
3537
private val parentToChildrenEventReceiver: WooPosParentToChildrenEventReceiver,
3638
private val preferencesRepository: WooPosPreferencesRepository,
3739
private val analyticsTracker: WooPosAnalyticsTracker,
40+
private val syncStatusChecker: WooPosFullSyncStatusChecker,
3841
) : ViewModel() {
3942
private var preservedStateBeforeOpeningVariations: WooPosItemsToolbarViewState? = null
4043
private val _viewState = MutableStateFlow<WooPosItemsToolbarViewState>(initialState())
@@ -45,6 +48,10 @@ class WooPosItemsViewModel @Inject constructor(
4548
initialValue = _viewState.value,
4649
)
4750

51+
private val _catalogSyncOverdueBannerState =
52+
MutableStateFlow<CatalogSyncOverdueBannerState>(CatalogSyncOverdueBannerState.Hidden)
53+
val catalogSyncOverdueBannerState: StateFlow<CatalogSyncOverdueBannerState> = _catalogSyncOverdueBannerState
54+
4855
init {
4956
listenUpEvents()
5057
searchHelper.initialize(
@@ -55,6 +62,18 @@ class WooPosItemsViewModel @Inject constructor(
5562
viewModelScope.launch {
5663
preferencesRepository.setWasOpenedOnce(true)
5764
}
65+
66+
checkSyncStatusAndUpdateBanner()
67+
}
68+
69+
private fun checkSyncStatusAndUpdateBanner() {
70+
viewModelScope.launch {
71+
val requirement = syncStatusChecker.checkSyncRequirement()
72+
_catalogSyncOverdueBannerState.value = when (requirement) {
73+
is WooPosFullSyncRequirement.Overdue -> CatalogSyncOverdueBannerState.Visible
74+
else -> CatalogSyncOverdueBannerState.Hidden
75+
}
76+
}
5877
}
5978

6079
fun onUIEvent(event: WooPosItemsUIEvent) {
@@ -77,6 +96,9 @@ class WooPosItemsViewModel @Inject constructor(
7796
}
7897

7998
is WooPosItemsUIEvent.AddCouponIconClicked -> createAndAddCoupon()
99+
WooPosItemsUIEvent.SyncOverdueBannerDismissed -> {
100+
_catalogSyncOverdueBannerState.value = CatalogSyncOverdueBannerState.Hidden
101+
}
80102
}
81103
}
82104

@@ -241,4 +263,9 @@ class WooPosItemsViewModel @Inject constructor(
241263
@Parcelize
242264
data class Coupon(override val id: Long, val couponCode: String) : ItemClickedData(id), Parcelable
243265
}
266+
267+
sealed class CatalogSyncOverdueBannerState {
268+
data object Hidden : CatalogSyncOverdueBannerState()
269+
data object Visible : CatalogSyncOverdueBannerState()
270+
}
244271
}

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

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

33
import androidx.compose.foundation.layout.Box
44
import androidx.compose.foundation.layout.fillMaxSize
5-
import androidx.compose.foundation.layout.padding
65
import androidx.compose.foundation.lazy.LazyListState
76
import androidx.compose.foundation.lazy.rememberLazyListState
87
import androidx.compose.material.ExperimentalMaterialApi
@@ -22,7 +21,6 @@ import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosErrorS
2221
import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosErrorScreenButtonState
2322
import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosPaginationErrorIndicator
2423
import com.woocommerce.android.ui.woopos.common.composeui.designsystem.WooPosIcons
25-
import com.woocommerce.android.ui.woopos.common.composeui.designsystem.WooPosSpacing
2624
import com.woocommerce.android.ui.woopos.common.composeui.designsystem.WooPosTheme
2725
import com.woocommerce.android.ui.woopos.home.items.WooPosCouponsViewState
2826
import com.woocommerce.android.ui.woopos.home.items.WooPosItemList
@@ -74,7 +72,6 @@ private fun WooPosCouponsScreen(
7472
when (val itemsState = state.value) {
7573
is WooPosCouponsViewState.Content -> {
7674
WooPosItemList(
77-
modifier = Modifier.padding(top = WooPosSpacing.Large.value),
7875
state = itemsState,
7976
listState = listState,
8077
onItemClicked = { item -> onUIEvent(WooPosCouponsUIEvent.CouponClicked(item.id, item.name)) },
@@ -88,9 +85,7 @@ private fun WooPosCouponsScreen(
8885
}
8986
}
9087

91-
is WooPosCouponsViewState.Loading -> WooPosItemsLoadingIndicator(
92-
modifier = Modifier.padding(top = WooPosSpacing.Large.value)
93-
)
88+
is WooPosCouponsViewState.Loading -> WooPosItemsLoadingIndicator()
9489

9590
is WooPosCouponsViewState.Empty -> WooPosEmptyScreen(
9691
modifier = Modifier.fillMaxSize(),

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@ private fun Content(
139139
onEndOfItemListReached: () -> Unit
140140
) {
141141
WooPosItemList(
142-
modifier = Modifier.padding(top = WooPosSpacing.Large.value),
143142
state = itemsState,
144143
listState = listState,
145144
onItemClicked = onItemClicked,

0 commit comments

Comments
 (0)