Skip to content

Conversation

@joshheald
Copy link
Contributor

Description

This PR animates changes in the item list. This only applies to adding/removing items at present. It's primarily visible using Local Catalog, in search.

The main blocker for this was that we used unstable UUIDs, as they would change when the item state changed. The main work in this PR is to change to use a new POSItemIdentifier struct for the Identifiable conformance of items in the ItemList.

We still use UUID for Identifiable on CartItem but we have the item identifier as well. This is because we can have more than one row in the cart for each product, so we can't just use the product/variation/coupon identifier and expect it to be unique.

Test Steps

Launch the app and open POS on a local catalog store.
Add or remove an item in WP-Admin
Pull to refresh
Observe that the item list updates are animated
Open search, and search for a term which will return multiple results.
Observe that the list changes are animated as you type.

Note that the animations aren't always perfect at the moment, but they're a lot better than none.

Screenshots

item.list.animations.mp4

  • I have considered if this change warrants user-facing release notes and have added them to RELEASE-NOTES.txt if necessary.

joshheald and others added 24 commits November 28, 2025 14:44
- Rename generic Identifier<T> to POSItemIdentifier
- Remove siteID property (only work with one site at a time)
- Structure simplified to (underlyingType, itemID)
- Update all POSItem types to use POSItemIdentifier

This makes the identifier system simpler and more focused on its
purpose: stable identification for animations and deduplication.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Fix variation mapping to use .variation underlyingType
- Remove siteID from all identifier creation
- Update coupon mapping in fetch strategy

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Add posItemIdentifier property to CartItem protocol
- PurchasableItem: Compute from loaded POSOrderableItem
- CouponItem: Store POSItemIdentifier on creation
- Update cart add() method to capture coupon identifier

This enables comparison between cart items and POSItems using
stable identifiers while maintaining UUID for cart animations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Uncomment and fix coupon duplicate check
- Compare cart coupons using posItemIdentifier
- Prevents duplicate coupons from being added to cart

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Update PreviewHelpers to use POSItemIdentifier
- Update ScreenshotMock to use POSItemIdentifier
- Fix ChildItemList previews with proper identifiers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Fix hardcoded coupon itemID in PointOfSaleOrderController
- Use posItemIdentifier from CartItem instead of hardcoding
- Update test mocks to use POSItemIdentifier
- Fix all UUID() usages in PointOfSaleItemServiceTests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Replace UUID() with POSItemIdentifier in all test files
- Use appropriate underlyingType (.product, .variation, .coupon)
- Use meaningful itemID values (productID, variationID, or test IDs)

Files updated:
- POSOrderServiceTests.swift
- GRDBObservableDataSourceTests.swift
- POSProductOrVariationResolverTests.swift
- POSCouponTests.swift
- PointOfSaleItemMapperTests.swift
- PointOfSaleCouponServiceTests.swift
- CartViewHelperTests.swift
- PointOfSaleAggregateModelTests.swift
- PointOfSaleItemsControllerTests.swift
- PointOfSaleOrderControllerTests.swift
- PointOfSaleObservableItemsControllerTests.swift
- POSItemActionHandlerFactoryTests.swift
- POSItemActionHandlerTests.swift

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Instead of reusing `.loading` for error states in
PurchasableItem.posItemIdentifier, we now have a dedicated `.error`
case for clearer semantic meaning.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Updated all preview code in views to use the new POSItemIdentifier
instead of UUID(). This includes:
- CartView, CouponRowView, POSCouponCreationSheet
- SimpleProductCardView, VariationCardView, CouponCardView
- Auto-generated Models+Copiable.generated.swift

Also fixed VariationCardView preview to use .variation instead of
.product for the underlyingType.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Changed id property from UUID to POSItemIdentifier to match the
updated POSOrderableItem protocol.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Changed coupon ID creation from UUID to POSItemIdentifier with
sequential itemID values for test data.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Changed the id parameter type from UUID to POSItemIdentifier in the
makeCartItem helper function.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Updated makeVariationItem() to use POSItemIdentifier instead of .init()
and fixed makeCouponItem() to use .coupon instead of .product for the
underlyingType.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Updated Cart.CouponItem and Cart.PurchasableItem test instantiations
to use UUID for the id parameter and POSItemIdentifier for
posItemIdentifier parameter, matching the updated dual-identifier
architecture.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Added missing import for POSItemIdentifier.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Updated POSCoupon initialization to use POSItemIdentifier instead of
.init() for the id parameter.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Added missing imports for POSItemIdentifier in test files.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Updated Cart.CouponItem instantiations to use UUID for id and
POSItemIdentifier for posItemIdentifier. Fixed makeItem() helper to use
UUID instead of POSItemIdentifier. Changed coupon underlyingType from
.product to .coupon.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Fixes compilation errors where POSItemIdentifier was not found in scope.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Updated makeSimpleProduct and makeVariation helper functions to use POSItemIdentifier instead of UUID, matching the updated type signatures.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Fixes compilation errors where POSItemIdentifier was not found in scope.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@joshheald joshheald added this to the 23.9 milestone Nov 28, 2025
@joshheald joshheald added type: task An internally driven task. feature: POS labels Nov 28, 2025
@wpmobilebot
Copy link
Collaborator

App Icon📲 You can test the changes from this Pull Request in WooCommerce iOS Prototype by scanning the QR code below to install the corresponding build.

App NameWooCommerce iOS Prototype
Build Numberpr16418-f076409
Version23.7
Bundle IDcom.automattic.alpha.woocommerce
Commitf076409
Installation URL5ucvvgj67srvg
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@iamgabrielma iamgabrielma self-assigned this Dec 3, 2025
Copy link
Contributor

@iamgabrielma iamgabrielma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

import Networking
import WooFoundation
import enum NetworkingCore.OrderStatusEnum
import struct Networking.PagedItems
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one can be removed, we're already importing Networking.

Comment on lines +35 to +36
let id = POSItemIdentifier(underlyingType: .coupon, itemID: coupon.couponID)
addedCouponItem = .coupon(.init(id: id, code: coupon.code, summary: coupon.summary(currencySettings: currencySettings)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hints that we could create the POSItemIdentifier internally within the POSItem rather than injecting it, have you had the chance to explore it, and if makes sense to do so?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature: POS type: task An internally driven task.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants