Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.google.gson.reflect.TypeToken
import com.woocommerce.android.R
import com.woocommerce.android.model.Product
import com.woocommerce.android.model.ProductVariation
import com.woocommerce.android.ui.woopos.common.data.models.WooPosProductModelVersion2
import com.woocommerce.android.util.WooLog
import com.woocommerce.android.viewmodel.ResourceProvider
import org.wordpress.android.fluxc.model.WCProductVariationModel
Expand Down Expand Up @@ -91,7 +92,7 @@ class WooPosVariationMapper @Inject constructor(

fun getNameForPOS(
variation: WooPosVariation,
parentProduct: Product? = null,
parentProduct: WooPosProductModelVersion2? = null,
resourceProvider: ResourceProvider,
): String {
return parentProduct?.variationEnabledAttributes?.joinToString(", ") { attribute ->
Expand Down Expand Up @@ -167,7 +168,7 @@ fun WCPosVariationModel.toWooPosVariation(mapper: WooPosVariationMapper): WooPos

fun WooPosVariation.getNameForPOS(
mapper: WooPosVariationMapper,
parentProduct: Product? = null,
parentProduct: WooPosProductModelVersion2? = null,
resourceProvider: ResourceProvider,
): String = mapper.getNameForPOS(this, parentProduct, resourceProvider)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ data class WooPosProductModelVersion2(
val variationIds: List<Long> = emptyList(),
) : Parcelable {

val variationEnabledAttributes
get() = attributes.filter { it.isVariation }
Copy link
Contributor

@kidinov kidinov Sep 8, 2025

Choose a reason for hiding this comment

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

It's an immutable data class, so do we need to refilter the list every time when it's accessed, or maybe it will be more efficient to have it as a field?

 @IgnoredOnParcel
  val variationEnabledAttributes = attributes.filter { it.isVariation }

?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point, I'll create a lazily calculated property - that gives us the best of both worlds.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in 4bb389f


sealed class WooPosPricing : Parcelable {
@Parcelize
data object NoPricing : WooPosPricing()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import androidx.lifecycle.asLiveData
import androidx.lifecycle.map
import androidx.lifecycle.viewModelScope
import com.woocommerce.android.R
import com.woocommerce.android.model.Product
import com.woocommerce.android.ui.woopos.common.composeui.modifier.BarcodeInputDetector
import com.woocommerce.android.ui.woopos.common.data.WooPosGetCouponById
import com.woocommerce.android.ui.woopos.common.data.WooPosGetProductById
import com.woocommerce.android.ui.woopos.common.data.WooPosGetVariationById
import com.woocommerce.android.ui.woopos.common.data.WooPosVariation
import com.woocommerce.android.ui.woopos.common.data.WooPosVariationMapper
import com.woocommerce.android.ui.woopos.common.data.getNameForPOS
import com.woocommerce.android.ui.woopos.common.data.models.WooPosProductModelVersion2
import com.woocommerce.android.ui.woopos.common.data.searchbyidentifier.WooPosSearchByIdentifier
import com.woocommerce.android.ui.woopos.common.data.searchbyidentifier.WooPosSearchByIdentifierResult
import com.woocommerce.android.ui.woopos.common.util.WooPosLogWrapper
Expand All @@ -26,6 +26,7 @@ import com.woocommerce.android.ui.woopos.home.ParentToChildrenEvent
import com.woocommerce.android.ui.woopos.home.WooPosChildrenToParentEventSender
import com.woocommerce.android.ui.woopos.home.WooPosParentToChildrenEventReceiver
import com.woocommerce.android.ui.woopos.home.cart.WooPosCartItemViewState.Coupon.CouponValidationState
import com.woocommerce.android.ui.woopos.home.cart.WooPosCartItemViewState.Product
import com.woocommerce.android.ui.woopos.home.cart.WooPosCartStatus.CHECKOUT
import com.woocommerce.android.ui.woopos.home.cart.WooPosCartStatus.EDITABLE
import com.woocommerce.android.ui.woopos.home.cart.WooPosCartStatus.EMPTY
Expand Down Expand Up @@ -177,8 +178,8 @@ class WooPosCartViewModel @Inject constructor(
private fun getCartItemsDataList(): List<WooPosItemsViewModel.ItemClickedData> {
val itemClickedDataList = (_state.value.body as WooPosCartState.Body.WithItems).itemsInCart.mapNotNull {
when (it) {
is WooPosCartItemViewState.Product.Simple -> WooPosItemsViewModel.ItemClickedData.Product.Simple(it.id)
is WooPosCartItemViewState.Product.Variation -> WooPosItemsViewModel.ItemClickedData.Product.Variation(
is Product.Simple -> WooPosItemsViewModel.ItemClickedData.Product.Simple(it.id)
is Product.Variation -> WooPosItemsViewModel.ItemClickedData.Product.Variation(
productId = it.id,
id = it.variationId
)
Expand Down Expand Up @@ -575,27 +576,26 @@ class WooPosCartViewModel @Inject constructor(
.map { item ->
when (item) {
is WooPosCartItemViewState.Coupon -> item.copy(validationState = CouponValidationState.Unknown)
is WooPosCartItemViewState.Product -> item
is Product -> item
is WooPosCartItemViewState.Error -> item
is WooPosCartItemViewState.Loading -> item
}
}

private suspend fun Product.toCartListItem(itemNumber: Int): WooPosCartItemViewState.Product.Simple =
WooPosCartItemViewState.Product.Simple(
itemNumber = itemNumber,
id = this.remoteId,
name = name,
description = null,
price = formatPrice(price),
imageUrl = firstImageUrl,
)
private suspend fun WooPosProductModelVersion2.toCartListItem(itemNumber: Int): Product.Simple = Product.Simple(
itemNumber = itemNumber,
id = this.remoteId,
name = name,
description = null,
price = formatPrice(pricing.displayPrice),
imageUrl = firstImageUrl,
)

private suspend fun WooPosVariation.toCartListItem(
itemNumber: Int,
product: Product
): WooPosCartItemViewState.Product.Variation =
WooPosCartItemViewState.Product.Variation(
product: WooPosProductModelVersion2
): Product.Variation =
Product.Variation(
itemNumber = itemNumber,
id = product.remoteId,
variationId = this.remoteVariationId,
Expand All @@ -609,7 +609,7 @@ class WooPosCartViewModel @Inject constructor(
(_state.value.body as? WooPosCartState.Body.WithItems)?.itemsInCart?.maxOfOrNull { it.itemNumber } ?: 1

private fun cartContainsPurchasableItems(body: WooPosCartState.Body.WithItems) =
body.itemsInCart.filterIsInstance<WooPosCartItemViewState.Product>().isNotEmpty()
body.itemsInCart.filterIsInstance<Product>().isNotEmpty()

private fun cartContainsLoadingOrErrorItems(body: WooPosCartState.Body.WithItems) =
body.itemsInCart.any { it is WooPosCartItemViewState.Loading || it is WooPosCartItemViewState.Error }
Expand All @@ -621,18 +621,18 @@ class WooPosCartViewModel @Inject constructor(
return when (this) {
is WooPosSearchByIdentifierResult.Success -> {
val product = this.product
WooPosCartItemViewState.Product.Simple(
Product.Simple(
itemNumber = itemNumber,
id = product.remoteId,
name = product.name,
description = null,
price = formatPrice(product.price),
price = formatPrice(product.pricing.displayPrice),
imageUrl = product.firstImageUrl
)
}

is WooPosSearchByIdentifierResult.VariationSuccess -> {
WooPosCartItemViewState.Product.Variation(
Product.Variation(
itemNumber = itemNumber,
id = variation.remoteProductId,
variationId = variation.remoteVariationId,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.woocommerce.android.ui.woopos.home.items.search

import com.woocommerce.android.extensions.semverCompareTo
import com.woocommerce.android.model.Product
import com.woocommerce.android.ui.woopos.common.data.models.WooPosProductModelVersion2
import com.woocommerce.android.util.GetWooCorePluginCachedVersion
import javax.inject.Inject
import javax.inject.Singleton
Expand All @@ -13,14 +13,14 @@ class WooPosProductSearchPredicate @Inject constructor(
private val whitespaceRegex = "\\s+".toRegex()
private var cachedSupportsNameOrSkuSearch: Boolean? = null

operator fun invoke(query: String): (Product) -> Boolean =
operator fun invoke(query: String): (WooPosProductModelVersion2) -> Boolean =
when {
query.isBlank() -> { _ -> true }
isWooCoreSupportsNameOrSkuSearch() -> tokenizedSkuOrNameSearchPredicate(query)
else -> simpleSearchPredicate(query)
}

private fun simpleSearchPredicate(query: String): (Product) -> Boolean {
private fun simpleSearchPredicate(query: String): (WooPosProductModelVersion2) -> Boolean {
val terms: List<String> = query.split(whitespaceRegex).filter { it.isNotBlank() }.map { it.lowercase() }

return { product ->
Expand All @@ -36,7 +36,7 @@ class WooPosProductSearchPredicate @Inject constructor(
}
}

private fun tokenizedSkuOrNameSearchPredicate(query: String): (Product) -> Boolean {
private fun tokenizedSkuOrNameSearchPredicate(query: String): (WooPosProductModelVersion2) -> Boolean {
val tokens: List<String> = query
.trim()
.split(whitespaceRegex)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.woocommerce.android.ui.woopos.home.items.search

import com.woocommerce.android.WooException
import com.woocommerce.android.model.Product
import com.woocommerce.android.model.toAppModel
import com.woocommerce.android.tools.SelectedSite
import com.woocommerce.android.ui.woopos.common.data.WooPosProductsCache
import com.woocommerce.android.ui.woopos.common.data.WooPosProductsTypesFilterConfig
import com.woocommerce.android.ui.woopos.common.data.models.WCProductToWooPosProductModelMapper
import com.woocommerce.android.ui.woopos.common.data.models.WooPosProductModelVersion2
import com.woocommerce.android.util.WooLog
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
Expand All @@ -21,7 +21,8 @@ class WooPosSearchProductsDataSource @Inject constructor(
private val productsCache: WooPosProductsCache,
private val searchResultsIndex: WooPosSearchResultsIndex,
private val searchPredicate: WooPosProductSearchPredicate,
private val productsTypesFilterConfig: WooPosProductsTypesFilterConfig
private val productsTypesFilterConfig: WooPosProductsTypesFilterConfig,
private val posProductModelMapper: WCProductToWooPosProductModelMapper,
) {
companion object {
private const val PAGE_SIZE = 15
Expand All @@ -33,22 +34,23 @@ class WooPosSearchProductsDataSource @Inject constructor(
val hasMorePages: Boolean
get() = canLoadMore.get()

suspend fun searchLocalProducts(query: String): List<Product> = withContext(Dispatchers.IO) {
suspend fun searchLocalProducts(query: String): List<WooPosProductModelVersion2> = withContext(Dispatchers.IO) {
sortProducts(productsCache.getAll().filter(searchPredicate(query))).take(PAGE_SIZE)
}

suspend fun searchRemoteProducts(query: String): Result<List<Product>> = withContext(Dispatchers.IO) {
searchResultsIndex.clearCache()
suspend fun searchRemoteProducts(query: String): Result<List<WooPosProductModelVersion2>> =
withContext(Dispatchers.IO) {
searchResultsIndex.clearCache()

performRemoteSearch(query).fold(
onSuccess = { result ->
Result.success(result.products.sortedBy { it.name.lowercase() })
},
onFailure = { error -> Result.failure(error) }
)
}
performRemoteSearch(query).fold(
onSuccess = { result ->
Result.success(result.products.sortedBy { it.name.lowercase() })
},
onFailure = { error -> Result.failure(error) }
)
}

suspend fun loadMore(query: String): Result<List<Product>> {
suspend fun loadMore(query: String): Result<List<WooPosProductModelVersion2>> {
if (!canLoadMore.get()) {
return Result.success(searchResultsIndex.getSearchResults(query))
}
Expand Down Expand Up @@ -88,7 +90,7 @@ class WooPosSearchProductsDataSource @Inject constructor(
} else {
val searchResult = result.model!!
val products = searchResult.products
.map { product -> product.toAppModel() }
.map { product -> posProductModelMapper.map(product) }
.sortedBy { it.name.lowercase() }

canLoadMore.set(searchResult.canLoadMore)
Expand All @@ -106,17 +108,17 @@ class WooPosSearchProductsDataSource @Inject constructor(
}
}

private fun sortProducts(products: List<Product>): List<Product> {
private fun sortProducts(products: List<WooPosProductModelVersion2>): List<WooPosProductModelVersion2> {
return products.sortedBy { it.name.lowercase() }
}

sealed class ProductsResult {
data class Cached(val products: List<Product>) : ProductsResult()
data class Remote(val productsResult: Result<List<Product>>) : ProductsResult()
data class Cached(val products: List<WooPosProductModelVersion2>) : ProductsResult()
data class Remote(val productsResult: Result<List<WooPosProductModelVersion2>>) : ProductsResult()
}

data class SearchResult(
val products: List<Product>,
val products: List<WooPosProductModelVersion2>,
val canLoadMore: Boolean
)
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.woocommerce.android.ui.woopos.home.items.search

import com.woocommerce.android.model.Product
import com.woocommerce.android.ui.woopos.common.data.WooPosProductsCache
import com.woocommerce.android.ui.woopos.common.data.models.WooPosProductModelVersion2
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import javax.inject.Inject
Expand All @@ -22,7 +22,7 @@ class WooPosSearchResultsIndex @Inject constructor(
paginatedResults = (paginatedResults + productIds).distinct()
}

suspend fun getSearchResults(query: String): List<Product> = mutex.withLock {
suspend fun getSearchResults(query: String): List<WooPosProductModelVersion2> = mutex.withLock {
if (currentSearchQuery != query.lowercase()) {
return emptyList()
}
Expand Down
Loading
Loading