Skip to content

Commit 81619ae

Browse files
authored
Merge pull request #13442 from woocommerce/13441-woo-pos-removal-animation-from-the-cart-is-missing
[Woo POS] Removal animation from the cart is missing
2 parents 46bb108 + 8f502a2 commit 81619ae

File tree

6 files changed

+65
-154
lines changed

6 files changed

+65
-154
lines changed

RELEASE-NOTES.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
-----
66
- [**] Improved accessibility support (handling of increased text size) in in-person payment flows [https://github.com/woocommerce/woocommerce-android/pull/13414]
77
- [*] Automatically select the first available order when filtering is active in the two-pane layout.[https://github.com/woocommerce/woocommerce-android/pull/13491]
8-
8+
- [*] [Internal] Removal animation of the items from the cart [https://github.com/woocommerce/woocommerce-android/pull/13442]
99

1010
21.7
1111
-----

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

Lines changed: 64 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,9 @@ import androidx.compose.animation.core.animateDpAsState
55
import androidx.compose.animation.core.animateFloatAsState
66
import androidx.compose.animation.core.tween
77
import androidx.compose.animation.expandHorizontally
8-
import androidx.compose.animation.expandVertically
98
import androidx.compose.animation.fadeIn
109
import androidx.compose.animation.fadeOut
1110
import androidx.compose.animation.shrinkHorizontally
12-
import androidx.compose.animation.shrinkVertically
1311
import androidx.compose.foundation.Image
1412
import androidx.compose.foundation.background
1513
import androidx.compose.foundation.layout.Arrangement
@@ -37,9 +35,7 @@ import androidx.compose.runtime.LaunchedEffect
3735
import androidx.compose.runtime.getValue
3836
import androidx.compose.runtime.livedata.observeAsState
3937
import androidx.compose.runtime.mutableIntStateOf
40-
import androidx.compose.runtime.mutableStateOf
4138
import androidx.compose.runtime.remember
42-
import androidx.compose.runtime.setValue
4339
import androidx.compose.ui.Alignment
4440
import androidx.compose.ui.Modifier
4541
import androidx.compose.ui.graphics.painter.ColorPainter
@@ -258,7 +254,7 @@ private fun CartBodyWithItems(
258254
key = { item -> item.id.itemNumber }
259255
) { item ->
260256
ProductItem(
261-
modifier = Modifier,
257+
modifier = Modifier.animateItem(),
262258
item = item,
263259
canRemoveItems = areItemsRemovable,
264260
onUIEvent = onUIEvent,
@@ -402,117 +398,91 @@ private fun ProductItem(
402398
canRemoveItems: Boolean,
403399
onUIEvent: (WooPosCartUIEvent) -> Unit,
404400
) {
405-
var hasAnimationStarted by remember { mutableStateOf(item.isAppearanceAnimationPlayed) }
406-
LaunchedEffect(Unit) {
407-
hasAnimationStarted = true
408-
}
409-
410-
val cardElevation = 6.dp
411-
val elevation by animateDpAsState(
412-
targetValue = if (hasAnimationStarted) cardElevation else 0.dp,
413-
animationSpec = tween(durationMillis = 150, delayMillis = 250),
414-
label = "elevation"
415-
)
416-
417401
val itemContentDescription = stringResource(
418402
id = R.string.woopos_cart_item_content_description,
419403
item.name,
420404
item.price
421405
)
422406

423-
LaunchedEffect(elevation) {
424-
if (elevation == cardElevation) {
425-
onUIEvent(WooPosCartUIEvent.OnCartItemAppearanceAnimationPlayed(item))
426-
}
427-
}
428-
429-
AnimatedVisibility(
430-
visible = hasAnimationStarted,
431-
enter = expandVertically(
432-
animationSpec = tween(durationMillis = 200)
433-
),
434-
exit = shrinkVertically()
407+
WooPosCard(
408+
modifier = modifier
409+
.height(96.dp)
410+
.semantics { contentDescription = itemContentDescription },
411+
elevation = 6.dp,
412+
shadowType = ShadowType.Soft,
413+
shape = RoundedCornerShape(8.dp),
435414
) {
436-
WooPosCard(
437-
modifier = modifier
438-
.height(96.dp)
439-
.semantics { contentDescription = itemContentDescription },
440-
elevation = elevation,
441-
shadowType = ShadowType.Soft,
442-
shape = RoundedCornerShape(8.dp),
415+
Row(
416+
horizontalArrangement = Arrangement.SpaceBetween,
417+
verticalAlignment = Alignment.CenterVertically
443418
) {
444-
Row(
445-
horizontalArrangement = Arrangement.SpaceBetween,
446-
verticalAlignment = Alignment.CenterVertically
447-
) {
448-
AsyncImage(
449-
model = ImageRequest.Builder(LocalContext.current)
450-
.data(item.imageUrl)
451-
.crossfade(true)
452-
.build(),
453-
fallback = ColorPainter(WooPosTheme.colors.loadingSkeleton),
454-
error = ColorPainter(WooPosTheme.colors.loadingSkeleton),
455-
placeholder = ColorPainter(WooPosTheme.colors.loadingSkeleton),
456-
contentDescription = null,
457-
contentScale = ContentScale.Crop,
458-
modifier = Modifier.size(96.dp)
459-
)
419+
AsyncImage(
420+
model = ImageRequest.Builder(LocalContext.current)
421+
.data(item.imageUrl)
422+
.crossfade(true)
423+
.build(),
424+
fallback = ColorPainter(WooPosTheme.colors.loadingSkeleton),
425+
error = ColorPainter(WooPosTheme.colors.loadingSkeleton),
426+
placeholder = ColorPainter(WooPosTheme.colors.loadingSkeleton),
427+
contentDescription = null,
428+
contentScale = ContentScale.Crop,
429+
modifier = Modifier.size(96.dp)
430+
)
460431

461-
Spacer(modifier = Modifier.width(16.dp.toAdaptivePadding()))
432+
Spacer(modifier = Modifier.width(16.dp.toAdaptivePadding()))
462433

463-
Column(
464-
modifier = Modifier.weight(1f)
465-
) {
434+
Column(
435+
modifier = Modifier.weight(1f)
436+
) {
437+
Text(
438+
text = item.name,
439+
style = MaterialTheme.typography.body1,
440+
fontWeight = FontWeight.Bold,
441+
maxLines = 1,
442+
overflow = TextOverflow.Ellipsis,
443+
modifier = Modifier.clearAndSetSemantics { }
444+
)
445+
Spacer(modifier = Modifier.height(4.dp.toAdaptivePadding()))
446+
if (item.description.isNotNullOrEmpty()) {
466447
Text(
467-
text = item.name,
448+
text = item.description!!,
468449
style = MaterialTheme.typography.body1,
469-
fontWeight = FontWeight.Bold,
470-
maxLines = 1,
450+
maxLines = 2,
471451
overflow = TextOverflow.Ellipsis,
472-
modifier = Modifier.clearAndSetSemantics { }
473-
)
474-
Spacer(modifier = Modifier.height(4.dp.toAdaptivePadding()))
475-
if (item.description.isNotNullOrEmpty()) {
476-
Text(
477-
text = item.description!!,
478-
style = MaterialTheme.typography.body1,
479-
maxLines = 2,
480-
overflow = TextOverflow.Ellipsis,
481-
color = MaterialTheme.colors.secondaryVariant,
482-
modifier = Modifier.clearAndSetSemantics { }
483-
)
484-
Spacer(modifier = Modifier.height(4.dp.toAdaptivePadding()))
485-
}
486-
Text(
487-
text = item.price,
488-
style = MaterialTheme.typography.body1,
489452
color = MaterialTheme.colors.secondaryVariant,
490453
modifier = Modifier.clearAndSetSemantics { }
491454
)
455+
Spacer(modifier = Modifier.height(4.dp.toAdaptivePadding()))
492456
}
457+
Text(
458+
text = item.price,
459+
style = MaterialTheme.typography.body1,
460+
color = MaterialTheme.colors.secondaryVariant,
461+
modifier = Modifier.clearAndSetSemantics { }
462+
)
463+
}
493464

494-
if (canRemoveItems) {
495-
Spacer(modifier = Modifier.width(8.dp.toAdaptivePadding()))
465+
if (canRemoveItems) {
466+
Spacer(modifier = Modifier.width(8.dp.toAdaptivePadding()))
496467

497-
val removeButtonContentDescription = stringResource(
498-
id = R.string.woopos_remove_item_button_from_cart_content_description,
499-
item.name
468+
val removeButtonContentDescription = stringResource(
469+
id = R.string.woopos_remove_item_button_from_cart_content_description,
470+
item.name
471+
)
472+
IconButton(
473+
onClick = { onUIEvent(WooPosCartUIEvent.ItemRemovedFromCart(item)) },
474+
modifier = Modifier
475+
.size(32.dp)
476+
.semantics { contentDescription = removeButtonContentDescription }
477+
) {
478+
Icon(
479+
painter = painterResource(id = R.drawable.ic_pos_remove_cart_item),
480+
tint = MaterialTheme.colors.onBackground,
481+
contentDescription = null,
500482
)
501-
IconButton(
502-
onClick = { onUIEvent(WooPosCartUIEvent.ItemRemovedFromCart(item)) },
503-
modifier = Modifier
504-
.size(32.dp)
505-
.semantics { contentDescription = removeButtonContentDescription }
506-
) {
507-
Icon(
508-
painter = painterResource(id = R.drawable.ic_pos_remove_cart_item),
509-
tint = MaterialTheme.colors.onBackground,
510-
contentDescription = null,
511-
)
512-
}
513483
}
514-
Spacer(modifier = Modifier.width(16.dp.toAdaptivePadding()))
515484
}
485+
Spacer(modifier = Modifier.width(16.dp.toAdaptivePadding()))
516486
}
517487
}
518488
}
@@ -542,7 +512,6 @@ fun WooPosCartScreenProductsPreview(modifier: Modifier = Modifier) {
542512
"VW California VW California, VW California,VW California",
543513
description = "test description",
544514
price = "€50,000",
545-
isAppearanceAnimationPlayed = true,
546515
productType = ProductType.Simple,
547516
),
548517
WooPosCartState.Body.WithItems.Item(
@@ -556,7 +525,6 @@ fun WooPosCartScreenProductsPreview(modifier: Modifier = Modifier) {
556525
description = "test description test description test description test description" +
557526
" test description test description test description test description test description",
558527
price = "$150,000",
559-
isAppearanceAnimationPlayed = true,
560528
productType = ProductType.Simple,
561529
),
562530
WooPosCartState.Body.WithItems.Item(
@@ -569,7 +537,6 @@ fun WooPosCartScreenProductsPreview(modifier: Modifier = Modifier) {
569537
name = "VW California",
570538
description = "",
571539
price = "€250,000",
572-
isAppearanceAnimationPlayed = true,
573540
productType = ProductType.Simple,
574541
)
575542
)
@@ -605,7 +572,6 @@ fun WooPosCartScreenCheckoutPreview(modifier: Modifier = Modifier) {
605572
name = "VW California",
606573
description = null,
607574
price = "€50,000",
608-
isAppearanceAnimationPlayed = true,
609575
productType = ProductType.Simple,
610576
),
611577
WooPosCartState.Body.WithItems.Item(
@@ -618,7 +584,6 @@ fun WooPosCartScreenCheckoutPreview(modifier: Modifier = Modifier) {
618584
name = "VW California",
619585
description = null,
620586
price = "$150,000",
621-
isAppearanceAnimationPlayed = true,
622587
productType = ProductType.Simple,
623588
),
624589
WooPosCartState.Body.WithItems.Item(
@@ -631,7 +596,6 @@ fun WooPosCartScreenCheckoutPreview(modifier: Modifier = Modifier) {
631596
name = "VW California",
632597
description = null,
633598
price = "€250,000",
634-
isAppearanceAnimationPlayed = true,
635599
productType = ProductType.Simple,
636600
)
637601
)

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ data class WooPosCartState(
3030
val price: String,
3131
val description: String?,
3232
val imageUrl: String?,
33-
val isAppearanceAnimationPlayed: Boolean,
3433
val productType: ProductType,
3534
) : Parcelable {
3635
@Parcelize

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,4 @@ sealed class WooPosCartUIEvent {
55
data class ItemRemovedFromCart(val item: WooPosCartState.Body.WithItems.Item) : WooPosCartUIEvent()
66
data object ClearAllClicked : WooPosCartUIEvent()
77
data object BackClicked : WooPosCartUIEvent()
8-
data class OnCartItemAppearanceAnimationPlayed(val item: WooPosCartState.Body.WithItems.Item) : WooPosCartUIEvent()
98
}

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

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -93,18 +93,6 @@ class WooPosCartViewModel @Inject constructor(
9393
body = WooPosCartState.Body.Empty
9494
)
9595
}
96-
97-
is WooPosCartUIEvent.OnCartItemAppearanceAnimationPlayed -> {
98-
val currentState = _state.value
99-
val currentStateBody = currentState.body as? WooPosCartState.Body.WithItems ?: return
100-
_state.value = currentState.copy(
101-
body = currentStateBody.copy(
102-
itemsInCart = currentState.body.itemsInCart.map {
103-
if (it.id == event.item.id) it.copy(isAppearanceAnimationPlayed = true) else it
104-
}
105-
)
106-
)
107-
}
10896
}
10997
}
11098

@@ -268,7 +256,6 @@ class WooPosCartViewModel @Inject constructor(
268256
description = null,
269257
price = formatPrice(price),
270258
imageUrl = firstImageUrl,
271-
isAppearanceAnimationPlayed = false,
272259
productType = ProductType.Simple,
273260
)
274261

@@ -286,7 +273,6 @@ class WooPosCartViewModel @Inject constructor(
286273
description = getNameForPOS(product, resourceProvider),
287274
price = formatPrice(price),
288275
imageUrl = image?.source,
289-
isAppearanceAnimationPlayed = false,
290276
productType = ProductType.Variation,
291277
)
292278
}

WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModelTest.kt

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,6 @@ class WooPosCartViewModelTest {
169169
name = product.name,
170170
price = "10.0$",
171171
imageUrl = product.firstImageUrl,
172-
isAppearanceAnimationPlayed = false,
173172
productType = ProductType.Simple,
174173
description = null,
175174
)
@@ -321,7 +320,6 @@ class WooPosCartViewModelTest {
321320
name = product1.name,
322321
price = "10.0$",
323322
imageUrl = product1.firstImageUrl,
324-
isAppearanceAnimationPlayed = false,
325323
productType = ProductType.Simple,
326324
description = null,
327325
)
@@ -423,7 +421,6 @@ class WooPosCartViewModelTest {
423421
name = product.name,
424422
price = "10.0$",
425423
imageUrl = product.firstImageUrl,
426-
isAppearanceAnimationPlayed = false,
427424
productType = ProductType.Simple,
428425
description = null,
429426
)
@@ -499,40 +496,6 @@ class WooPosCartViewModelTest {
499496
verify(analyticsTracker).track(WooPosAnalyticsEvent.Event.ItemAddedToCart)
500497
}
501498

502-
@Test
503-
fun `given non-empty cart, when OnCartItemAppearanceAnimationPlayed is received, then should update UI`() = runTest {
504-
// GIVEN
505-
val product = ProductTestUtils.generateProduct(
506-
productId = 23L,
507-
productName = "title",
508-
amount = "10.0"
509-
).copy(firstImageUrl = "url")
510-
511-
val parentToChildrenEventsMutableFlow = MutableSharedFlow<ParentToChildrenEvent>()
512-
whenever(parentToChildrenEventReceiver.events).thenReturn(parentToChildrenEventsMutableFlow)
513-
whenever(getProductById(eq(product.remoteId))).thenReturn(product)
514-
val sut = createSut()
515-
val states = sut.state.captureValues()
516-
517-
parentToChildrenEventsMutableFlow.emit(
518-
ParentToChildrenEvent.ItemClickedInProductSelector(
519-
WooPosItemsViewModel.ItemClickedData.SimpleProduct(
520-
id = product.remoteId
521-
)
522-
)
523-
)
524-
525-
// WHEN
526-
val firstItem = (states.last().body as WooPosCartState.Body.WithItems).itemsInCart.first()
527-
val updatedItem = firstItem.copy(isAppearanceAnimationPlayed = true)
528-
sut.onUIEvent(WooPosCartUIEvent.OnCartItemAppearanceAnimationPlayed(updatedItem))
529-
530-
// THEN
531-
val finalState = states.last()
532-
val finalItem = (finalState.body as WooPosCartState.Body.WithItems).itemsInCart.first()
533-
assertThat(finalItem.isAppearanceAnimationPlayed).isTrue
534-
}
535-
536499
@Test
537500
fun `when simple product added to cart, then should track analytics event with product type simple`() = runTest {
538501
// GIVEN

0 commit comments

Comments
 (0)