From a95805a8296ae81ceb071f98ea10e77d2d38a719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81sar=20Vargas=20Casaseca?= Date: Wed, 29 Oct 2025 11:43:49 +0100 Subject: [PATCH 01/21] WIP: Adding first batch of analytics --- .../toolbar/WooPosHomeFloatingToolbarViewModel.kt | 3 ++- .../ui/woopos/orders/WooPosOrdersDataSource.kt | 7 ++++++- .../ui/woopos/orders/WooPosOrdersViewModel.kt | 2 ++ .../woopos/util/analytics/WooPosAnalyticsEvent.kt | 15 +++++++++++++++ 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbarViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbarViewModel.kt index 4e9133ce6841..274fd315c301 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbarViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbarViewModel.kt @@ -20,6 +20,7 @@ import com.woocommerce.android.ui.woopos.home.toolbar.WooPosHomeFloatingToolbarU import com.woocommerce.android.ui.woopos.home.toolbar.WooPosHomeFloatingToolbarUIEvent.OnOutsideOfToolbarMenuClicked import com.woocommerce.android.ui.woopos.home.toolbar.WooPosHomeFloatingToolbarUIEvent.OnToolbarMenuClicked import com.woocommerce.android.ui.woopos.util.WooPosNetworkStatus +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.GoToOrdersTapped import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.ExitTapped import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsTracker import com.woocommerce.android.viewmodel.ResourceProvider @@ -87,6 +88,7 @@ class WooPosHomeFloatingToolbarViewModel @Inject constructor( R.string.woopos_orders_title -> { viewModelScope.launch { childrenToParentEventSender.sendToParent(ChildToParentEvent.NavigationEvent.ToOrders) + analyticsTracker.track(GoToOrdersTapped) } } R.string.woopos_settings_title -> { @@ -98,7 +100,6 @@ class WooPosHomeFloatingToolbarViewModel @Inject constructor( R.string.woopos_exit_confirmation_title -> viewModelScope.launch { childrenToParentEventSender.sendToParent(ChildToParentEvent.ExitPosClicked) - analyticsTracker.track(ExitTapped) } } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSource.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSource.kt index 5d5f6db25060..a38f73282393 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSource.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSource.kt @@ -3,6 +3,9 @@ package com.woocommerce.android.ui.woopos.orders import com.woocommerce.android.model.Order import com.woocommerce.android.model.OrderMapper import com.woocommerce.android.tools.SelectedSite +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListFetched +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsTracker import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow @@ -30,7 +33,8 @@ class WooPosOrdersDataSource @Inject constructor( private val restClient: OrderRestClient, private val selectedSite: SelectedSite, private val orderMapper: OrderMapper, - private val ordersCache: WooPosOrdersInMemoryCache + private val ordersCache: WooPosOrdersInMemoryCache, + private val analyticsTracker: WooPosAnalyticsTracker ) { private val canLoadMore = AtomicBoolean(false) private val page = AtomicInteger(1) @@ -49,6 +53,7 @@ class WooPosOrdersDataSource @Inject constructor( val result = loadFirstPage() result.onSuccess { + analyticsTracker.track(OrdersListFetched()) ordersCache.setAll(it) emit(LoadOrdersResult.SuccessRemote(it)) }.onFailure { diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt index 19b29402d108..4cf2c8cc3a2d 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt @@ -12,6 +12,7 @@ import com.woocommerce.android.ui.woopos.home.ChildToParentEvent.NavigationEvent import com.woocommerce.android.ui.woopos.home.WooPosChildrenToParentEventSender import com.woocommerce.android.ui.woopos.home.items.WooPosPaginationState import com.woocommerce.android.ui.woopos.home.items.WooPosPullToRefreshState +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsTracker import com.woocommerce.android.ui.woopos.util.ext.formatToMMMddYYYYAtHHmm import com.woocommerce.android.ui.woopos.util.format.WooPosFormatPrice import com.woocommerce.android.viewmodel.ResourceProvider @@ -35,6 +36,7 @@ class WooPosOrdersViewModel @Inject constructor( private val childrenToParentEventSender: WooPosChildrenToParentEventSender, private val formatPrice: WooPosFormatPrice, private val getOrderRefunds: WooPosGetOrderRefundsByOrderId, + private val analyticsTracker: WooPosAnalyticsTracker ) : ViewModel() { private val _state = MutableStateFlow( diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt index 83534602b730..dbe741e80b59 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt @@ -126,6 +126,21 @@ sealed class WooPosAnalyticsEvent : IAnalyticsEvent { data object InteractionWithCustomerStarted : Event() { override val name: String = "interaction_with_customer_started" } + data object GoToOrdersTapped: Event() { + override val name: String = "orders_menu_item_tapped" + } + + data class OrdersListFetched(val milimetersSinceRequestSent: Boolean): Event() { + override val name: String = "orders_list_fetched" + + init { + addProperties( + mapOf( + "milliseconds_since_request_sent" to milimetersSinceRequestSent.toString() + ) + ) + } + } data class BarcodeScanned( val scanDurationMs: Long, From 8d5fb97d54722b11788d359f22dae1f16ab48df2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81sar=20Vargas=20Casaseca?= Date: Wed, 29 Oct 2025 16:45:55 +0100 Subject: [PATCH 02/21] add milliseconds parameter --- .../android/ui/woopos/orders/WooPosOrdersDataSource.kt | 6 +++++- .../ui/woopos/util/analytics/WooPosAnalyticsEvent.kt | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSource.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSource.kt index a38f73282393..9cbc31aa3afe 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSource.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSource.kt @@ -1,5 +1,6 @@ package com.woocommerce.android.ui.woopos.orders +import android.os.SystemClock import com.woocommerce.android.model.Order import com.woocommerce.android.model.OrderMapper import com.woocommerce.android.tools.SelectedSite @@ -51,9 +52,12 @@ class WooPosOrdersDataSource @Inject constructor( val cached = ordersCache.getAll() if (cached.isNotEmpty()) emit(LoadOrdersResult.SuccessCache(cached)) + val startMs = SystemClock.elapsedRealtime() val result = loadFirstPage() + val elapsedMs = SystemClock.elapsedRealtime() - startMs + result.onSuccess { - analyticsTracker.track(OrdersListFetched()) + analyticsTracker.track(OrdersListFetched(elapsedMs)) ordersCache.setAll(it) emit(LoadOrdersResult.SuccessRemote(it)) }.onFailure { diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt index dbe741e80b59..b30e0062b58e 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt @@ -130,7 +130,7 @@ sealed class WooPosAnalyticsEvent : IAnalyticsEvent { override val name: String = "orders_menu_item_tapped" } - data class OrdersListFetched(val milimetersSinceRequestSent: Boolean): Event() { + data class OrdersListFetched(val milimetersSinceRequestSent: Long): Event() { override val name: String = "orders_list_fetched" init { From 293db1bd60d875273d16b2cbe548f82cd67cf103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81sar=20Vargas=20Casaseca?= Date: Thu, 30 Oct 2025 11:08:01 +0100 Subject: [PATCH 03/21] Analytics event for POS Orders pull to refresh --- .../android/ui/woopos/orders/WooPosOrdersViewModel.kt | 6 ++++++ .../ui/woopos/util/analytics/WooPosAnalyticsEvent.kt | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt index 05387a53776a..2903c9d19682 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt @@ -13,6 +13,7 @@ import com.woocommerce.android.ui.woopos.home.ChildToParentEvent.NavigationEvent import com.woocommerce.android.ui.woopos.home.WooPosChildrenToParentEventSender import com.woocommerce.android.ui.woopos.home.items.WooPosPaginationState import com.woocommerce.android.ui.woopos.home.items.WooPosPullToRefreshState +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsTracker import com.woocommerce.android.ui.woopos.util.ext.formatToMMMddYYYYAtHHmm import com.woocommerce.android.ui.woopos.util.format.WooPosFormatPrice @@ -30,6 +31,7 @@ import kotlinx.coroutines.launch import java.math.BigDecimal import java.util.Locale import javax.inject.Inject +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListPullToRefreshTriggered @HiltViewModel class WooPosOrdersViewModel @Inject constructor( @@ -89,6 +91,10 @@ class WooPosOrdersViewModel @Inject constructor( } fun onRefresh() { + viewModelScope.launch { + analyticsTracker.track(OrdersListPullToRefreshTriggered) + } + val currentState = _state.value _state.value = when (currentState) { is WooPosOrdersState.Content -> currentState.copy( diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt index b30e0062b58e..c15472e65fa7 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt @@ -130,6 +130,10 @@ sealed class WooPosAnalyticsEvent : IAnalyticsEvent { override val name: String = "orders_menu_item_tapped" } + data object OrdersListPullToRefreshTriggered: Event() { + override val name: String = "orders_list_pull_to_refresh" + } + data class OrdersListFetched(val milimetersSinceRequestSent: Long): Event() { override val name: String = "orders_list_fetched" From 0b49a94d9da70cc85cb88cab6dfd895258a087ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81sar=20Vargas=20Casaseca?= Date: Thu, 30 Oct 2025 11:14:36 +0100 Subject: [PATCH 04/21] Analytics Event tracking for the loading the next page in orders --- .../android/ui/woopos/orders/WooPosOrdersViewModel.kt | 2 ++ .../android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt index 2903c9d19682..ffdb31f42a84 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt @@ -32,6 +32,7 @@ import java.math.BigDecimal import java.util.Locale import javax.inject.Inject import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListPullToRefreshTriggered +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListNextPageLoaded @HiltViewModel class WooPosOrdersViewModel @Inject constructor( @@ -179,6 +180,7 @@ class WooPosOrdersViewModel @Inject constructor( val result = ordersDataSource.loadMore(normalizedQuery) if (result.isSuccess) { + analyticsTracker.track(OrdersListNextPageLoaded) appendOrders(result.getOrThrow()) } else { _state.value = newState.copy(paginationState = WooPosPaginationState.Error) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt index c15472e65fa7..59540ce1faf6 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt @@ -134,6 +134,10 @@ sealed class WooPosAnalyticsEvent : IAnalyticsEvent { override val name: String = "orders_list_pull_to_refresh" } + data object OrdersListNextPageLoaded: Event() { + override val name: String = "orders_list_next_page_loaded" + } + data class OrdersListFetched(val milimetersSinceRequestSent: Long): Event() { override val name: String = "orders_list_fetched" From 464a70d969e29b7a56087312f166b44f572a9d6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81sar=20Vargas=20Casaseca?= Date: Thu, 30 Oct 2025 12:39:31 +0100 Subject: [PATCH 05/21] Track tap on the orders list row --- .../ui/woopos/orders/WooPosOrdersViewModel.kt | 41 +++++++++++++++++++ .../util/analytics/WooPosAnalyticsEvent.kt | 21 ++++++++++ 2 files changed, 62 insertions(+) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt index ffdb31f42a84..86d0d8472621 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt @@ -33,6 +33,7 @@ import java.util.Locale import javax.inject.Inject import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListPullToRefreshTriggered import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListNextPageLoaded +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListRowTapped @HiltViewModel class WooPosOrdersViewModel @Inject constructor( @@ -50,6 +51,11 @@ class WooPosOrdersViewModel @Inject constructor( WooPosOrdersState.Loading(searchInputState = WooPosSearchInputState.Closed) ) val state: StateFlow = _state.asStateFlow() + private data class OrderTrackingMeta( + val createdAtMillis: Long, + val statusSlug: String + ) + private val trackingMeta = mutableMapOf() private val _openUrlEvent = MutableSharedFlow() val openUrlEvent: SharedFlow = _openUrlEvent.asSharedFlow() @@ -77,6 +83,11 @@ class WooPosOrdersViewModel @Inject constructor( val current = _state.value as? WooPosOrdersState.Content ?: return val loadedItems = current.items as? WooPosOrdersState.Content.Items.Loaded ?: return + val keys = loadedItems.items.keys.toList() + val position = keys.indexOfFirst { it.id == orderId }.coerceAtLeast(0) + + trackOrdersListRowTapped(orderId, position) + val updatedItems = loadedItems.items.mapKeys { (item, _) -> item.copy(isSelected = item.id == orderId) } @@ -91,6 +102,25 @@ class WooPosOrdersViewModel @Inject constructor( ) } + private fun trackOrdersListRowTapped(orderId: Long, position: Int) { + val meta = trackingMeta[orderId] ?: return + + val daysSince = ((System.currentTimeMillis() - meta.createdAtMillis) / 86_400_000L) + .toInt() + .coerceAtLeast(0) + + viewModelScope.launch { + analyticsTracker.track( + OrdersListRowTapped( + orderId = orderId, + orderStatus = meta.statusSlug, + listPosition = position, + daysSinceCreated = daysSince + ) + ) + } + } + fun onRefresh() { viewModelScope.launch { analyticsTracker.track(OrdersListPullToRefreshTriggered) @@ -258,6 +288,8 @@ class WooPosOrdersViewModel @Inject constructor( val current = _state.value as? WooPosOrdersState.Content ?: return val loaded = current.items as? WooPosOrdersState.Content.Items.Loaded ?: return + cacheOrderTrackingMeta(updated) + val selectedId = loaded.items.keys.firstOrNull { it.isSelected }?.id val newItem = mapOrderItem(updated, selectedId) val newDetails = mapOrderDetails(updated) @@ -427,6 +459,8 @@ class WooPosOrdersViewModel @Inject constructor( private suspend fun mapOrderItem(order: Order, selectedId: Long?): OrderItemViewState { val statusText = order.status.localizedLabel(resourceProvider, locale) + cacheOrderTrackingMeta(order) + return OrderItemViewState( id = order.id, title = "#${order.number}", @@ -443,6 +477,13 @@ class WooPosOrdersViewModel @Inject constructor( ) } + private fun cacheOrderTrackingMeta(order: Order) { + trackingMeta[order.id] = OrderTrackingMeta( + createdAtMillis = order.dateCreated.time, + statusSlug = order.status.toString() + ) + } + private suspend fun mapOrderDetails(order: Order): OrderDetailsViewState { val statusText = order.status.localizedLabel(resourceProvider, locale) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt index 59540ce1faf6..7f29d64632d8 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt @@ -10,6 +10,7 @@ import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEventCons import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEventConstant.ItemsListProductType import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEventConstant.ItemsListSource import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEventConstant.ItemsListSourceType +import java.util.Date import kotlin.reflect.KClass sealed class WooPosAnalyticsEvent : IAnalyticsEvent { @@ -138,6 +139,26 @@ sealed class WooPosAnalyticsEvent : IAnalyticsEvent { override val name: String = "orders_list_next_page_loaded" } + data class OrdersListRowTapped( + val orderId: Long, + val orderStatus: String, + val listPosition: Int, + val daysSinceCreated: Int + ) : Event() { + override val name: String = "orders_list_row_tapped" + + init { + addProperties( + mapOf( + "order_id" to orderId.toString(), + "order_status" to orderStatus, + "list_position" to listPosition.toString(), + "days_since_created" to daysSinceCreated.toString() + ) + ) + } + } + data class OrdersListFetched(val milimetersSinceRequestSent: Long): Event() { override val name: String = "orders_list_fetched" From 5fffc0e8e7e2d951e22e22495bb71bbc78513a77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81sar=20Vargas=20Casaseca?= Date: Thu, 30 Oct 2025 12:56:58 +0100 Subject: [PATCH 06/21] Track search button tapped --- .../android/ui/woopos/orders/WooPosOrdersViewModel.kt | 5 +++++ .../android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt index 86d0d8472621..fb7d8aba5725 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt @@ -34,6 +34,7 @@ import javax.inject.Inject import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListPullToRefreshTriggered import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListNextPageLoaded import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListRowTapped +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListSearchButtonTapped @HiltViewModel class WooPosOrdersViewModel @Inject constructor( @@ -221,6 +222,10 @@ class WooPosOrdersViewModel @Inject constructor( fun onSearchEvent(event: WooPosSearchUIEvent) { when (event) { is WooPosSearchUIEvent.SearchIconClicked -> { + viewModelScope.launch { + analyticsTracker.track(OrdersListSearchButtonTapped) + } + updateSearchState( WooPosSearchInputState.Open( input = WooPosSearchInputState.Open.Input.Hint( diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt index 7f29d64632d8..84af04301102 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt @@ -171,6 +171,10 @@ sealed class WooPosAnalyticsEvent : IAnalyticsEvent { } } + data object OrdersListSearchButtonTapped: Event() { + override val name: String = "pos_orders_list_search_button_tapped" + } + data class BarcodeScanned( val scanDurationMs: Long, val barcodeLength: Int, From e6a0a96e9c714780ce7b5a3d2aba3781d433d769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81sar=20Vargas=20Casaseca?= Date: Thu, 30 Oct 2025 13:13:23 +0100 Subject: [PATCH 07/21] Track order list search results fetched --- .../ui/woopos/orders/WooPosOrdersDataSource.kt | 7 ++++++- .../ui/woopos/util/analytics/WooPosAnalyticsEvent.kt | 12 ++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSource.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSource.kt index aa4723141d89..9810b53247ac 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSource.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSource.kt @@ -4,8 +4,8 @@ import android.os.SystemClock import com.woocommerce.android.model.Order import com.woocommerce.android.model.OrderMapper import com.woocommerce.android.tools.SelectedSite -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListFetched +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListSearchResultsFetched import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsTracker import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow @@ -66,7 +66,12 @@ class WooPosOrdersDataSource @Inject constructor( } suspend fun searchOrders(searchQuery: String): SearchOrdersResult { + val startMs = SystemClock.elapsedRealtime() val result = loadFirstPage(searchQuery) + val elapsedMs = SystemClock.elapsedRealtime() - startMs + + analyticsTracker.track(OrdersListSearchResultsFetched(elapsedMs)) + return result.fold( onSuccess = { SearchOrdersResult.Success(it) }, onFailure = { SearchOrdersResult.Error(it.message ?: UNKNOWN_ERROR) } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt index 84af04301102..3a851d5b28c3 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt @@ -171,6 +171,18 @@ sealed class WooPosAnalyticsEvent : IAnalyticsEvent { } } + data class OrdersListSearchResultsFetched(val milimetersSinceRequestSent: Long): Event() { + override val name: String = "pos_orders_list_search_results_fetched" + + init { + addProperties( + mapOf( + "milliseconds_since_request_sent" to milimetersSinceRequestSent.toString() + ) + ) + } + } + data object OrdersListSearchButtonTapped: Event() { override val name: String = "pos_orders_list_search_button_tapped" } From 4406f3873eaf3b282a1f5b8eda2082958ca89bee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81sar=20Vargas=20Casaseca?= Date: Thu, 30 Oct 2025 14:33:04 +0100 Subject: [PATCH 08/21] Track order details loaded --- .../ui/woopos/orders/WooPosOrdersDetails.kt | 11 ++++-- .../ui/woopos/orders/WooPosOrdersScreen.kt | 16 ++++++--- .../ui/woopos/orders/WooPosOrdersViewModel.kt | 34 +++++++++++++++---- .../util/analytics/WooPosAnalyticsEvent.kt | 18 ++++++++++ 4 files changed, 67 insertions(+), 12 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDetails.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDetails.kt index fff78d5da0a1..7bedd3c1fa4c 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDetails.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDetails.kt @@ -18,6 +18,7 @@ import androidx.compose.material.icons.outlined.Inventory2 import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -46,8 +47,13 @@ import com.woocommerce.android.ui.woopos.common.composeui.designsystem.WooPosTyp fun WooPosOrderDetails( modifier: Modifier = Modifier, details: OrderDetailsViewState, - onEmailReceiptButtonClicked: (Long) -> Unit + onEmailReceiptButtonClicked: (Long) -> Unit, + onShown: (Long) -> Unit ) { + LaunchedEffect(details.id) { + onShown(details.id) + } + Column( modifier = modifier .fillMaxSize() @@ -376,7 +382,8 @@ fun WooPosOrderDetailsPreview() { WooPosTheme { WooPosOrderDetails( details = orderDetails, - onEmailReceiptButtonClicked = {} + onEmailReceiptButtonClicked = {}, + onShown = {} ) } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersScreen.kt index a89403391125..484043dd4a8c 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersScreen.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersScreen.kt @@ -109,6 +109,7 @@ fun WooPosOrdersScreen( onSearchErrorRetry = viewModel::onSearchErrorRetry, onOrdersEmptyActionClicked = viewModel::onOrdersEmptyActionClicked, onOrdersLoadingErrorRetryButtonClicked = viewModel::onOrdersLoadingErrorRetryButtonClicked, + onDetailsShown = viewModel::onOrdersDetailsShown, onEmailReceiptButtonClicked = viewModel::onEmailReceiptButtonClicked ) } @@ -126,6 +127,7 @@ private fun WooPosOrdersScreen( onSearchErrorRetry: () -> Unit, onOrdersEmptyActionClicked: () -> Unit, onOrdersLoadingErrorRetryButtonClicked: () -> Unit, + onDetailsShown: (Long) -> Unit, onEmailReceiptButtonClicked: (Long) -> Unit, ) { BackHandler { onBackClicked() } @@ -140,6 +142,7 @@ private fun WooPosOrdersScreen( onPaginationErrorTryAgain = onPaginationErrorTryAgain, onSearchEvent = onSearchEvent, onSearchErrorRetry = onSearchErrorRetry, + onDetailsShown = onDetailsShown, onEmailReceiptButtonClicked = onEmailReceiptButtonClicked ) @@ -173,6 +176,7 @@ private fun OrdersContent( onPaginationErrorTryAgain: () -> Unit, onSearchEvent: (WooPosSearchUIEvent) -> Unit, onSearchErrorRetry: () -> Unit, + onDetailsShown: (Long) -> Unit, onEmailReceiptButtonClicked: (Long) -> Unit ) { Row(modifier = Modifier.fillMaxSize()) { @@ -200,7 +204,8 @@ private fun OrdersContent( modifier = Modifier .fillMaxHeight(), details = state.selectedDetails, - onEmailReceiptButtonClicked = onEmailReceiptButtonClicked + onEmailReceiptButtonClicked = onEmailReceiptButtonClicked, + onShown = onDetailsShown ) } } @@ -556,7 +561,8 @@ fun WooPosOrdersScreenPreview() { onSearchErrorRetry = {}, onOrdersEmptyActionClicked = {}, onOrdersLoadingErrorRetryButtonClicked = {}, - onEmailReceiptButtonClicked = {} + onEmailReceiptButtonClicked = {}, + onDetailsShown = {} ) } } @@ -589,7 +595,8 @@ fun WooPosOrdersSearchErrorStatePreview() { onSearchErrorRetry = {}, onOrdersEmptyActionClicked = {}, onOrdersLoadingErrorRetryButtonClicked = {}, - onEmailReceiptButtonClicked = {} + onEmailReceiptButtonClicked = {}, + onDetailsShown = {} ) } } @@ -622,7 +629,8 @@ fun WooPosOrdersNothingFoundStatePreview() { onSearchErrorRetry = {}, onOrdersEmptyActionClicked = {}, onOrdersLoadingErrorRetryButtonClicked = {}, - onEmailReceiptButtonClicked = {} + onEmailReceiptButtonClicked = {}, + onDetailsShown = {} ) } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt index fb7d8aba5725..cdc0e43e97dc 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt @@ -13,7 +13,6 @@ import com.woocommerce.android.ui.woopos.home.ChildToParentEvent.NavigationEvent import com.woocommerce.android.ui.woopos.home.WooPosChildrenToParentEventSender import com.woocommerce.android.ui.woopos.home.items.WooPosPaginationState import com.woocommerce.android.ui.woopos.home.items.WooPosPullToRefreshState -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsTracker import com.woocommerce.android.ui.woopos.util.ext.formatToMMMddYYYYAtHHmm import com.woocommerce.android.ui.woopos.util.format.WooPosFormatPrice @@ -35,6 +34,8 @@ import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Eve import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListNextPageLoaded import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListRowTapped import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListSearchButtonTapped +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrderDetailsLoaded +import org.apache.commons.lang3.time.DateUtils.MILLIS_PER_DAY @HiltViewModel class WooPosOrdersViewModel @Inject constructor( @@ -103,20 +104,41 @@ class WooPosOrdersViewModel @Inject constructor( ) } - private fun trackOrdersListRowTapped(orderId: Long, position: Int) { - val meta = trackingMeta[orderId] ?: return + fun onOrdersDetailsShown(orderId: Long) { + trackOrderDetailsShown(orderId) + } - val daysSince = ((System.currentTimeMillis() - meta.createdAtMillis) / 86_400_000L) + private inline fun retrieveOrderTrackingData( + orderId: Long, + crossinline block: (meta: OrderTrackingMeta, daysSince: Int) -> Unit + ) { + val meta = trackingMeta[orderId] ?: return + val daysSince = (((System.currentTimeMillis() - meta.createdAtMillis) / MILLIS_PER_DAY) .toInt() - .coerceAtLeast(0) + .coerceAtLeast(0)) + viewModelScope.launch { block(meta, daysSince) } + } + + private fun trackOrderDetailsShown(orderId: Long) = retrieveOrderTrackingData(orderId) { meta, days -> + viewModelScope.launch { + analyticsTracker.track( + OrderDetailsLoaded( + orderId = orderId, + orderStatus = meta.statusSlug, + daysSinceCreated = days + ) + ) + } + } + private fun trackOrdersListRowTapped(orderId: Long, position: Int) = retrieveOrderTrackingData(orderId) { meta, days -> viewModelScope.launch { analyticsTracker.track( OrdersListRowTapped( orderId = orderId, orderStatus = meta.statusSlug, listPosition = position, - daysSinceCreated = daysSince + daysSinceCreated = days ) ) } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt index 3a851d5b28c3..f6525d7179d7 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt @@ -159,6 +159,24 @@ sealed class WooPosAnalyticsEvent : IAnalyticsEvent { } } + data class OrderDetailsLoaded( + val orderId: Long, + val orderStatus: String, + val daysSinceCreated: Int + ) : Event() { + override val name: String = "pos_order_details_loaded" + + init { + addProperties( + mapOf( + "order_id" to orderId.toString(), + "order_status" to orderStatus, + "days_since_created" to daysSinceCreated.toString() + ) + ) + } + } + data class OrdersListFetched(val milimetersSinceRequestSent: Long): Event() { override val name: String = "orders_list_fetched" From 9c2836680b3266f978d55d3eeccbc60bd6530912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81sar=20Vargas=20Casaseca?= Date: Thu, 30 Oct 2025 14:38:41 +0100 Subject: [PATCH 09/21] Track email receipt button tap event --- .../android/ui/woopos/orders/WooPosOrdersViewModel.kt | 2 ++ .../android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt index cdc0e43e97dc..0f3b2e9cdd4a 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt @@ -35,6 +35,7 @@ import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Eve import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListRowTapped import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListSearchButtonTapped import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrderDetailsLoaded +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrderDetailsEmailReceiptTapped import org.apache.commons.lang3.time.DateUtils.MILLIS_PER_DAY @HiltViewModel @@ -191,6 +192,7 @@ class WooPosOrdersViewModel @Inject constructor( fun onEmailReceiptButtonClicked(orderId: Long) { viewModelScope.launch { + analyticsTracker.track(OrderDetailsEmailReceiptTapped) childrenToParentEventSender.sendToParent( ToEmailReceipt(orderId) ) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt index f6525d7179d7..5f96f57e4939 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt @@ -139,6 +139,10 @@ sealed class WooPosAnalyticsEvent : IAnalyticsEvent { override val name: String = "orders_list_next_page_loaded" } + data object OrderDetailsEmailReceiptTapped: Event() { + override val name: String = "order_details_email_receipt_tapped" + } + data class OrdersListRowTapped( val orderId: Long, val orderStatus: String, From 92a5ea3df8b7bb0d2b9a47a34f8952db75fecad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81sar=20Vargas=20Casaseca?= Date: Thu, 30 Oct 2025 15:11:08 +0100 Subject: [PATCH 10/21] Fix and add new tests --- .../orders/WooPosOrdersDataSourceTest.kt | 6 +- .../orders/WooPosOrdersViewModelTest.kt | 60 ++++++++++++++++++- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSourceTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSourceTest.kt index 591c26a757bd..7d268754db8c 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSourceTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSourceTest.kt @@ -4,11 +4,13 @@ import com.woocommerce.android.model.OrderMapper import com.woocommerce.android.tools.SelectedSite import com.woocommerce.android.ui.orders.OrderTestUtils import com.woocommerce.android.ui.woopos.util.WooPosCoroutineTestRule +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsTracker import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.toList import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat import org.junit.Rule +import org.mockito.Mockito import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.argumentCaptor @@ -37,12 +39,14 @@ class WooPosOrdersDataSourceTest { private val selectedSite: SelectedSite = mock { on { get() }.thenReturn(siteModel) } private val orderMapper: OrderMapper = mock() private val ordersCache: WooPosOrdersInMemoryCache = mock() + private val analyticsTracker: WooPosAnalyticsTracker = Mockito.mock() private val sut = WooPosOrdersDataSource( restClient = orderRestClient, selectedSite = selectedSite, orderMapper = orderMapper, - ordersCache = ordersCache + ordersCache = ordersCache, + analyticsTracker = analyticsTracker ) @Test diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModelTest.kt index 0c3f9820f0fe..277b426939ef 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModelTest.kt @@ -13,6 +13,8 @@ import com.woocommerce.android.ui.woopos.home.WooPosChildrenToParentEventSender import com.woocommerce.android.ui.woopos.home.items.WooPosPaginationState import com.woocommerce.android.ui.woopos.home.items.WooPosPullToRefreshState import com.woocommerce.android.ui.woopos.util.WooPosCoroutineTestRule +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.BackToCartTapped +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsTracker import com.woocommerce.android.ui.woopos.util.format.WooPosFormatPrice import com.woocommerce.android.viewmodel.ResourceProvider import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -32,6 +34,13 @@ import org.mockito.kotlin.whenever import java.math.BigDecimal import java.util.Date import java.util.Locale +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListPullToRefreshTriggered +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListNextPageLoaded +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListRowTapped +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListSearchButtonTapped +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrderDetailsLoaded +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrderDetailsEmailReceiptTapped +import org.mockito.kotlin.argThat @OptIn(ExperimentalCoroutinesApi::class) class WooPosOrdersViewModelTest { @@ -50,6 +59,7 @@ class WooPosOrdersViewModelTest { private val getOrderRefunds: WooPosGetOrderRefundsByOrderId = mock() private val providedLocale: Locale = Locale.US private val childrenToParentEventSender: WooPosChildrenToParentEventSender = mock() + private val analyticsTracker: WooPosAnalyticsTracker = mock() private fun createViewModel(): WooPosOrdersViewModel { return WooPosOrdersViewModel( @@ -59,7 +69,8 @@ class WooPosOrdersViewModelTest { getProductById = getProductById, childrenToParentEventSender = childrenToParentEventSender, formatPrice = formatPrice, - getOrderRefunds = getOrderRefunds + getOrderRefunds = getOrderRefunds, + analyticsTracker = analyticsTracker ) } @@ -284,6 +295,9 @@ class WooPosOrdersViewModelTest { val hint = openState.input as WooPosSearchInputState.Open.Input.Hint assertThat(hint.hint).isEqualTo("Search orders") assertThat(openState.requestFocus).isTrue() + verify(analyticsTracker).track(argThat { + this is OrdersListSearchButtonTapped + }) } @Test @@ -375,6 +389,9 @@ class WooPosOrdersViewModelTest { val loadedItems = content.items as WooPosOrdersState.Content.Items.Loaded assertThat(loadedItems.items.keys.map { it.id }).containsExactly(1L, 2L, 3L, 4L) assertThat(content.paginationState).isEqualTo(WooPosPaginationState.None) + verify(analyticsTracker).track(argThat { + this is OrdersListNextPageLoaded + }) } @Test @@ -615,6 +632,9 @@ class WooPosOrdersViewModelTest { val loadedItems = content.items as WooPosOrdersState.Content.Items.Loaded assertThat(loadedItems.items.keys.map { it.id }).containsExactly(300L, 400L) assertThat(content.selectedDetails.id).isEqualTo(300L) + verify(analyticsTracker).track(argThat { + this is OrdersListPullToRefreshTriggered + }) } @Test @@ -632,6 +652,9 @@ class WooPosOrdersViewModelTest { // THEN verify(childrenToParentEventSender).sendToParent(ToEmailReceipt(123L)) + verify(analyticsTracker).track(argThat { + this is OrderDetailsEmailReceiptTapped + }) } @Test @@ -750,4 +773,39 @@ class WooPosOrdersViewModelTest { assertThat(after.selectedDetails).isEqualTo(beforeDetails) verify(dataSource).refreshOrderById(1L) } + + @Test + fun `given orders loaded, when selecting an order, then tracks OrdersListRowTapped`() = runTest { + whenever(dataSource.loadOrders()).thenReturn( + flow { emit(LoadOrdersResult.SuccessRemote(listOf(order(1), order(2), order(3)))) } + ) + viewModel = createViewModel() + advanceUntilIdle() + + viewModel.onOrderSelected(3L) + advanceUntilIdle() + + verify(analyticsTracker).track(argThat { + this is OrdersListRowTapped + }) + } + + @Test + fun `given selected order, when details shown, then tracks OrderDetailsLoaded`() = runTest { + whenever(dataSource.loadOrders()).thenReturn( + flow { emit(LoadOrdersResult.SuccessRemote(listOf(order(10), order(20)))) } + ) + viewModel = createViewModel() + advanceUntilIdle() + + viewModel.onOrderSelected(20L) + advanceUntilIdle() + + viewModel.onOrdersDetailsShown(20L) + advanceUntilIdle() + + verify(analyticsTracker).track(argThat { + this is com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrderDetailsLoaded + }) + } } From b482de69123eb47ff824ace9fe8bec964c4292c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81sar=20Vargas=20Casaseca?= Date: Thu, 30 Oct 2025 15:25:57 +0100 Subject: [PATCH 11/21] Move all events to view model --- .../woopos/orders/WooPosOrdersDataSource.kt | 32 ++----------------- .../ui/woopos/orders/WooPosOrdersViewModel.kt | 23 +++++++++++++ 2 files changed, 25 insertions(+), 30 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSource.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSource.kt index 9810b53247ac..ea3f05665b6a 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSource.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSource.kt @@ -1,12 +1,8 @@ package com.woocommerce.android.ui.woopos.orders -import android.os.SystemClock import com.woocommerce.android.model.Order import com.woocommerce.android.model.OrderMapper import com.woocommerce.android.tools.SelectedSite -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListFetched -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListSearchResultsFetched -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsTracker import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow @@ -34,8 +30,7 @@ class WooPosOrdersDataSource @Inject constructor( private val restClient: OrderRestClient, private val selectedSite: SelectedSite, private val orderMapper: OrderMapper, - private val ordersCache: WooPosOrdersInMemoryCache, - private val analyticsTracker: WooPosAnalyticsTracker + private val ordersCache: WooPosOrdersInMemoryCache ) { private val canLoadMore = AtomicBoolean(false) private val page = AtomicInteger(1) @@ -52,12 +47,8 @@ class WooPosOrdersDataSource @Inject constructor( val cached = ordersCache.getAll() if (cached.isNotEmpty()) emit(LoadOrdersResult.SuccessCache(cached)) - val startMs = SystemClock.elapsedRealtime() val result = loadFirstPage() - val elapsedMs = SystemClock.elapsedRealtime() - startMs - result.onSuccess { - analyticsTracker.track(OrdersListFetched(elapsedMs)) ordersCache.setAll(it) emit(LoadOrdersResult.SuccessRemote(it)) }.onFailure { @@ -66,12 +57,7 @@ class WooPosOrdersDataSource @Inject constructor( } suspend fun searchOrders(searchQuery: String): SearchOrdersResult { - val startMs = SystemClock.elapsedRealtime() val result = loadFirstPage(searchQuery) - val elapsedMs = SystemClock.elapsedRealtime() - startMs - - analyticsTracker.track(OrdersListSearchResultsFetched(elapsedMs)) - return result.fold( onSuccess = { SearchOrdersResult.Success(it) }, onFailure = { SearchOrdersResult.Error(it.message ?: UNKNOWN_ERROR) } @@ -86,26 +72,12 @@ class WooPosOrdersDataSource @Inject constructor( val payload = restClient.fetchSingleOrder(site, orderId) return if (payload.error == null) { val entity = payload.orderWithMeta.first - val order = orderMapper.toAppModel(entity) - - updateCachedOrderIfPresent(order) - - Result.success(order) + Result.success(orderMapper.toAppModel(entity)) } else { Result.failure(Throwable("[${payload.error.type}] ${payload.error.message}")) } } - private fun updateCachedOrderIfPresent(order: Order) { - val current = ordersCache.getAll() - val idx = current.indexOfFirst { it.id == order.id } - if (idx < 0) return - - val updated = current.toMutableList() - updated[idx] = order - ordersCache.setAll(updated) - } - private suspend fun loadFirstPage(searchQuery: String? = null): Result> { page.set(1) canLoadMore.set(false) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt index 0f3b2e9cdd4a..e251a50b07a6 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt @@ -1,5 +1,6 @@ package com.woocommerce.android.ui.woopos.orders +import android.os.SystemClock import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.woocommerce.android.AppUrls @@ -36,6 +37,8 @@ import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Eve import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListSearchButtonTapped import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrderDetailsLoaded import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrderDetailsEmailReceiptTapped +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListFetched +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListSearchResultsFetched import org.apache.commons.lang3.time.DateUtils.MILLIS_PER_DAY @HiltViewModel @@ -67,6 +70,9 @@ class WooPosOrdersViewModel @Inject constructor( private var loadingJob: Job? = null private var loadingMoreOrdersJob: Job? = null + private var ordersLoadStartMs: Long? = null + private var searchStartMs: Long? = null + private val currentSearchQuery: String? get() = ( ( @@ -357,9 +363,12 @@ class WooPosOrdersViewModel @Inject constructor( paginationState = WooPosPaginationState.None ) } + + searchStartMs = SystemClock.elapsedRealtime() val result = ordersDataSource.searchOrders(query) when (result) { is SearchOrdersResult.Error -> { + searchStartMs = null _state.value = WooPosOrdersState.Content( items = WooPosOrdersState.Content.Items.Error( title = resourceProvider.getString(R.string.woopos_search_orders_error_title), @@ -373,6 +382,12 @@ class WooPosOrdersViewModel @Inject constructor( } is SearchOrdersResult.Success -> { + searchStartMs?.let { start -> + val elapsed = SystemClock.elapsedRealtime() - start + analyticsTracker.track(OrdersListSearchResultsFetched(elapsed)) + } + searchStartMs = null + if (result.orders.isEmpty()) { _state.value = WooPosOrdersState.Content( items = WooPosOrdersState.Content.Items.NothingFound( @@ -394,10 +409,12 @@ class WooPosOrdersViewModel @Inject constructor( private fun loadOrders() { cancelJobs() + ordersLoadStartMs = SystemClock.elapsedRealtime() loadingJob = viewModelScope.launch { ordersDataSource.loadOrders().collect { result -> when (result) { is LoadOrdersResult.Error -> { + ordersLoadStartMs = null _state.value = WooPosOrdersState.Error( message = result.message, searchInputState = WooPosSearchInputState.Closed @@ -415,6 +432,12 @@ class WooPosOrdersViewModel @Inject constructor( } is LoadOrdersResult.SuccessRemote -> { + ordersLoadStartMs?.let { start -> + val elapsed = SystemClock.elapsedRealtime() - start + analyticsTracker.track(OrdersListFetched(elapsed)) + } + ordersLoadStartMs = null + if (result.orders.isEmpty()) { _state.value = WooPosOrdersState.Empty( searchInputState = WooPosSearchInputState.Closed From 0e993cac9606b4d88e6ed26054a4b2ddc63c89ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81sar=20Vargas=20Casaseca?= Date: Thu, 30 Oct 2025 15:46:10 +0100 Subject: [PATCH 12/21] Remove analytics tracker --- .../android/ui/woopos/orders/WooPosOrdersDataSourceTest.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSourceTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSourceTest.kt index 7d268754db8c..c521b789c631 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSourceTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSourceTest.kt @@ -39,14 +39,12 @@ class WooPosOrdersDataSourceTest { private val selectedSite: SelectedSite = mock { on { get() }.thenReturn(siteModel) } private val orderMapper: OrderMapper = mock() private val ordersCache: WooPosOrdersInMemoryCache = mock() - private val analyticsTracker: WooPosAnalyticsTracker = Mockito.mock() private val sut = WooPosOrdersDataSource( restClient = orderRestClient, selectedSite = selectedSite, orderMapper = orderMapper, - ordersCache = ordersCache, - analyticsTracker = analyticsTracker + ordersCache = ordersCache ) @Test From 5324ffec3a3c81e55752fa4b0539c16f66aeb74d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81sar=20Vargas=20Casaseca?= Date: Thu, 30 Oct 2025 15:46:32 +0100 Subject: [PATCH 13/21] Add tests for new events in view model --- .../orders/WooPosOrdersViewModelTest.kt | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModelTest.kt index 277b426939ef..520598ce04d3 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModelTest.kt @@ -38,8 +38,10 @@ import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Eve import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListNextPageLoaded import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListRowTapped import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListSearchButtonTapped -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrderDetailsLoaded import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrderDetailsEmailReceiptTapped +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListFetched +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListSearchResultsFetched + import org.mockito.kotlin.argThat @OptIn(ExperimentalCoroutinesApi::class) @@ -808,4 +810,43 @@ class WooPosOrdersViewModelTest { this is com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrderDetailsLoaded }) } + + @Test + fun `when remote load succeeds, then tracks OrdersListFetched`() = runTest { + val cached = listOf(order(1)) + val remote = listOf(order(2), order(3)) + whenever(dataSource.loadOrders()).thenReturn( + flow { + emit(LoadOrdersResult.SuccessCache(cached)) + emit(LoadOrdersResult.SuccessRemote(remote)) + } + ) + + viewModel = createViewModel() + advanceUntilIdle() + + verify(analyticsTracker).track( + argThat { this is OrdersListFetched } + ) + } + + @Test + fun `when search succeeds, then tracks OrdersListSearchResultsFetched`() = runTest { + val query = "abc" + val searchResult = listOf(order(10), order(20)) + whenever(dataSource.loadOrders()).thenReturn( + flow { emit(LoadOrdersResult.SuccessRemote(listOf(order(1)))) } + ) + whenever(dataSource.searchOrders(query)).thenReturn(SearchOrdersResult.Success(searchResult)) + + viewModel = createViewModel() + advanceUntilIdle() + + viewModel.onSearchEvent(WooPosSearchUIEvent.Search(query, query.length)) + advanceUntilIdle() + + verify(analyticsTracker).track( + argThat { this is OrdersListSearchResultsFetched } + ) + } } From 5eddcaeb9913cea1caa6c5deb781dee371120fa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81sar=20Vargas=20Casaseca?= Date: Thu, 30 Oct 2025 15:49:37 +0100 Subject: [PATCH 14/21] Use kotlin time instead of android function --- .../ui/woopos/orders/WooPosOrdersViewModel.kt | 25 ++++++------------- 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt index e251a50b07a6..022f1f318364 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt @@ -1,6 +1,6 @@ package com.woocommerce.android.ui.woopos.orders -import android.os.SystemClock +import kotlin.time.TimeSource.Monotonic import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.woocommerce.android.AppUrls @@ -70,9 +70,6 @@ class WooPosOrdersViewModel @Inject constructor( private var loadingJob: Job? = null private var loadingMoreOrdersJob: Job? = null - private var ordersLoadStartMs: Long? = null - private var searchStartMs: Long? = null - private val currentSearchQuery: String? get() = ( ( @@ -364,11 +361,10 @@ class WooPosOrdersViewModel @Inject constructor( ) } - searchStartMs = SystemClock.elapsedRealtime() + val mark = Monotonic.markNow() val result = ordersDataSource.searchOrders(query) when (result) { is SearchOrdersResult.Error -> { - searchStartMs = null _state.value = WooPosOrdersState.Content( items = WooPosOrdersState.Content.Items.Error( title = resourceProvider.getString(R.string.woopos_search_orders_error_title), @@ -382,11 +378,8 @@ class WooPosOrdersViewModel @Inject constructor( } is SearchOrdersResult.Success -> { - searchStartMs?.let { start -> - val elapsed = SystemClock.elapsedRealtime() - start - analyticsTracker.track(OrdersListSearchResultsFetched(elapsed)) - } - searchStartMs = null + val elapsedMs = mark.elapsedNow().inWholeMilliseconds + analyticsTracker.track(OrdersListSearchResultsFetched(elapsedMs)) if (result.orders.isEmpty()) { _state.value = WooPosOrdersState.Content( @@ -409,12 +402,11 @@ class WooPosOrdersViewModel @Inject constructor( private fun loadOrders() { cancelJobs() - ordersLoadStartMs = SystemClock.elapsedRealtime() + val mark = Monotonic.markNow() loadingJob = viewModelScope.launch { ordersDataSource.loadOrders().collect { result -> when (result) { is LoadOrdersResult.Error -> { - ordersLoadStartMs = null _state.value = WooPosOrdersState.Error( message = result.message, searchInputState = WooPosSearchInputState.Closed @@ -432,11 +424,8 @@ class WooPosOrdersViewModel @Inject constructor( } is LoadOrdersResult.SuccessRemote -> { - ordersLoadStartMs?.let { start -> - val elapsed = SystemClock.elapsedRealtime() - start - analyticsTracker.track(OrdersListFetched(elapsed)) - } - ordersLoadStartMs = null + val elapsedMs = mark.elapsedNow().inWholeMilliseconds + analyticsTracker.track(OrdersListFetched(elapsedMs)) if (result.orders.isEmpty()) { _state.value = WooPosOrdersState.Empty( From ca2e91764d7994cb3cbe40dfd2e63fd257422b24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81sar=20Vargas=20Casaseca?= Date: Thu, 30 Oct 2025 15:52:16 +0100 Subject: [PATCH 15/21] Fix detekt issues --- .../WooPosHomeFloatingToolbarViewModel.kt | 1 - .../ui/woopos/orders/WooPosOrdersViewModel.kt | 32 +++++---- .../util/analytics/WooPosAnalyticsEvent.kt | 15 ++--- .../orders/WooPosOrdersDataSourceTest.kt | 2 - .../orders/WooPosOrdersViewModelTest.kt | 66 +++++++++++-------- 5 files changed, 63 insertions(+), 53 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbarViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbarViewModel.kt index 274fd315c301..43cd62588f75 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbarViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbarViewModel.kt @@ -21,7 +21,6 @@ import com.woocommerce.android.ui.woopos.home.toolbar.WooPosHomeFloatingToolbarU import com.woocommerce.android.ui.woopos.home.toolbar.WooPosHomeFloatingToolbarUIEvent.OnToolbarMenuClicked import com.woocommerce.android.ui.woopos.util.WooPosNetworkStatus import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.GoToOrdersTapped -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.ExitTapped import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsTracker import com.woocommerce.android.viewmodel.ResourceProvider import dagger.hilt.android.lifecycle.HiltViewModel diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt index 022f1f318364..501358d97224 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt @@ -1,6 +1,5 @@ package com.woocommerce.android.ui.woopos.orders -import kotlin.time.TimeSource.Monotonic import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.woocommerce.android.AppUrls @@ -14,6 +13,14 @@ import com.woocommerce.android.ui.woopos.home.ChildToParentEvent.NavigationEvent import com.woocommerce.android.ui.woopos.home.WooPosChildrenToParentEventSender import com.woocommerce.android.ui.woopos.home.items.WooPosPaginationState import com.woocommerce.android.ui.woopos.home.items.WooPosPullToRefreshState +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrderDetailsEmailReceiptTapped +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrderDetailsLoaded +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListFetched +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListNextPageLoaded +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListPullToRefreshTriggered +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListRowTapped +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListSearchButtonTapped +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListSearchResultsFetched import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsTracker import com.woocommerce.android.ui.woopos.util.ext.formatToMMMddYYYYAtHHmm import com.woocommerce.android.ui.woopos.util.format.WooPosFormatPrice @@ -28,18 +35,11 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch +import org.apache.commons.lang3.time.DateUtils.MILLIS_PER_DAY import java.math.BigDecimal import java.util.Locale import javax.inject.Inject -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListPullToRefreshTriggered -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListNextPageLoaded -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListRowTapped -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListSearchButtonTapped -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrderDetailsLoaded -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrderDetailsEmailReceiptTapped -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListFetched -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListSearchResultsFetched -import org.apache.commons.lang3.time.DateUtils.MILLIS_PER_DAY +import kotlin.time.TimeSource.Monotonic @HiltViewModel class WooPosOrdersViewModel @Inject constructor( @@ -117,9 +117,11 @@ class WooPosOrdersViewModel @Inject constructor( crossinline block: (meta: OrderTrackingMeta, daysSince: Int) -> Unit ) { val meta = trackingMeta[orderId] ?: return - val daysSince = (((System.currentTimeMillis() - meta.createdAtMillis) / MILLIS_PER_DAY) - .toInt() - .coerceAtLeast(0)) + val daysSince = ( + ((System.currentTimeMillis() - meta.createdAtMillis) / MILLIS_PER_DAY) + .toInt() + .coerceAtLeast(0) + ) viewModelScope.launch { block(meta, daysSince) } } @@ -135,7 +137,9 @@ class WooPosOrdersViewModel @Inject constructor( } } - private fun trackOrdersListRowTapped(orderId: Long, position: Int) = retrieveOrderTrackingData(orderId) { meta, days -> + private fun trackOrdersListRowTapped(orderId: Long, position: Int) = retrieveOrderTrackingData( + orderId + ) { meta, days -> viewModelScope.launch { analyticsTracker.track( OrdersListRowTapped( diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt index 5f96f57e4939..de069a8567fd 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/analytics/WooPosAnalyticsEvent.kt @@ -10,7 +10,6 @@ import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEventCons import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEventConstant.ItemsListProductType import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEventConstant.ItemsListSource import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEventConstant.ItemsListSourceType -import java.util.Date import kotlin.reflect.KClass sealed class WooPosAnalyticsEvent : IAnalyticsEvent { @@ -127,19 +126,19 @@ sealed class WooPosAnalyticsEvent : IAnalyticsEvent { data object InteractionWithCustomerStarted : Event() { override val name: String = "interaction_with_customer_started" } - data object GoToOrdersTapped: Event() { + data object GoToOrdersTapped : Event() { override val name: String = "orders_menu_item_tapped" } - data object OrdersListPullToRefreshTriggered: Event() { + data object OrdersListPullToRefreshTriggered : Event() { override val name: String = "orders_list_pull_to_refresh" } - data object OrdersListNextPageLoaded: Event() { + data object OrdersListNextPageLoaded : Event() { override val name: String = "orders_list_next_page_loaded" } - data object OrderDetailsEmailReceiptTapped: Event() { + data object OrderDetailsEmailReceiptTapped : Event() { override val name: String = "order_details_email_receipt_tapped" } @@ -181,7 +180,7 @@ sealed class WooPosAnalyticsEvent : IAnalyticsEvent { } } - data class OrdersListFetched(val milimetersSinceRequestSent: Long): Event() { + data class OrdersListFetched(val milimetersSinceRequestSent: Long) : Event() { override val name: String = "orders_list_fetched" init { @@ -193,7 +192,7 @@ sealed class WooPosAnalyticsEvent : IAnalyticsEvent { } } - data class OrdersListSearchResultsFetched(val milimetersSinceRequestSent: Long): Event() { + data class OrdersListSearchResultsFetched(val milimetersSinceRequestSent: Long) : Event() { override val name: String = "pos_orders_list_search_results_fetched" init { @@ -205,7 +204,7 @@ sealed class WooPosAnalyticsEvent : IAnalyticsEvent { } } - data object OrdersListSearchButtonTapped: Event() { + data object OrdersListSearchButtonTapped : Event() { override val name: String = "pos_orders_list_search_button_tapped" } diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSourceTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSourceTest.kt index c521b789c631..591c26a757bd 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSourceTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSourceTest.kt @@ -4,13 +4,11 @@ import com.woocommerce.android.model.OrderMapper import com.woocommerce.android.tools.SelectedSite import com.woocommerce.android.ui.orders.OrderTestUtils import com.woocommerce.android.ui.woopos.util.WooPosCoroutineTestRule -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsTracker import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.toList import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat import org.junit.Rule -import org.mockito.Mockito import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.argumentCaptor diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModelTest.kt index 520598ce04d3..60bf637036eb 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModelTest.kt @@ -13,7 +13,13 @@ import com.woocommerce.android.ui.woopos.home.WooPosChildrenToParentEventSender import com.woocommerce.android.ui.woopos.home.items.WooPosPaginationState import com.woocommerce.android.ui.woopos.home.items.WooPosPullToRefreshState import com.woocommerce.android.ui.woopos.util.WooPosCoroutineTestRule -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.BackToCartTapped +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrderDetailsEmailReceiptTapped +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListFetched +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListNextPageLoaded +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListPullToRefreshTriggered +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListRowTapped +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListSearchButtonTapped +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListSearchResultsFetched import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsTracker import com.woocommerce.android.ui.woopos.util.format.WooPosFormatPrice import com.woocommerce.android.viewmodel.ResourceProvider @@ -28,21 +34,13 @@ import org.junit.Rule import org.junit.Test import org.mockito.Mockito.mock import org.mockito.kotlin.any +import org.mockito.kotlin.argThat import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import java.math.BigDecimal import java.util.Date import java.util.Locale -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListPullToRefreshTriggered -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListNextPageLoaded -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListRowTapped -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListSearchButtonTapped -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrderDetailsEmailReceiptTapped -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListFetched -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListSearchResultsFetched - -import org.mockito.kotlin.argThat @OptIn(ExperimentalCoroutinesApi::class) class WooPosOrdersViewModelTest { @@ -297,9 +295,11 @@ class WooPosOrdersViewModelTest { val hint = openState.input as WooPosSearchInputState.Open.Input.Hint assertThat(hint.hint).isEqualTo("Search orders") assertThat(openState.requestFocus).isTrue() - verify(analyticsTracker).track(argThat { - this is OrdersListSearchButtonTapped - }) + verify(analyticsTracker).track( + argThat { + this is OrdersListSearchButtonTapped + } + ) } @Test @@ -391,9 +391,11 @@ class WooPosOrdersViewModelTest { val loadedItems = content.items as WooPosOrdersState.Content.Items.Loaded assertThat(loadedItems.items.keys.map { it.id }).containsExactly(1L, 2L, 3L, 4L) assertThat(content.paginationState).isEqualTo(WooPosPaginationState.None) - verify(analyticsTracker).track(argThat { - this is OrdersListNextPageLoaded - }) + verify(analyticsTracker).track( + argThat { + this is OrdersListNextPageLoaded + } + ) } @Test @@ -634,9 +636,11 @@ class WooPosOrdersViewModelTest { val loadedItems = content.items as WooPosOrdersState.Content.Items.Loaded assertThat(loadedItems.items.keys.map { it.id }).containsExactly(300L, 400L) assertThat(content.selectedDetails.id).isEqualTo(300L) - verify(analyticsTracker).track(argThat { - this is OrdersListPullToRefreshTriggered - }) + verify(analyticsTracker).track( + argThat { + this is OrdersListPullToRefreshTriggered + } + ) } @Test @@ -654,9 +658,11 @@ class WooPosOrdersViewModelTest { // THEN verify(childrenToParentEventSender).sendToParent(ToEmailReceipt(123L)) - verify(analyticsTracker).track(argThat { - this is OrderDetailsEmailReceiptTapped - }) + verify(analyticsTracker).track( + argThat { + this is OrderDetailsEmailReceiptTapped + } + ) } @Test @@ -787,9 +793,11 @@ class WooPosOrdersViewModelTest { viewModel.onOrderSelected(3L) advanceUntilIdle() - verify(analyticsTracker).track(argThat { - this is OrdersListRowTapped - }) + verify(analyticsTracker).track( + argThat { + this is OrdersListRowTapped + } + ) } @Test @@ -806,9 +814,11 @@ class WooPosOrdersViewModelTest { viewModel.onOrdersDetailsShown(20L) advanceUntilIdle() - verify(analyticsTracker).track(argThat { - this is com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrderDetailsLoaded - }) + verify(analyticsTracker).track( + argThat { + this is com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrderDetailsLoaded + } + ) } @Test From 389fe12d40818df8a60ad6f5224cee7fea714c47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81sar=20Vargas=20Casaseca?= Date: Thu, 30 Oct 2025 15:54:00 +0100 Subject: [PATCH 16/21] Bring accidentally removed event tracking back --- .../woopos/home/toolbar/WooPosHomeFloatingToolbarViewModel.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbarViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbarViewModel.kt index 43cd62588f75..50613882c0d0 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbarViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbarViewModel.kt @@ -20,6 +20,7 @@ import com.woocommerce.android.ui.woopos.home.toolbar.WooPosHomeFloatingToolbarU import com.woocommerce.android.ui.woopos.home.toolbar.WooPosHomeFloatingToolbarUIEvent.OnOutsideOfToolbarMenuClicked import com.woocommerce.android.ui.woopos.home.toolbar.WooPosHomeFloatingToolbarUIEvent.OnToolbarMenuClicked import com.woocommerce.android.ui.woopos.util.WooPosNetworkStatus +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.ExitTapped import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.GoToOrdersTapped import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsTracker import com.woocommerce.android.viewmodel.ResourceProvider @@ -99,6 +100,7 @@ class WooPosHomeFloatingToolbarViewModel @Inject constructor( R.string.woopos_exit_confirmation_title -> viewModelScope.launch { childrenToParentEventSender.sendToParent(ChildToParentEvent.ExitPosClicked) + analyticsTracker.track(ExitTapped) } } } From a2fefa9e185fdf603a5e94f416f7295e4f980a77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81sar=20Vargas=20Casaseca?= Date: Thu, 30 Oct 2025 15:57:35 +0100 Subject: [PATCH 17/21] Bring accidentally removed code back --- .../woopos/orders/WooPosOrdersDataSource.kt | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSource.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSource.kt index ea3f05665b6a..6f91d2113b6a 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSource.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSource.kt @@ -30,7 +30,7 @@ class WooPosOrdersDataSource @Inject constructor( private val restClient: OrderRestClient, private val selectedSite: SelectedSite, private val orderMapper: OrderMapper, - private val ordersCache: WooPosOrdersInMemoryCache + private val ordersCache: WooPosOrdersInMemoryCache, ) { private val canLoadMore = AtomicBoolean(false) private val page = AtomicInteger(1) @@ -48,6 +48,7 @@ class WooPosOrdersDataSource @Inject constructor( if (cached.isNotEmpty()) emit(LoadOrdersResult.SuccessCache(cached)) val result = loadFirstPage() + result.onSuccess { ordersCache.setAll(it) emit(LoadOrdersResult.SuccessRemote(it)) @@ -72,12 +73,26 @@ class WooPosOrdersDataSource @Inject constructor( val payload = restClient.fetchSingleOrder(site, orderId) return if (payload.error == null) { val entity = payload.orderWithMeta.first - Result.success(orderMapper.toAppModel(entity)) + val order = orderMapper.toAppModel(entity) + + updateCachedOrderIfPresent(order) + + Result.success(order) } else { Result.failure(Throwable("[${payload.error.type}] ${payload.error.message}")) } } + private fun updateCachedOrderIfPresent(order: Order) { + val current = ordersCache.getAll() + val idx = current.indexOfFirst { it.id == order.id } + if (idx < 0) return + + val updated = current.toMutableList() + updated[idx] = order + ordersCache.setAll(updated) + } + private suspend fun loadFirstPage(searchQuery: String? = null): Result> { page.set(1) canLoadMore.set(false) From 9a0cf09b3f7b1ab2b01be01e614ba485dd6d429a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81sar=20Vargas=20Casaseca?= Date: Thu, 30 Oct 2025 15:58:46 +0100 Subject: [PATCH 18/21] Revert previous changes --- .../android/ui/woopos/orders/WooPosOrdersDataSource.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSource.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSource.kt index 6f91d2113b6a..06267c69e44a 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSource.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSource.kt @@ -30,7 +30,7 @@ class WooPosOrdersDataSource @Inject constructor( private val restClient: OrderRestClient, private val selectedSite: SelectedSite, private val orderMapper: OrderMapper, - private val ordersCache: WooPosOrdersInMemoryCache, + private val ordersCache: WooPosOrdersInMemoryCache ) { private val canLoadMore = AtomicBoolean(false) private val page = AtomicInteger(1) @@ -48,7 +48,6 @@ class WooPosOrdersDataSource @Inject constructor( if (cached.isNotEmpty()) emit(LoadOrdersResult.SuccessCache(cached)) val result = loadFirstPage() - result.onSuccess { ordersCache.setAll(it) emit(LoadOrdersResult.SuccessRemote(it)) From a8dec0332f8d11580c0a7ed375a8a4b8c9ab151d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81sar=20Vargas=20Casaseca?= Date: Fri, 31 Oct 2025 07:56:58 +0100 Subject: [PATCH 19/21] Simplify and clean code --- .../orders/WooPosOrdersAnalyticsTracker.kt | 79 +++++++++++++ .../ui/woopos/orders/WooPosOrdersState.kt | 4 +- .../ui/woopos/orders/WooPosOrdersViewModel.kt | 105 +++++------------- 3 files changed, 108 insertions(+), 80 deletions(-) create mode 100644 WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersAnalyticsTracker.kt diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersAnalyticsTracker.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersAnalyticsTracker.kt new file mode 100644 index 000000000000..6baf0c60260c --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersAnalyticsTracker.kt @@ -0,0 +1,79 @@ +package com.woocommerce.android.ui.woopos.orders + +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrderDetailsEmailReceiptTapped +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrderDetailsLoaded +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListFetched +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListNextPageLoaded +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListPullToRefreshTriggered +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListRowTapped +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListSearchButtonTapped +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListSearchResultsFetched +import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsTracker +import org.apache.commons.lang3.time.DateUtils.MILLIS_PER_DAY +import javax.inject.Inject + +class WooPosOrdersAnalyticsTracker @Inject constructor( + private val analyticsTracker: WooPosAnalyticsTracker +) { + suspend fun trackOrdersListFetched(elapsedMs: Long) { + analyticsTracker.track(OrdersListFetched(elapsedMs)) + } + + suspend fun trackOrdersListRowTapped( + orderId: Long, + orderStatus: String, + listPosition: Int, + createdAtMillis: Long + ) { + val daysSinceCreated = calculateDaysSinceCreated(createdAtMillis) + analyticsTracker.track( + OrdersListRowTapped( + orderId = orderId, + orderStatus = orderStatus, + listPosition = listPosition, + daysSinceCreated = daysSinceCreated + ) + ) + } + + suspend fun trackOrderDetailsLoaded( + orderId: Long, + orderStatus: String, + createdAtMillis: Long + ) { + val daysSinceCreated = calculateDaysSinceCreated(createdAtMillis) + analyticsTracker.track( + OrderDetailsLoaded( + orderId = orderId, + orderStatus = orderStatus, + daysSinceCreated = daysSinceCreated + ) + ) + } + + suspend fun trackOrdersListPullToRefreshTriggered() { + analyticsTracker.track(OrdersListPullToRefreshTriggered) + } + + suspend fun trackOrderDetailsEmailReceiptTapped() { + analyticsTracker.track(OrderDetailsEmailReceiptTapped) + } + + suspend fun trackOrdersListNextPageLoaded() { + analyticsTracker.track(OrdersListNextPageLoaded) + } + + suspend fun trackOrdersListSearchButtonTapped() { + analyticsTracker.track(OrdersListSearchButtonTapped) + } + + suspend fun trackOrdersListSearchResultsFetched(elapsedMs: Long) { + analyticsTracker.track(OrdersListSearchResultsFetched(elapsedMs)) + } + + private fun calculateDaysSinceCreated(createdAtMillis: Long): Int { + return ((System.currentTimeMillis() - createdAtMillis) / MILLIS_PER_DAY) + .toInt() + .coerceAtLeast(0) + } +} diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersState.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersState.kt index a1d29237154f..c28acdc9ba57 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersState.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersState.kt @@ -49,7 +49,9 @@ data class OrderItemViewState( val total: String, val customerEmail: String?, val isSelected: Boolean, - val status: PosOrderStatus + val status: PosOrderStatus, + val statusSlug: String, + val createdAtMillis: Long ) @Immutable diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt index 501358d97224..0499173d0878 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt @@ -13,15 +13,6 @@ import com.woocommerce.android.ui.woopos.home.ChildToParentEvent.NavigationEvent import com.woocommerce.android.ui.woopos.home.WooPosChildrenToParentEventSender import com.woocommerce.android.ui.woopos.home.items.WooPosPaginationState import com.woocommerce.android.ui.woopos.home.items.WooPosPullToRefreshState -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrderDetailsEmailReceiptTapped -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrderDetailsLoaded -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListFetched -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListNextPageLoaded -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListPullToRefreshTriggered -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListRowTapped -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListSearchButtonTapped -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListSearchResultsFetched -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsTracker import com.woocommerce.android.ui.woopos.util.ext.formatToMMMddYYYYAtHHmm import com.woocommerce.android.ui.woopos.util.format.WooPosFormatPrice import com.woocommerce.android.viewmodel.ResourceProvider @@ -35,7 +26,6 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch -import org.apache.commons.lang3.time.DateUtils.MILLIS_PER_DAY import java.math.BigDecimal import java.util.Locale import javax.inject.Inject @@ -50,18 +40,13 @@ class WooPosOrdersViewModel @Inject constructor( private val childrenToParentEventSender: WooPosChildrenToParentEventSender, private val formatPrice: WooPosFormatPrice, private val getOrderRefunds: WooPosGetOrderRefundsByOrderId, - private val analyticsTracker: WooPosAnalyticsTracker + private val ordersAnalyticsTracker: WooPosOrdersAnalyticsTracker ) : ViewModel() { private val _state = MutableStateFlow( WooPosOrdersState.Loading(searchInputState = WooPosSearchInputState.Closed) ) val state: StateFlow = _state.asStateFlow() - private data class OrderTrackingMeta( - val createdAtMillis: Long, - val statusSlug: String - ) - private val trackingMeta = mutableMapOf() private val _openUrlEvent = MutableSharedFlow() val openUrlEvent: SharedFlow = _openUrlEvent.asSharedFlow() @@ -91,8 +76,23 @@ class WooPosOrdersViewModel @Inject constructor( val keys = loadedItems.items.keys.toList() val position = keys.indexOfFirst { it.id == orderId }.coerceAtLeast(0) + val selectedItem = keys.firstOrNull { it.id == orderId } - trackOrdersListRowTapped(orderId, position) + selectedItem?.let { + viewModelScope.launch { + ordersAnalyticsTracker.trackOrdersListRowTapped( + orderId = it.id, + orderStatus = it.statusSlug, + listPosition = position, + createdAtMillis = it.createdAtMillis + ) + ordersAnalyticsTracker.trackOrderDetailsLoaded( + orderId = it.id, + orderStatus = it.statusSlug, + createdAtMillis = it.createdAtMillis + ) + } + } val updatedItems = loadedItems.items.mapKeys { (item, _) -> item.copy(isSelected = item.id == orderId) @@ -108,53 +108,9 @@ class WooPosOrdersViewModel @Inject constructor( ) } - fun onOrdersDetailsShown(orderId: Long) { - trackOrderDetailsShown(orderId) - } - - private inline fun retrieveOrderTrackingData( - orderId: Long, - crossinline block: (meta: OrderTrackingMeta, daysSince: Int) -> Unit - ) { - val meta = trackingMeta[orderId] ?: return - val daysSince = ( - ((System.currentTimeMillis() - meta.createdAtMillis) / MILLIS_PER_DAY) - .toInt() - .coerceAtLeast(0) - ) - viewModelScope.launch { block(meta, daysSince) } - } - - private fun trackOrderDetailsShown(orderId: Long) = retrieveOrderTrackingData(orderId) { meta, days -> - viewModelScope.launch { - analyticsTracker.track( - OrderDetailsLoaded( - orderId = orderId, - orderStatus = meta.statusSlug, - daysSinceCreated = days - ) - ) - } - } - - private fun trackOrdersListRowTapped(orderId: Long, position: Int) = retrieveOrderTrackingData( - orderId - ) { meta, days -> - viewModelScope.launch { - analyticsTracker.track( - OrdersListRowTapped( - orderId = orderId, - orderStatus = meta.statusSlug, - listPosition = position, - daysSinceCreated = days - ) - ) - } - } - fun onRefresh() { viewModelScope.launch { - analyticsTracker.track(OrdersListPullToRefreshTriggered) + ordersAnalyticsTracker.trackOrdersListPullToRefreshTriggered() } val currentState = _state.value @@ -199,7 +155,7 @@ class WooPosOrdersViewModel @Inject constructor( fun onEmailReceiptButtonClicked(orderId: Long) { viewModelScope.launch { - analyticsTracker.track(OrderDetailsEmailReceiptTapped) + ordersAnalyticsTracker.trackOrderDetailsEmailReceiptTapped() childrenToParentEventSender.sendToParent( ToEmailReceipt(orderId) ) @@ -242,7 +198,7 @@ class WooPosOrdersViewModel @Inject constructor( val result = ordersDataSource.loadMore(normalizedQuery) if (result.isSuccess) { - analyticsTracker.track(OrdersListNextPageLoaded) + ordersAnalyticsTracker.trackOrdersListNextPageLoaded() appendOrders(result.getOrThrow()) } else { _state.value = newState.copy(paginationState = WooPosPaginationState.Error) @@ -254,7 +210,7 @@ class WooPosOrdersViewModel @Inject constructor( when (event) { is WooPosSearchUIEvent.SearchIconClicked -> { viewModelScope.launch { - analyticsTracker.track(OrdersListSearchButtonTapped) + ordersAnalyticsTracker.trackOrdersListSearchButtonTapped() } updateSearchState( @@ -324,8 +280,6 @@ class WooPosOrdersViewModel @Inject constructor( val current = _state.value as? WooPosOrdersState.Content ?: return val loaded = current.items as? WooPosOrdersState.Content.Items.Loaded ?: return - cacheOrderTrackingMeta(updated) - val selectedId = loaded.items.keys.firstOrNull { it.isSelected }?.id val newItem = mapOrderItem(updated, selectedId) val newDetails = mapOrderDetails(updated) @@ -383,7 +337,7 @@ class WooPosOrdersViewModel @Inject constructor( is SearchOrdersResult.Success -> { val elapsedMs = mark.elapsedNow().inWholeMilliseconds - analyticsTracker.track(OrdersListSearchResultsFetched(elapsedMs)) + ordersAnalyticsTracker.trackOrdersListSearchResultsFetched(elapsedMs) if (result.orders.isEmpty()) { _state.value = WooPosOrdersState.Content( @@ -429,7 +383,7 @@ class WooPosOrdersViewModel @Inject constructor( is LoadOrdersResult.SuccessRemote -> { val elapsedMs = mark.elapsedNow().inWholeMilliseconds - analyticsTracker.track(OrdersListFetched(elapsedMs)) + ordersAnalyticsTracker.trackOrdersListFetched(elapsedMs) if (result.orders.isEmpty()) { _state.value = WooPosOrdersState.Empty( @@ -504,8 +458,6 @@ class WooPosOrdersViewModel @Inject constructor( private suspend fun mapOrderItem(order: Order, selectedId: Long?): OrderItemViewState { val statusText = order.status.localizedLabel(resourceProvider, locale) - cacheOrderTrackingMeta(order) - return OrderItemViewState( id = order.id, title = "#${order.number}", @@ -518,14 +470,9 @@ class WooPosOrdersViewModel @Inject constructor( status = PosOrderStatus( text = statusText, colorKey = OrderStatusColorKey.fromStatus(order.status) - ) - ) - } - - private fun cacheOrderTrackingMeta(order: Order) { - trackingMeta[order.id] = OrderTrackingMeta( - createdAtMillis = order.dateCreated.time, - statusSlug = order.status.toString() + ), + statusSlug = order.status.toString(), + createdAtMillis = order.dateCreated.time ) } From d3abf2e2c6ba84e9fd1325bb2848b4dc955cd8ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81sar=20Vargas=20Casaseca?= Date: Fri, 31 Oct 2025 08:40:16 +0100 Subject: [PATCH 20/21] Further changes related to the patch --- .../ui/woopos/orders/WooPosOrdersDetails.kt | 10 +-- .../ui/woopos/orders/WooPosOrdersScreen.kt | 24 +++---- .../orders/WooPosOrdersViewModelTest.kt | 71 +++++-------------- 3 files changed, 31 insertions(+), 74 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDetails.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDetails.kt index 7bedd3c1fa4c..c6f3a53008c9 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDetails.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDetails.kt @@ -47,13 +47,8 @@ import com.woocommerce.android.ui.woopos.common.composeui.designsystem.WooPosTyp fun WooPosOrderDetails( modifier: Modifier = Modifier, details: OrderDetailsViewState, - onEmailReceiptButtonClicked: (Long) -> Unit, - onShown: (Long) -> Unit + onEmailReceiptButtonClicked: (Long) -> Unit ) { - LaunchedEffect(details.id) { - onShown(details.id) - } - Column( modifier = modifier .fillMaxSize() @@ -382,8 +377,7 @@ fun WooPosOrderDetailsPreview() { WooPosTheme { WooPosOrderDetails( details = orderDetails, - onEmailReceiptButtonClicked = {}, - onShown = {} + onEmailReceiptButtonClicked = {} ) } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersScreen.kt index 484043dd4a8c..820023b4a67e 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersScreen.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersScreen.kt @@ -109,7 +109,6 @@ fun WooPosOrdersScreen( onSearchErrorRetry = viewModel::onSearchErrorRetry, onOrdersEmptyActionClicked = viewModel::onOrdersEmptyActionClicked, onOrdersLoadingErrorRetryButtonClicked = viewModel::onOrdersLoadingErrorRetryButtonClicked, - onDetailsShown = viewModel::onOrdersDetailsShown, onEmailReceiptButtonClicked = viewModel::onEmailReceiptButtonClicked ) } @@ -127,7 +126,6 @@ private fun WooPosOrdersScreen( onSearchErrorRetry: () -> Unit, onOrdersEmptyActionClicked: () -> Unit, onOrdersLoadingErrorRetryButtonClicked: () -> Unit, - onDetailsShown: (Long) -> Unit, onEmailReceiptButtonClicked: (Long) -> Unit, ) { BackHandler { onBackClicked() } @@ -142,7 +140,6 @@ private fun WooPosOrdersScreen( onPaginationErrorTryAgain = onPaginationErrorTryAgain, onSearchEvent = onSearchEvent, onSearchErrorRetry = onSearchErrorRetry, - onDetailsShown = onDetailsShown, onEmailReceiptButtonClicked = onEmailReceiptButtonClicked ) @@ -176,7 +173,6 @@ private fun OrdersContent( onPaginationErrorTryAgain: () -> Unit, onSearchEvent: (WooPosSearchUIEvent) -> Unit, onSearchErrorRetry: () -> Unit, - onDetailsShown: (Long) -> Unit, onEmailReceiptButtonClicked: (Long) -> Unit ) { Row(modifier = Modifier.fillMaxSize()) { @@ -204,8 +200,7 @@ private fun OrdersContent( modifier = Modifier .fillMaxHeight(), details = state.selectedDetails, - onEmailReceiptButtonClicked = onEmailReceiptButtonClicked, - onShown = onDetailsShown + onEmailReceiptButtonClicked = onEmailReceiptButtonClicked ) } } @@ -520,7 +515,9 @@ fun WooPosOrdersScreenPreview() { status = PosOrderStatus( text = "Completed", colorKey = OrderStatusColorKey.COMPLETED - ) + ), + statusSlug = "Completed", + createdAtMillis = 1 ) val item2 = OrderItemViewState( id = 2, @@ -532,7 +529,9 @@ fun WooPosOrdersScreenPreview() { status = PosOrderStatus( text = "Processing", colorKey = OrderStatusColorKey.PROCESSING - ) + ), + statusSlug = "Completed", + createdAtMillis = 1 ) val details1 = sampleOrderDetails(id = 1L, number = "#014") @@ -561,8 +560,7 @@ fun WooPosOrdersScreenPreview() { onSearchErrorRetry = {}, onOrdersEmptyActionClicked = {}, onOrdersLoadingErrorRetryButtonClicked = {}, - onEmailReceiptButtonClicked = {}, - onDetailsShown = {} + onEmailReceiptButtonClicked = {} ) } } @@ -595,8 +593,7 @@ fun WooPosOrdersSearchErrorStatePreview() { onSearchErrorRetry = {}, onOrdersEmptyActionClicked = {}, onOrdersLoadingErrorRetryButtonClicked = {}, - onEmailReceiptButtonClicked = {}, - onDetailsShown = {} + onEmailReceiptButtonClicked = {} ) } } @@ -629,8 +626,7 @@ fun WooPosOrdersNothingFoundStatePreview() { onSearchErrorRetry = {}, onOrdersEmptyActionClicked = {}, onOrdersLoadingErrorRetryButtonClicked = {}, - onEmailReceiptButtonClicked = {}, - onDetailsShown = {} + onEmailReceiptButtonClicked = {} ) } } diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModelTest.kt index 60bf637036eb..9ea6d968c1ea 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModelTest.kt @@ -35,6 +35,7 @@ import org.junit.Test import org.mockito.Mockito.mock import org.mockito.kotlin.any import org.mockito.kotlin.argThat +import org.mockito.kotlin.eq import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @@ -59,7 +60,7 @@ class WooPosOrdersViewModelTest { private val getOrderRefunds: WooPosGetOrderRefundsByOrderId = mock() private val providedLocale: Locale = Locale.US private val childrenToParentEventSender: WooPosChildrenToParentEventSender = mock() - private val analyticsTracker: WooPosAnalyticsTracker = mock() + private val ordersAnalyticsTracker: WooPosOrdersAnalyticsTracker = mock() private fun createViewModel(): WooPosOrdersViewModel { return WooPosOrdersViewModel( @@ -70,7 +71,7 @@ class WooPosOrdersViewModelTest { childrenToParentEventSender = childrenToParentEventSender, formatPrice = formatPrice, getOrderRefunds = getOrderRefunds, - analyticsTracker = analyticsTracker + ordersAnalyticsTracker = ordersAnalyticsTracker ) } @@ -295,11 +296,7 @@ class WooPosOrdersViewModelTest { val hint = openState.input as WooPosSearchInputState.Open.Input.Hint assertThat(hint.hint).isEqualTo("Search orders") assertThat(openState.requestFocus).isTrue() - verify(analyticsTracker).track( - argThat { - this is OrdersListSearchButtonTapped - } - ) + verify(ordersAnalyticsTracker).trackOrdersListSearchButtonTapped() } @Test @@ -391,11 +388,7 @@ class WooPosOrdersViewModelTest { val loadedItems = content.items as WooPosOrdersState.Content.Items.Loaded assertThat(loadedItems.items.keys.map { it.id }).containsExactly(1L, 2L, 3L, 4L) assertThat(content.paginationState).isEqualTo(WooPosPaginationState.None) - verify(analyticsTracker).track( - argThat { - this is OrdersListNextPageLoaded - } - ) + verify(ordersAnalyticsTracker).trackOrdersListNextPageLoaded() } @Test @@ -636,11 +629,7 @@ class WooPosOrdersViewModelTest { val loadedItems = content.items as WooPosOrdersState.Content.Items.Loaded assertThat(loadedItems.items.keys.map { it.id }).containsExactly(300L, 400L) assertThat(content.selectedDetails.id).isEqualTo(300L) - verify(analyticsTracker).track( - argThat { - this is OrdersListPullToRefreshTriggered - } - ) + verify(ordersAnalyticsTracker).trackOrdersListPullToRefreshTriggered() } @Test @@ -658,11 +647,7 @@ class WooPosOrdersViewModelTest { // THEN verify(childrenToParentEventSender).sendToParent(ToEmailReceipt(123L)) - verify(analyticsTracker).track( - argThat { - this is OrderDetailsEmailReceiptTapped - } - ) + verify(ordersAnalyticsTracker).trackOrderDetailsEmailReceiptTapped() } @Test @@ -783,7 +768,7 @@ class WooPosOrdersViewModelTest { } @Test - fun `given orders loaded, when selecting an order, then tracks OrdersListRowTapped`() = runTest { + fun `given orders loaded, when selecting an order, then tracks OrdersListRowTapped and OrderDetailsLoaded`() = runTest { whenever(dataSource.loadOrders()).thenReturn( flow { emit(LoadOrdersResult.SuccessRemote(listOf(order(1), order(2), order(3)))) } ) @@ -793,31 +778,17 @@ class WooPosOrdersViewModelTest { viewModel.onOrderSelected(3L) advanceUntilIdle() - verify(analyticsTracker).track( - argThat { - this is OrdersListRowTapped - } + verify(ordersAnalyticsTracker).trackOrdersListRowTapped( + orderId = eq(3L), + orderStatus = any(), + listPosition = eq(2), + createdAtMillis = any() ) - } - @Test - fun `given selected order, when details shown, then tracks OrderDetailsLoaded`() = runTest { - whenever(dataSource.loadOrders()).thenReturn( - flow { emit(LoadOrdersResult.SuccessRemote(listOf(order(10), order(20)))) } - ) - viewModel = createViewModel() - advanceUntilIdle() - - viewModel.onOrderSelected(20L) - advanceUntilIdle() - - viewModel.onOrdersDetailsShown(20L) - advanceUntilIdle() - - verify(analyticsTracker).track( - argThat { - this is com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrderDetailsLoaded - } + verify(ordersAnalyticsTracker).trackOrderDetailsLoaded( + orderId = eq(3L), + orderStatus = any(), + createdAtMillis = any() ) } @@ -835,9 +806,7 @@ class WooPosOrdersViewModelTest { viewModel = createViewModel() advanceUntilIdle() - verify(analyticsTracker).track( - argThat { this is OrdersListFetched } - ) + verify(ordersAnalyticsTracker).trackOrdersListFetched(any()) } @Test @@ -855,8 +824,6 @@ class WooPosOrdersViewModelTest { viewModel.onSearchEvent(WooPosSearchUIEvent.Search(query, query.length)) advanceUntilIdle() - verify(analyticsTracker).track( - argThat { this is OrdersListSearchResultsFetched } - ) + verify(ordersAnalyticsTracker).trackOrdersListSearchResultsFetched(any()) } } From 70de4e36c0600c49e70359c731bcb210fb9546f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81sar=20Vargas=20Casaseca?= Date: Fri, 31 Oct 2025 08:51:05 +0100 Subject: [PATCH 21/21] Fix detekt issues --- .../android/ui/woopos/orders/WooPosOrdersDetails.kt | 1 - .../ui/woopos/orders/WooPosOrdersViewModelTest.kt | 9 --------- 2 files changed, 10 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDetails.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDetails.kt index c6f3a53008c9..fff78d5da0a1 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDetails.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDetails.kt @@ -18,7 +18,6 @@ import androidx.compose.material.icons.outlined.Inventory2 import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModelTest.kt index 9ea6d968c1ea..f75171de2def 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModelTest.kt @@ -13,14 +13,6 @@ import com.woocommerce.android.ui.woopos.home.WooPosChildrenToParentEventSender import com.woocommerce.android.ui.woopos.home.items.WooPosPaginationState import com.woocommerce.android.ui.woopos.home.items.WooPosPullToRefreshState import com.woocommerce.android.ui.woopos.util.WooPosCoroutineTestRule -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrderDetailsEmailReceiptTapped -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListFetched -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListNextPageLoaded -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListPullToRefreshTriggered -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListRowTapped -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListSearchButtonTapped -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.OrdersListSearchResultsFetched -import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsTracker import com.woocommerce.android.ui.woopos.util.format.WooPosFormatPrice import com.woocommerce.android.viewmodel.ResourceProvider import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -34,7 +26,6 @@ import org.junit.Rule import org.junit.Test import org.mockito.Mockito.mock import org.mockito.kotlin.any -import org.mockito.kotlin.argThat import org.mockito.kotlin.eq import org.mockito.kotlin.times import org.mockito.kotlin.verify