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
@@ -1,8 +1,8 @@
package com.woocommerce.android.ui.woopos.common.data

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.models.WCProductToWooPosProductModelMapper
import com.woocommerce.android.ui.woopos.common.data.models.WooPosProductModelVersion2
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.withContext
import org.wordpress.android.fluxc.network.rest.wpcom.wc.product.ProductRestClient
Expand All @@ -12,8 +12,9 @@ class WooPosGetProductById @Inject constructor(
private val selectedSite: SelectedSite,
private val cache: WooPosProductsCache,
private val productRestClient: ProductRestClient,
private val productMapper: WCProductToWooPosProductModelMapper,
) {
suspend operator fun invoke(productId: Long): Product? = withContext(IO) {
suspend operator fun invoke(productId: Long): WooPosProductModelVersion2? = withContext(IO) {
val cachedProduct = cache.getProductById(productId)
if (cachedProduct != null) {
return@withContext cachedProduct
Expand All @@ -27,7 +28,7 @@ class WooPosGetProductById @Inject constructor(

return@withContext if (!remoteProductResult.isError) {
val remoteProduct = remoteProductResult.productWithMetaData.product
val product = remoteProduct.toAppModel()
val product = productMapper.map(remoteProduct)
cache.addAll(listOf(product))
product
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.woocommerce.android.ui.woopos.common.data

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.models.WCProductToWooPosProductModelMapper
import com.woocommerce.android.ui.woopos.common.data.models.WooPosProductModelVersion2
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.wordpress.android.fluxc.store.WCProductStore
Expand All @@ -16,15 +16,16 @@ class WooPosPopularProductsProvider @Inject constructor(
private val productStore: WCProductStore,
private val productsCache: WooPosProductsCache,
private val productsTypesFilterConfig: WooPosProductsTypesFilterConfig,
private val productMapper: WCProductToWooPosProductModelMapper,
) {
companion object {
private const val MAX_POPULAR_PRODUCTS = 10
}

private val mutex = Mutex()
private val popularProductsCache = mutableListOf<Product>()
private val popularProductsCache = mutableListOf<WooPosProductModelVersion2>()

suspend fun getPopularProducts(): List<Product> = mutex.withLock { popularProductsCache }
suspend fun getPopularProducts(): List<WooPosProductModelVersion2> = mutex.withLock { popularProductsCache }

suspend fun addPopularItemsToCache() = mutex.withLock {
productsCache.addAll(popularProductsCache)
Expand All @@ -44,7 +45,7 @@ class WooPosPopularProductsProvider @Inject constructor(
Result.failure(Exception(result.error.message))
} else {
val products = result.model ?: emptyList()
var productsAppModel = products.map { it.toAppModel() }
val productsAppModel = products.map { productMapper.map(it) }

popularProductsCache.clear()
popularProductsCache.addAll(productsAppModel)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
package com.woocommerce.android.ui.woopos.common.data

import com.woocommerce.android.model.Product
import com.woocommerce.android.ui.woopos.common.data.models.WooPosProductModelVersion2

interface WooPosProductsCache {
companion object {
const val MAX_CACHE_SIZE = 2_000
}

suspend fun addAll(products: List<Product>)
suspend fun addAll(products: List<WooPosProductModelVersion2>)

suspend fun getAll(): List<Product>
suspend fun getAll(): List<WooPosProductModelVersion2>

suspend fun getProductById(productId: Long): Product?
suspend fun getProductById(productId: Long): WooPosProductModelVersion2?

suspend fun getProductByGlobalUniqueIdentifier(globalUniqueIdentifier: String): Product?
suspend fun getProductByGlobalUniqueIdentifier(globalUniqueIdentifier: String): WooPosProductModelVersion2?

suspend fun updateProduct(product: Product)
suspend fun updateProduct(product: WooPosProductModelVersion2)

suspend fun deleteProduct(productId: Long)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.woocommerce.android.ui.woopos.common.data

import com.woocommerce.android.model.Product
import com.woocommerce.android.ui.woopos.common.data.WooPosProductsCache.Companion.MAX_CACHE_SIZE
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 @@ -16,13 +16,13 @@ class WooPosProductsInMemoryCache @Inject constructor() : WooPosProductsCache {
private const val LOAD_FACTOR = 0.75f
}

private val productsCache = LinkedHashMap<Long, Product>(INITIAL_CAPACITY, LOAD_FACTOR, true)
private val productsCache = LinkedHashMap<Long, WooPosProductModelVersion2>(INITIAL_CAPACITY, LOAD_FACTOR, true)

override suspend fun addAll(products: List<Product>) = mutex.withLock {
override suspend fun addAll(products: List<WooPosProductModelVersion2>) = mutex.withLock {
addAllInternal(products)
}

override suspend fun updateProduct(product: Product) = mutex.withLock {
override suspend fun updateProduct(product: WooPosProductModelVersion2) = mutex.withLock {
productsCache[product.remoteId] = product
}

Expand All @@ -32,23 +32,22 @@ class WooPosProductsInMemoryCache @Inject constructor() : WooPosProductsCache {
}
}

override suspend fun getAll(): List<Product> = mutex.withLock {
override suspend fun getAll(): List<WooPosProductModelVersion2> = mutex.withLock {
return productsCache.values.toList()
}

override suspend fun getProductById(productId: Long): Product? = mutex.withLock {
override suspend fun getProductById(productId: Long): WooPosProductModelVersion2? = mutex.withLock {
return productsCache[productId]
}

override suspend fun getProductByGlobalUniqueIdentifier(globalUniqueIdentifier: String): Product? {
return productsCache.values.find { it.globalUniqueId == globalUniqueIdentifier }
}
override suspend fun getProductByGlobalUniqueIdentifier(globalUniqueIdentifier: String) =
productsCache.values.find { it.globalUniqueId == globalUniqueIdentifier }

override suspend fun clear() = mutex.withLock {
productsCache.clear()
}

private fun addAllInternal(products: List<Product>) {
private fun addAllInternal(products: List<WooPosProductModelVersion2>) {
products.forEach { product ->
productsCache[product.remoteId] = product
if (productsCache.size > MAX_CACHE_SIZE) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.woocommerce.android.ui.woopos.home.items.products

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.async
Expand All @@ -29,7 +29,8 @@ class WooPosProductsDataSource @Inject constructor(
private val selectedSite: SelectedSite,
private val productsCache: WooPosProductsCache,
private val productsIndex: WooPosProductsIndex,
private val productsTypesFilterConfig: WooPosProductsTypesFilterConfig
private val productsTypesFilterConfig: WooPosProductsTypesFilterConfig,
private val posProductMapper: WCProductToWooPosProductModelMapper,
) {
private val canLoadMore = AtomicBoolean(false)
private val offset = AtomicInteger(0)
Expand Down Expand Up @@ -57,7 +58,8 @@ class WooPosProductsDataSource @Inject constructor(
val pageOneResult = pageOne.await()
val pageTwoResult = pageTwo.await()

fun List<WCProductModel>?.toAppModels(): List<Product> = this?.map { it.toAppModel() } ?: emptyList()
fun List<WCProductModel>?.toAppModels(): List<WooPosProductModelVersion2> =
this?.map { posProductMapper.map(it) } ?: emptyList()

when {
pageOneResult.isError -> {
Expand Down Expand Up @@ -96,7 +98,7 @@ class WooPosProductsDataSource @Inject constructor(
}
}.flowOn(Dispatchers.IO).take(2)

suspend fun loadMore(): Result<List<Product>> = withContext(Dispatchers.IO) {
suspend fun loadMore(): Result<List<WooPosProductModelVersion2>> = withContext(Dispatchers.IO) {
if (!canLoadMore.get()) {
return@withContext Result.success(productsIndex.getProductList())
}
Expand All @@ -110,19 +112,19 @@ class WooPosProductsDataSource @Inject constructor(
}
}

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

private suspend fun fetchProducts(): Result<List<Product>> {
private suspend fun fetchProducts(): Result<List<WooPosProductModelVersion2>> {
val result = fetchProductsFromStore(
offset = offset.get(),
pageSize = NORMAL_PAGE_SIZE
)

return if (!result.isError) {
val productsList = result.model ?: emptyList()
val products = productsList.map { it.toAppModel() }
val products = productsList.map { posProductMapper.map(it) }

canLoadMore.set(productsList.size == NORMAL_PAGE_SIZE)
offset.addAndGet(NORMAL_PAGE_SIZE)
Expand Down Expand Up @@ -155,8 +157,8 @@ class WooPosProductsDataSource @Inject constructor(
}

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()
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.woocommerce.android.ui.woopos.home.items.products

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 @@ -19,7 +19,7 @@ class WooPosProductsIndex @Inject constructor(
productListIds = (productListIds + productIds).distinct()
}

suspend fun getProductList(): List<Product> = mutex.withLock {
suspend fun getProductList(): List<WooPosProductModelVersion2> = mutex.withLock {
return productListIds.mapNotNull { productsCache.getProductById(it) }
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.woocommerce.android.ui.woopos.common.data

import com.woocommerce.android.tools.SelectedSite
import com.woocommerce.android.ui.woopos.common.data.models.WCProductToWooPosProductModelMapper
import kotlinx.coroutines.test.runTest
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
Expand Down Expand Up @@ -29,6 +30,7 @@ class WooPosPopularProductsProviderTest {
on { filters }.thenReturn(emptyMap())
on { includeTypes }.thenReturn(emptyList())
}
private val productMapper: WCProductToWooPosProductModelMapper = mock()

private val sampleProducts = listOf(
WCProductModel().copy(
Expand Down Expand Up @@ -196,6 +198,8 @@ class WooPosPopularProductsProviderTest {
selectedSite = selectedSite,
productStore = productStore,
productsCache = productsCache,
productsTypesFilterConfig = productsTypesFilterConfig
productsTypesFilterConfig = productsTypesFilterConfig,
productMapper = productMapper,

Comment on lines +202 to +203
Copy link

Copilot AI Sep 4, 2025

Choose a reason for hiding this comment

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

There's an unnecessary blank line before the closing parenthesis. Remove the extra line for consistent formatting.

Suggested change
productMapper = productMapper,

Copilot uses AI. Check for mistakes.
)
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package com.woocommerce.android.ui.woopos.common.data

import com.woocommerce.android.model.Product
import com.woocommerce.android.ui.woopos.common.data.models.WooPosProductModelVersion2
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.test.runTest
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.mock
import java.math.BigDecimal
import java.util.Date

Expand Down Expand Up @@ -147,10 +146,19 @@ class WooPosProductsInMemoryCacheTest {
private fun createTestProduct(
id: Long,
name: String = "Test Product $id"
): Product = mock {
on { remoteId }.thenReturn(id)
on { this.name }.thenReturn(name)
on { price }.thenReturn(BigDecimal.TEN)
on { dateCreated }.thenReturn(Date())
}
): WooPosProductModelVersion2 =
WooPosProductModelVersion2(
remoteId = id,
parentId = null,
name = name,
sku = "SKU-$id",
pricing = WooPosProductModelVersion2.WooPosPricing.RegularPricing(price = BigDecimal.TEN),
globalUniqueId = "global-$id",
type = WooPosProductModelVersion2.WooPosProductType.SIMPLE,
status = WooPosProductModelVersion2.WooPosProductStatus.PUBLISH,
description = "Description for $name",
shortDescription = "Short description for $name",
isDownloadable = false,
lastModified = Date().toString(),
)
}
Loading