Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e5ad229
Add WooShippingPackagesEntity
irfano Aug 29, 2025
d31ed1e
Bump WCAndroidDatabase version to 60 and add WooShippingPackagesEntity
irfano Aug 29, 2025
f59ddc6
Add Shipping Packages methods to WooShippingDao
irfano Aug 29, 2025
446d0bc
Refactor WooShippingLabelPackageMapper to use FluxC entities
irfano Aug 29, 2025
696cd34
Inject WooShippingDao into WooShippingLabelPackageRepository
irfano Aug 29, 2025
3c613f9
Update WooShippingLabelPackageRepository to save fetched packages
irfano Aug 29, 2025
b29a566
Rename `fetchAllStorePackages` to `fetchShippingPackages`
irfano Aug 29, 2025
9eda96f
Add new FetchShippingPackages use case to adapt data model changes
irfano Aug 29, 2025
e165382
Remove `FetchPackagesFromStore` and use new `FetchShippingPackages`
irfano Aug 29, 2025
7384ecc
Remove PackageDAOs in favor of using WooShippingPackageEntity
irfano Aug 29, 2025
b0cf524
Add ObserveShippingPackages
irfano Aug 29, 2025
ed433de
Use ObserveShippingPackages in WooShippingLabelPackageCreationViewModel
irfano Aug 29, 2025
948c7e1
Fix unit tests related to shipment packages
irfano Aug 29, 2025
bf8450e
Add new test to ObserveShippingPackagesTest
irfano Aug 29, 2025
55893ae
Add delete and get methods for packages to WooShippingDao
irfano Aug 29, 2025
fcdecd2
Update package cache after saving/deleting packages
irfano Aug 29, 2025
0f87ddb
Update WooShippingLabelPackageRepository tests
irfano Aug 30, 2025
2b4d5c5
Add release note for shipping labels package caching
irfano Aug 30, 2025
5e13f7f
Merge branch 'trunk' into issue/WOOMOB-887-cache-carrier-packages-data
irfano Sep 4, 2025
2cfd82d
Merge branch 'trunk' into issue/WOOMOB-887-cache-carrier-packages-data
irfano Sep 4, 2025
146ebde
Merge branch 'trunk' into issue/WOOMOB-887-cache-carrier-packages-data
irfano Sep 5, 2025
33ce680
Remove unused `deleteShippingPackages` function
irfano Sep 9, 2025
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
1 change: 1 addition & 0 deletions RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- [WEAR] Updated UserAgent of API requests to use `http.agent` System Property to fix performance issues related to WebView usage on app launch [https://github.com/woocommerce/woocommerce-android/pull/14431]
- [***] POS: A new settings to customize the POS [https://github.com/woocommerce/woocommerce-android/pull/14527]
- [*] [Login] Improved the detection of WooCommerce API version during login flow [https://github.com/woocommerce/woocommerce-android/pull/14524]
- [*] Shipping Labels: Adds caching to the "Select a Package" screen [https://github.com/woocommerce/woocommerce-android/pull/14541]
- [*] Add more filter options to orders filters sales channel [https://github.com/woocommerce/woocommerce-android/pull/14546]
- [*] Automatically populate the weight field in the create shipment flow [https://github.com/woocommerce/woocommerce-android/pull/14525]
- [*] [Shipping Labels] Fixed displaying incorrect hazardous material option for purchased labels [https://github.com/woocommerce/woocommerce-android/pull/14571]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import com.woocommerce.android.analytics.AnalyticsTracker.Companion.VALUE_STARTE
import com.woocommerce.android.analytics.AnalyticsTrackerWrapper
import com.woocommerce.android.tools.SelectedSite
import com.woocommerce.android.ui.orders.wooshippinglabels.models.StoreOptionsModel
import com.woocommerce.android.ui.orders.wooshippinglabels.packages.datasource.FetchPackagesFromStore
import com.woocommerce.android.ui.orders.wooshippinglabels.packages.datasource.FetchShippingPackages
import com.woocommerce.android.ui.orders.wooshippinglabels.packages.datasource.ObserveShippingPackages
import com.woocommerce.android.ui.orders.wooshippinglabels.packages.datasource.WooShippingLabelPackageRepository
import com.woocommerce.android.ui.orders.wooshippinglabels.packages.networking.CustomPackageCreationRequestData
import com.woocommerce.android.ui.orders.wooshippinglabels.packages.ui.Carrier
Expand All @@ -27,7 +28,9 @@ import com.woocommerce.android.viewmodel.ScopedViewModel
import com.woocommerce.android.viewmodel.getStateFlow
import com.woocommerce.android.viewmodel.navArgs
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize
Expand All @@ -39,12 +42,15 @@ class WooShippingLabelPackageCreationViewModel @Inject constructor(
savedState: SavedStateHandle,
private val selectedSite: SelectedSite,
private val resourceProvider: ResourceProvider,
private val fetchPackages: FetchPackagesFromStore,
observeShippingPackages: ObserveShippingPackages,
private val fetchShippingPackages: FetchShippingPackages,
private val updateSavedCarrierPackages: UpdateSavedCarrierPackages,
private val packageRepository: WooShippingLabelPackageRepository,
private val tracker: AnalyticsTrackerWrapper,
) : ScopedViewModel(savedState) {
private val navArgs: WooShippingLabelPackageCreationFragmentArgs by savedState.navArgs()
private val shippingPackagesFlow = observeShippingPackages()
.shareIn(viewModelScope, started = SharingStarted.Lazily, replay = 1)

private val _viewState = savedState.getStateFlow(
scope = viewModelScope,
Expand Down Expand Up @@ -72,20 +78,16 @@ class WooShippingLabelPackageCreationViewModel @Inject constructor(
)

init {
loadData()
}

private fun loadData(): Job {
return launch {
tracker.track(AnalyticsEvent.WCS_PACKAGE_SELECTION_STEP, mapOf(KEY_STATE to VALUE_STARTED))
fetchPackages().let { response ->
_viewState.update { viewState -> viewState.copy(packagesState = response) }
if (response is PackagesState.Data) {
tracker.track(AnalyticsEvent.WCS_PACKAGE_SELECTION_STEP, mapOf(KEY_STATE to VALUE_STARTED))
launch {
shippingPackagesFlow.collectLatest { packages ->
_viewState.update { viewState -> viewState.copy(packagesState = packages) }
if (packages is PackagesState.Data) {
tracker.track(AnalyticsEvent.WCS_PACKAGE_SELECTION_STEP, mapOf(KEY_STATE to "loading_success"))
} else if (response is PackagesState.Error) {
} else if (packages is PackagesState.Error) {
tracker.track(
AnalyticsEvent.WCS_PACKAGE_SELECTION_STEP,
mapOf(KEY_STATE to "loading_failed", KEY_ERROR to response.error)
mapOf(KEY_STATE to "loading_failed", KEY_ERROR to packages.error)
)
}
}
Expand Down Expand Up @@ -148,7 +150,7 @@ class WooShippingLabelPackageCreationViewModel @Inject constructor(
fun onRetryClick() {
triggerEvent(ShowLoadingDialog(true))
launch {
loadData().join()
fetchShippingPackages()
triggerEvent(ShowLoadingDialog(false))
}
}
Expand Down Expand Up @@ -333,7 +335,7 @@ class WooShippingLabelPackageCreationViewModel @Inject constructor(

return response.takeIf { it.isError.not() }
?.model?.firstOrNull()
?.let { PackageData.fromPackageDAO(it) }
?.let { PackageData.fromPackageEntity(it) }
?.let { Result.success(it) }
?: Result.failure(Throwable(response.error.type.toString()))
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.woocommerce.android.ui.orders.wooshippinglabels.packages.datasource

import com.woocommerce.android.tools.SelectedSite
import org.wordpress.android.fluxc.persistence.entity.WooShippingPackagesEntity
import javax.inject.Inject

class FetchShippingPackages @Inject constructor(
Copy link
Member

Choose a reason for hiding this comment

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

np, I'm not sure this is needed, since we already use the usercase ObserveShippingPackages for the accessing the packages, I think ObserveShippingPackages can call the repository directly, and it would be easier to follow.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ObserveShippingPackages has a different logic: it uses cached data while also fetching new data. FetchShippingPackages is used for retry functionality to force a fresh fetch, and it’s a very small use case.
If we switch to ObserveShippingPackages, we’d need to add a forceFetch parameter, adjust the logic, and probably rename the use case. I think keeping it as is would be simpler. But let me know if I’ve misunderstood your request.

Copy link
Member

Choose a reason for hiding this comment

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

I think I wasn't clear here, I meant deleting this use case completely, and update ObserveShippingPackages to use the repository directly (calling WooShippingLabelPackageRepository#fetchShippingPackages directly).

This use case doesn't have any additional logic; it just maps the response, which normally should be the repository role.

private val packageRepository: WooShippingLabelPackageRepository,
private val selectedSite: SelectedSite
) {
suspend operator fun invoke(): Result<WooShippingPackagesEntity> {
val response = packageRepository.fetchShippingPackages(selectedSite.get())
val result = response.model
return if (response.isError || result == null) {
val message = response.error?.type?.name ?: "Unknown error"
Result.failure(Exception(message))
} else {
Result.success(result)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.woocommerce.android.ui.orders.wooshippinglabels.packages.datasource

import com.woocommerce.android.tools.SelectedSite
import com.woocommerce.android.ui.orders.wooshippinglabels.packages.WooShippingLabelPackageCreationViewModel.PackagesState
import com.woocommerce.android.ui.orders.wooshippinglabels.packages.ui.Carrier
import com.woocommerce.android.ui.orders.wooshippinglabels.packages.ui.CarrierPackageGroup
import com.woocommerce.android.ui.orders.wooshippinglabels.packages.ui.PackageData
import com.woocommerce.android.ui.orders.wooshippinglabels.packages.ui.StoreOptionsForPackages
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.transformLatest
import kotlinx.coroutines.flow.withIndex
import org.wordpress.android.fluxc.persistence.entity.WooShippingPackagesEntity
import org.wordpress.android.fluxc.persistence.entity.WooShippingPackagesEntity.CarrierPackageGroups
import org.wordpress.android.fluxc.persistence.entity.WooShippingPackagesEntity.StoreOptions
import javax.inject.Inject

class ObserveShippingPackages @Inject constructor(
private val selectedSite: SelectedSite,
private val packageRepository: WooShippingLabelPackageRepository,
private val fetchShippingPackages: FetchShippingPackages
) {
@OptIn(ExperimentalCoroutinesApi::class)
operator fun invoke(): Flow<PackagesState> = packageRepository.observeShippingPackages(selectedSite.get())
.withIndex()
.transformLatest { (index, entity) ->
val isFirst = index == 0
when {
isFirst && entity == null -> {
val result = fetchShippingPackages()
if (result.isFailure) {
emit(PackagesState.Error(result.exceptionOrNull()?.message ?: "Unknown error"))
}
}

entity == null -> emit(PackagesState.Error())
else -> {
emit(entity.toPackagesState())
if (isFirst) {
fetchShippingPackages()
}
}
}
}

private fun StoreOptions.toStoreOptionsForPackages() = StoreOptionsForPackages(
currencySymbol = currencySymbol ?: StoreOptionsForPackages.DEFAULT.currencySymbol,
dimensionUnit = dimensionUnit ?: StoreOptionsForPackages.DEFAULT.dimensionUnit,
weightUnit = weightUnit ?: StoreOptionsForPackages.DEFAULT.weightUnit,
originCountry = originCountry ?: StoreOptionsForPackages.DEFAULT.originCountry
)

private fun WooShippingPackagesEntity.filterCarrierData(): Map<Carrier, List<CarrierPackageGroup>> =
buildMap {
carrierPackageGroups.parseCarrierData(WooShippingPackagesEntity.CarrierType.USPS)
.takeIf { it.isNotEmpty() }
?.let { packageGroups -> put(Carrier.USPS, packageGroups) }

carrierPackageGroups.parseCarrierData(WooShippingPackagesEntity.CarrierType.DHL)
.takeIf { it.isNotEmpty() }
?.let { packageGroups -> put(Carrier.DHL, packageGroups) }

carrierPackageGroups.parseCarrierData(WooShippingPackagesEntity.CarrierType.UPS)
.takeIf { it.isNotEmpty() }
?.let { packageGroups -> put(Carrier.UPS, packageGroups) }
}

private fun List<CarrierPackageGroups>.parseCarrierData(
carrierType: WooShippingPackagesEntity.CarrierType
) = find { it.carrierType == carrierType }?.let {
it.packageGroups?.mapNotNull { group ->
group.description?.let { description ->
CarrierPackageGroup(
groupName = description,
packages = group.packages?.map { packageItem ->
PackageData.fromPackageEntity(packageItem)
}.orEmpty()
)
}
}
} ?: emptyList()

private fun WooShippingPackagesEntity.toPackagesState() = PackagesState.Data(
storeOptions = storeOptions.toStoreOptionsForPackages(),
savedPackages = savedPackages.map { PackageData.fromPackageEntity(it) },
carrierPackages = filterCarrierData()
)
}

This file was deleted.

Loading