From 45a60b1dacfe14a61d60b7bf19d20091de2f98d4 Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 26 Jan 2024 12:23:43 +0100 Subject: [PATCH 01/74] Method to check if backend receipt generation supported --- .../onboarding/CardReaderOnboardingChecker.kt | 2 -- .../payments/receipt/PaymentReceiptHelper.kt | 21 ++++++++++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/onboarding/CardReaderOnboardingChecker.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/onboarding/CardReaderOnboardingChecker.kt index c52fc728e83..9f0bceea198 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/onboarding/CardReaderOnboardingChecker.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/onboarding/CardReaderOnboardingChecker.kt @@ -54,8 +54,6 @@ import org.wordpress.android.fluxc.store.WCInPersonPaymentsStore.InPersonPayment import org.wordpress.android.fluxc.store.WooCommerceStore import javax.inject.Inject -const val WCPAY_RECEIPTS_SENDING_SUPPORT_VERSION = "4.0.0" - /** * This class is used to check if the selected store is ready to accept In Person Payments. The app should check store's * eligibility every time it attempts to connect to a card reader. diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelper.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelper.kt index 4ce9c100406..e2bb634367d 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelper.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelper.kt @@ -4,12 +4,15 @@ import com.woocommerce.android.AppPrefsWrapper import com.woocommerce.android.extensions.semverCompareTo import com.woocommerce.android.tools.SelectedSite import com.woocommerce.android.ui.payments.cardreader.onboarding.PluginType -import com.woocommerce.android.ui.payments.cardreader.onboarding.WCPAY_RECEIPTS_SENDING_SUPPORT_VERSION +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import org.wordpress.android.fluxc.model.SiteModel +import org.wordpress.android.fluxc.store.WooCommerceStore import javax.inject.Inject class PaymentReceiptHelper @Inject constructor( private val selectedSite: SelectedSite, + private val wooCommerceStore: WooCommerceStore, private val appPrefsWrapper: AppPrefsWrapper ) { fun storeReceiptUrl(orderId: Long, receiptUrl: String) { @@ -41,4 +44,20 @@ class PaymentReceiptHelper @Inject constructor( pluginVersion.semverCompareTo(WCPAY_RECEIPTS_SENDING_SUPPORT_VERSION) >= 0 } } + + private suspend fun isWCCanGenerateReceipts() = + withContext(Dispatchers.IO) { + val currentWooCoreVersion = + wooCommerceStore.getSitePlugin( + selectedSite.get(), + WooCommerceStore.WooPlugin.WOO_CORE + )?.version ?: return@withContext false + + currentWooCoreVersion.semverCompareTo(WC_CAN_GENERATE_RECEIPTS_VERSION) >= 0 + } + + private companion object { + const val WCPAY_RECEIPTS_SENDING_SUPPORT_VERSION = "4.0.0" + const val WC_CAN_GENERATE_RECEIPTS_VERSION = "6.4.0" + } } From fa22ea2ff7ca0892b49d866f6e0ab8b9946c1841 Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 26 Jan 2024 14:30:16 +0100 Subject: [PATCH 02/74] Updated fluxc hash --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 7bf7df5415c..55a46108b90 100644 --- a/build.gradle +++ b/build.gradle @@ -96,7 +96,7 @@ tasks.register("installGitHooks", Copy) { } ext { - fluxCVersion = 'trunk-bf45b08090e8241eb930e1eef32f935bc6cd0424' + fluxCVersion = '2948-235834ae9467ae3ca367a4b13c4f9c6aea0f31bf' glideVersion = '4.13.2' coilVersion = '2.1.0' constraintLayoutVersion = '1.2.0' From 553956694142df24073a6657d0e534b3eae4df65 Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 26 Jan 2024 14:37:00 +0100 Subject: [PATCH 03/74] Use the receipt helper instead of preference wrapper --- .../ui/orders/details/OrderDetailViewModel.kt | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailViewModel.kt index 38375776ee7..07788c9b132 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailViewModel.kt @@ -11,6 +11,7 @@ import androidx.lifecycle.distinctUntilChanged import com.google.android.material.snackbar.Snackbar import com.woocommerce.android.AppPrefs import com.woocommerce.android.R.string +import com.woocommerce.android.extensions.orNullIfEmpty import com.woocommerce.android.extensions.whenNotNullNorEmpty import com.woocommerce.android.model.GiftCardSummary import com.woocommerce.android.model.Order @@ -49,6 +50,7 @@ import com.woocommerce.android.ui.orders.OrderStatusUpdateSource import com.woocommerce.android.ui.orders.details.customfields.CustomOrderFieldsHelper import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderFlowParam import com.woocommerce.android.ui.payments.cardreader.payment.CardReaderPaymentCollectibilityChecker +import com.woocommerce.android.ui.payments.receipt.PaymentReceiptHelper import com.woocommerce.android.ui.payments.tracking.PaymentsFlowTracker import com.woocommerce.android.ui.products.ProductDetailRepository import com.woocommerce.android.ui.products.addons.AddonRepository @@ -91,7 +93,8 @@ class OrderDetailViewModel @Inject constructor( private val getOrderSubscriptions: GetOrderSubscriptions, private val giftCardRepository: GiftCardRepository, private val orderProductMapper: OrderProductMapper, - private val productDetailRepository: ProductDetailRepository + private val productDetailRepository: ProductDetailRepository, + private val paymentReceiptHelper: PaymentReceiptHelper, ) : ScopedViewModel(savedState), OnProductFetchedListener { private val navArgs: OrderDetailFragmentArgs by savedState.navArgs() @@ -344,7 +347,7 @@ class OrderDetailViewModel @Inject constructor( fun onSeeReceiptClicked() { tracker.trackReceiptViewTapped(order.id, order.status) - loadReceiptUrl()?.let { + loadReceiptUrl().orNullIfEmpty()?.let { triggerEvent(PreviewReceipt(order.billingAddress.email, it, order.id)) } ?: WooLog.e(T.ORDERS, "ReceiptUrl is null, but SeeReceipt button is visible") } @@ -372,11 +375,7 @@ class OrderDetailViewModel @Inject constructor( triggerEvent(ShowSnackbar(message)) } - private fun loadReceiptUrl(): String? { - return selectedSite.getIfExists()?.let { - appPrefs.getReceiptUrl(it.id, it.siteId, it.selfHostedSiteId, order.id) - } - } + private fun loadReceiptUrl() = paymentReceiptHelper.getReceiptUrl(order.id) fun onViewRefundedProductsClicked() { triggerEvent(ViewRefundedProducts(orderId = order.id)) @@ -580,7 +579,7 @@ class OrderDetailViewModel @Inject constructor( orderInfo = OrderDetailViewState.OrderInfo( order = order, isPaymentCollectableWithCardReader = isPaymentCollectable, - isReceiptButtonsVisible = !loadReceiptUrl().isNullOrEmpty() + isReceiptButtonsVisible = loadReceiptUrl().isNotEmpty() ), orderStatus = orderStatus, toolbarTitle = resourceProvider.getString( From 0cbba32ab28b5f66c135f3e7f541742cd505c67b Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 26 Jan 2024 14:56:21 +0100 Subject: [PATCH 04/74] Made test compilable --- .../android/ui/payments/receipt/PaymentReceiptHelper.kt | 2 +- .../android/ui/orders/OrderDetailViewModelTest.kt | 7 +++++-- .../ui/payments/receipt/PaymentReceiptHelperTest.kt | 4 +++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelper.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelper.kt index e2bb634367d..38dba7d44a0 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelper.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelper.kt @@ -13,7 +13,7 @@ import javax.inject.Inject class PaymentReceiptHelper @Inject constructor( private val selectedSite: SelectedSite, private val wooCommerceStore: WooCommerceStore, - private val appPrefsWrapper: AppPrefsWrapper + private val appPrefsWrapper: AppPrefsWrapper, ) { fun storeReceiptUrl(orderId: Long, receiptUrl: String) { selectedSite.get().let { diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/orders/OrderDetailViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/orders/OrderDetailViewModelTest.kt index e9bd5d97a39..e7c1e68b49a 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/orders/OrderDetailViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/orders/OrderDetailViewModelTest.kt @@ -115,6 +115,7 @@ class OrderDetailViewModelTest : BaseUnitTest() { private val orderDetailsTransactionLauncher = mock() private val orderProductMapper = OrderProductMapper() private val productDetailRepository: ProductDetailRepository = mock() + private val paymentReceiptHelper: PaymentReceiptHelper = mock() private val order = OrderTestUtils.generateTestOrder(ORDER_ID) private val orderInfo = OrderInfo(OrderTestUtils.generateTestOrder(ORDER_ID)) @@ -177,7 +178,8 @@ class OrderDetailViewModelTest : BaseUnitTest() { getOrderSubscriptions, giftCardRepository, orderProductMapper, - productDetailRepository + productDetailRepository, + paymentReceiptHelper, ) ) } @@ -201,7 +203,8 @@ class OrderDetailViewModelTest : BaseUnitTest() { getOrderSubscriptions, giftCardRepository, orderProductMapper, - productDetailRepository + productDetailRepository, + paymentReceiptHelper ) ) } diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelperTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelperTest.kt index 4dacf8a0fc2..fcad01fe160 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelperTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelperTest.kt @@ -10,6 +10,7 @@ import org.mockito.kotlin.any import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever +import org.wordpress.android.fluxc.store.WooCommerceStore @ExperimentalCoroutinesApi class PaymentReceiptHelperTest { @@ -17,8 +18,9 @@ class PaymentReceiptHelperTest { on { get() }.thenReturn(mock()) } private val appPrefsWrapper: AppPrefsWrapper = mock() + private val wooCommerceStore: WooCommerceStore = mock() - private val helper = PaymentReceiptHelper(selectedSite, appPrefsWrapper) + private val helper = PaymentReceiptHelper(selectedSite, wooCommerceStore, appPrefsWrapper) @Test fun `given selected site, when storeReceiptUrl, then url is stored`() { From 1e08d1ecd1ff8e4332361360e491dad421405e3f Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 26 Jan 2024 15:30:06 +0100 Subject: [PATCH 05/74] Modification on the order details screen to fetch the receipt including from the backend --- .../ui/orders/details/OrderDetailViewModel.kt | 18 ++++++----- .../payments/receipt/PaymentReceiptHelper.kt | 31 ++++++++++++++++++- WooCommerce/src/main/res/values/strings.xml | 1 + 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailViewModel.kt index 07788c9b132..42097b29b17 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailViewModel.kt @@ -11,7 +11,6 @@ import androidx.lifecycle.distinctUntilChanged import com.google.android.material.snackbar.Snackbar import com.woocommerce.android.AppPrefs import com.woocommerce.android.R.string -import com.woocommerce.android.extensions.orNullIfEmpty import com.woocommerce.android.extensions.whenNotNullNorEmpty import com.woocommerce.android.model.GiftCardSummary import com.woocommerce.android.model.Order @@ -346,10 +345,15 @@ class OrderDetailViewModel @Inject constructor( } fun onSeeReceiptClicked() { - tracker.trackReceiptViewTapped(order.id, order.status) - loadReceiptUrl().orNullIfEmpty()?.let { - triggerEvent(PreviewReceipt(order.billingAddress.email, it, order.id)) - } ?: WooLog.e(T.ORDERS, "ReceiptUrl is null, but SeeReceipt button is visible") + launch { + tracker.trackReceiptViewTapped(order.id, order.status) + val receiptResult = paymentReceiptHelper.getReceiptUrl(order.id) + if (receiptResult.isSuccess) { + triggerEvent(PreviewReceipt(order.billingAddress.email, receiptResult.getOrThrow(), order.id)) + } else { + triggerEvent(ShowSnackbar(string.order_detail_receipt_fetching_error)) + } + } } fun onPrintingInstructionsClicked() { @@ -375,8 +379,6 @@ class OrderDetailViewModel @Inject constructor( triggerEvent(ShowSnackbar(message)) } - private fun loadReceiptUrl() = paymentReceiptHelper.getReceiptUrl(order.id) - fun onViewRefundedProductsClicked() { triggerEvent(ViewRefundedProducts(orderId = order.id)) } @@ -579,7 +581,7 @@ class OrderDetailViewModel @Inject constructor( orderInfo = OrderDetailViewState.OrderInfo( order = order, isPaymentCollectableWithCardReader = isPaymentCollectable, - isReceiptButtonsVisible = loadReceiptUrl().isNotEmpty() + isReceiptButtonsVisible = paymentReceiptHelper.isReceiptAvailable(order.id), ), orderStatus = orderStatus, toolbarTitle = resourceProvider.getString( diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelper.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelper.kt index 38dba7d44a0..c483e8f1d97 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelper.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelper.kt @@ -7,6 +7,7 @@ import com.woocommerce.android.ui.payments.cardreader.onboarding.PluginType import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.wordpress.android.fluxc.model.SiteModel +import org.wordpress.android.fluxc.store.WCOrderStore import org.wordpress.android.fluxc.store.WooCommerceStore import javax.inject.Inject @@ -14,6 +15,7 @@ class PaymentReceiptHelper @Inject constructor( private val selectedSite: SelectedSite, private val wooCommerceStore: WooCommerceStore, private val appPrefsWrapper: AppPrefsWrapper, + private val orderStore: WCOrderStore, ) { fun storeReceiptUrl(orderId: Long, receiptUrl: String) { selectedSite.get().let { @@ -21,7 +23,32 @@ class PaymentReceiptHelper @Inject constructor( } } - fun getReceiptUrl(orderId: Long) = selectedSite.get().let { + suspend fun getReceiptUrl(orderId: Long): Result = + if (isWCCanGenerateReceipts()) { + val fetchingResult = orderStore.fetchOrdersReceipt( + site = selectedSite.get(), + orderId = orderId, + expirationDays = RECEIPT_EXPIRATION_DAYS, + ) + + val result = fetchingResult.result + if (fetchingResult.isError || result == null) { + Result.failure(Exception(fetchingResult.error.message)) + } else { + Result.success(result.receiptUrl) + } + } else { + Result.success(getReceiptUrlFromAppPrefs(orderId)) + } + + suspend fun isReceiptAvailable(orderId: Long) = + when { + isWCCanGenerateReceipts() -> true + getReceiptUrlFromAppPrefs(orderId).isNotEmpty() -> true + else -> false + } + + private fun getReceiptUrlFromAppPrefs(orderId: Long) = selectedSite.get().let { appPrefsWrapper.getReceiptUrl(it.id, it.siteId, it.selfHostedSiteId, orderId) } @@ -59,5 +86,7 @@ class PaymentReceiptHelper @Inject constructor( private companion object { const val WCPAY_RECEIPTS_SENDING_SUPPORT_VERSION = "4.0.0" const val WC_CAN_GENERATE_RECEIPTS_VERSION = "6.4.0" + + const val RECEIPT_EXPIRATION_DAYS = 365 } } diff --git a/WooCommerce/src/main/res/values/strings.xml b/WooCommerce/src/main/res/values/strings.xml index c3319c2b7db..21290f61aea 100644 --- a/WooCommerce/src/main/res/values/strings.xml +++ b/WooCommerce/src/main/res/values/strings.xml @@ -878,6 +878,7 @@ Use as Shipping Address PAYMENT TOTALS CUSTOM AMOUNTS + Sorry, we couldn\'t load a receipt for this order - - - - - - - + - - - + android:layout_height="wrap_content"> + + + + + + + + + + + + + + - - + From e5e2ab625cd90a5dcf36353c44388a914a80fe58 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 29 Jan 2024 15:56:48 +0100 Subject: [PATCH 26/74] Loading indicator to the receipt button --- .../views/OrderDetailPaymentInfoView.kt | 10 ++++- .../res/layout/order_detail_payment_info.xml | 42 ++++++++++++------- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/views/OrderDetailPaymentInfoView.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/views/OrderDetailPaymentInfoView.kt index 4f67cee39b9..1a2bf575cb5 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/views/OrderDetailPaymentInfoView.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/views/OrderDetailPaymentInfoView.kt @@ -198,10 +198,16 @@ class OrderDetailPaymentInfoView @JvmOverloads constructor( ) { when (receiptButtonStatus) { OrderDetailViewState.ReceiptButtonStatus.Loading -> { - + binding.paymentInfoSeeReceiptButton.visibility = VISIBLE + binding.paymentInfoSeeReceiptButton.isEnabled = false + binding.paymentInfoSeeReceiptButtonProgressBar.visibility = VISIBLE + } + OrderDetailViewState.ReceiptButtonStatus.Hidden -> { + binding.paymentInfoSeeReceiptButton.visibility = GONE + binding.paymentInfoSeeReceiptButtonProgressBar.visibility = GONE } - OrderDetailViewState.ReceiptButtonStatus.Hidden -> binding.paymentInfoSeeReceiptButton.visibility = GONE OrderDetailViewState.ReceiptButtonStatus.Visible -> { + binding.paymentInfoSeeReceiptButtonProgressBar.visibility = GONE binding.paymentInfoSeeReceiptButton.visibility = VISIBLE binding.paymentInfoSeeReceiptButton.setOnClickListener( onSeeReceiptClickListener diff --git a/WooCommerce/src/main/res/layout/order_detail_payment_info.xml b/WooCommerce/src/main/res/layout/order_detail_payment_info.xml index c3b8728e0e1..1152c5feec6 100644 --- a/WooCommerce/src/main/res/layout/order_detail_payment_info.xml +++ b/WooCommerce/src/main/res/layout/order_detail_payment_info.xml @@ -382,15 +382,24 @@ style="@style/Woo.Button.TextButton" android:layout_width="0dp" android:layout_height="wrap_content" + android:layout_marginTop="@dimen/minor_100" + android:gravity="start|center_vertical" android:paddingStart="@dimen/major_100" android:paddingEnd="@dimen/major_100" - android:gravity="start" android:text="@string/orderdetail_see_receipt_button" android:textAllCaps="false" + app:layout_constraintEnd_toStartOf="@id/paymentInfo_seeReceiptButton_progressBar" app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + + app:layout_constraintTop_toTopOf="@id/paymentInfo_seeReceiptButton" /> + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/paymentInfo_seeReceiptButton" /> + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/paymentInfo_collectCardPresentPaymentButton" /> - + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/paymentInfo_issueRefundButton" /> From 2b6bc61337e72c48634b18ba23a9455bdf4d0353 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 29 Jan 2024 16:15:43 +0100 Subject: [PATCH 27/74] Fixed blinking of the receipt details button issue --- .../android/ui/orders/details/OrderDetailViewModel.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailViewModel.kt index 1b80d6660cb..8902a5da997 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailViewModel.kt @@ -103,9 +103,13 @@ class OrderDetailViewModel @Inject constructor( get() = requireNotNull(viewState.orderInfo?.order) set(value) { viewState = viewState.copy( - orderInfo = OrderDetailViewState.OrderInfo( + orderInfo = viewState.orderInfo?.copy( + order = value, + isPaymentCollectableWithCardReader = viewState.orderInfo?.isPaymentCollectableWithCardReader + ?: false + ) ?: OrderDetailViewState.OrderInfo( value, - viewState.orderInfo?.isPaymentCollectableWithCardReader ?: false + isPaymentCollectableWithCardReader = false ) ) } From c537a9d0004cbc8a7ada1910f6202a274a3df056 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 29 Jan 2024 16:15:59 +0100 Subject: [PATCH 28/74] Better displaying of the progress bar --- WooCommerce/src/main/res/layout/order_detail_payment_info.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WooCommerce/src/main/res/layout/order_detail_payment_info.xml b/WooCommerce/src/main/res/layout/order_detail_payment_info.xml index 1152c5feec6..0566a777164 100644 --- a/WooCommerce/src/main/res/layout/order_detail_payment_info.xml +++ b/WooCommerce/src/main/res/layout/order_detail_payment_info.xml @@ -388,7 +388,7 @@ android:paddingEnd="@dimen/major_100" android:text="@string/orderdetail_see_receipt_button" android:textAllCaps="false" - app:layout_constraintEnd_toStartOf="@id/paymentInfo_seeReceiptButton_progressBar" + app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> From 3821eb77231a1819e7f8c86ea49c0ba564d271af Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 29 Jan 2024 18:03:16 +0100 Subject: [PATCH 29/74] Fixed and added tests --- .../ui/orders/OrderDetailViewModelTest.kt | 45 ++++++++++++++++--- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/orders/OrderDetailViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/orders/OrderDetailViewModelTest.kt index bdbafe61f95..ad4ad6ae310 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/orders/OrderDetailViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/orders/OrderDetailViewModelTest.kt @@ -40,6 +40,7 @@ import com.woocommerce.android.ui.payments.tracking.PaymentsFlowTracker import com.woocommerce.android.ui.products.ProductDetailRepository import com.woocommerce.android.ui.products.addons.AddonRepository import com.woocommerce.android.util.ContinuationWrapper +import com.woocommerce.android.util.captureValues import com.woocommerce.android.viewmodel.BaseUnitTest import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ShowSnackbar import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ShowUndoSnackbar @@ -326,7 +327,7 @@ class OrderDetailViewModelTest : BaseUnitTest() { } @Test - fun `given receipt is available and order is paid, when view model started, then state with receipt isReceiptButtonsVisible true emitted`() = + fun `given receipt is available and order is paid, when view model started, then state with receipt is visible emitted`() = testBlocking { // GIVEN whenever(paymentReceiptHelper.isReceiptAvailable(any())).thenReturn(true) @@ -350,11 +351,13 @@ class OrderDetailViewModelTest : BaseUnitTest() { viewModel.start() // THEN - assertThat(detailViewState!!.orderInfo!!.isReceiptButtonsVisible).isTrue() + assertThat(detailViewState!!.orderInfo!!.receiptButtonStatus).isEqualTo( + OrderDetailViewState.ReceiptButtonStatus.Visible + ) } @Test - fun `given receipt is available and order not paid, when view model started, then state with receipt isReceiptButtonsVisible false emitted`() = + fun `given receipt is available and order not paid, when view model started, then state with receipt is hidden emitted`() = testBlocking { // GIVEN whenever(paymentReceiptHelper.isReceiptAvailable(any())).thenReturn(true) @@ -378,11 +381,13 @@ class OrderDetailViewModelTest : BaseUnitTest() { viewModel.start() // THEN - assertThat(detailViewState!!.orderInfo!!.isReceiptButtonsVisible).isFalse() + assertThat(detailViewState!!.orderInfo!!.receiptButtonStatus).isEqualTo( + OrderDetailViewState.ReceiptButtonStatus.Hidden + ) } @Test - fun `given receipt is not available, when view model started, then state with receipt isReceiptButtonsVisible false emitted`() = + fun `given receipt is not available, when view model started, then state with receipt is hidden emitted`() = testBlocking { // GIVEN whenever(paymentReceiptHelper.isReceiptAvailable(any())).thenReturn(false) @@ -402,7 +407,9 @@ class OrderDetailViewModelTest : BaseUnitTest() { viewModel.start() // THEN - assertThat(detailViewState!!.orderInfo!!.isReceiptButtonsVisible).isFalse() + assertThat(detailViewState!!.orderInfo!!.receiptButtonStatus).isEqualTo( + OrderDetailViewState.ReceiptButtonStatus.Hidden + ) } @Test @@ -1275,6 +1282,32 @@ class OrderDetailViewModelTest : BaseUnitTest() { assertThat((viewModel.event.value as PreviewReceipt).billingEmail).isEqualTo(order.billingAddress.email) } + @Test + fun `when onSeeReceiptClicked clicked, then loading receipt status emitted`() = + testBlocking { + // GIVEN + whenever(orderDetailRepository.getOrderById(any())).thenReturn(order) + whenever(orderDetailRepository.fetchOrderNotes(any())).thenReturn(false) + whenever(addonsRepository.containsAddonsFrom(any())).thenReturn(false) + val receiptUrl = "https://example.com" + whenever(paymentReceiptHelper.getReceiptUrl(order.id)).thenReturn(Result.success(receiptUrl)) + + // WHEN + viewModel.start() + + val states = viewModel.viewStateData.liveData.captureValues() + + viewModel.onSeeReceiptClicked() + + // THEN + assertThat((states.last()).orderInfo!!.receiptButtonStatus).isEqualTo( + OrderDetailViewState.ReceiptButtonStatus.Visible + ) + assertThat((states[states.size - 2]).orderInfo!!.receiptButtonStatus).isEqualTo( + OrderDetailViewState.ReceiptButtonStatus.Loading + ) + } + @Test fun `given order is paid, when status is processing order complete button should be visible`() = testBlocking { From 59849fc93621bcd78f6815dbf00b8dae6d94ecac Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 30 Jan 2024 09:48:12 +0100 Subject: [PATCH 30/74] Operator to download and share receipt file --- .../payments/receipt/PaymentReceiptShare.kt | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptShare.kt diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptShare.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptShare.kt new file mode 100644 index 00000000000..fbe980141a1 --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptShare.kt @@ -0,0 +1,52 @@ +package com.woocommerce.android.ui.payments.receipt + +import android.app.Application +import android.content.Intent +import android.os.Environment +import androidx.core.content.FileProvider +import com.woocommerce.android.media.FileUtils +import com.woocommerce.android.util.FileDownloader +import javax.inject.Inject + +class PaymentReceiptShare @Inject constructor( + private val fileUtils: FileUtils, + private val fileDownloader: FileDownloader, + private val context: Application, +) { + suspend operator fun invoke(receiptUrl: String): ReceiptShareResult { + val receiptFile = fileUtils.createTempTimeStampedFile( + storageDir = context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS) + ?: context.filesDir, + prefix = "receipt", + fileExtension = "html" + ) ?: return ReceiptShareResult.Error.FileCreation + if (!fileDownloader.downloadFile(receiptUrl, receiptFile)) { + return ReceiptShareResult.Error.FileDownload + } + + val uri = FileProvider.getUriForFile( + context, + context.packageName + ".provider", + receiptFile + ) + val intent = Intent(Intent.ACTION_SEND).apply { + type = "application/*" + putExtra(Intent.EXTRA_STREAM, uri) + } + return try { + context.startActivity(Intent.createChooser(intent, null)) + ReceiptShareResult.Success + } catch (e: Exception) { + ReceiptShareResult.Error.Sharing + } + } + + sealed class ReceiptShareResult { + object Success : ReceiptShareResult() + sealed class Error : ReceiptShareResult() { + object Sharing : Error() + object FileCreation : Error() + object FileDownload : Error() + } + } +} From 9b8087082e7df9659c2e79970034ec5a1f7705ae Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 30 Jan 2024 10:27:08 +0100 Subject: [PATCH 31/74] Handle sharing errors --- .../payment/CardReaderPaymentViewModel.kt | 2 +- .../payments/receipt/PaymentReceiptShare.kt | 10 +++- .../receipt/preview/ReceiptPreviewFragment.kt | 4 +- .../preview/ReceiptPreviewViewModel.kt | 58 +++++++++++-------- .../src/main/res/values-ar/strings.xml | 2 +- .../src/main/res/values-de/strings.xml | 2 +- .../src/main/res/values-es/strings.xml | 2 +- .../src/main/res/values-fr/strings.xml | 2 +- .../src/main/res/values-he/strings.xml | 2 +- .../src/main/res/values-id/strings.xml | 2 +- .../src/main/res/values-it/strings.xml | 2 +- .../src/main/res/values-ja/strings.xml | 2 +- .../src/main/res/values-ko/strings.xml | 2 +- .../src/main/res/values-nl/strings.xml | 2 +- .../src/main/res/values-pt-rBR/strings.xml | 2 +- .../src/main/res/values-ru/strings.xml | 2 +- .../src/main/res/values-sv/strings.xml | 2 +- .../src/main/res/values-tr/strings.xml | 2 +- .../src/main/res/values-zh-rCN/strings.xml | 2 +- .../src/main/res/values-zh-rTW/strings.xml | 2 +- WooCommerce/src/main/res/values/strings.xml | 4 +- .../preview/ReceiptPreviewViewModelTest.kt | 8 +-- 22 files changed, 68 insertions(+), 50 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModel.kt index 339e8b351a1..711062c7a6b 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModel.kt @@ -749,7 +749,7 @@ class CardReaderPaymentViewModel fun onEmailActivityNotFound() { tracker.trackEmailReceiptFailed() - triggerEvent(ShowSnackbarInDialog(R.string.card_reader_payment_email_client_not_found)) + triggerEvent(ShowSnackbarInDialog(R.string.card_reader_payment_receipt_app_to_share_not_found)) } fun onPrintResult(result: PrintJobResult) { diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptShare.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptShare.kt index fbe980141a1..4fd8d553bd9 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptShare.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptShare.kt @@ -34,17 +34,21 @@ class PaymentReceiptShare @Inject constructor( putExtra(Intent.EXTRA_STREAM, uri) } return try { - context.startActivity(Intent.createChooser(intent, null)) + context.startActivity( + Intent.createChooser(intent, null).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + } + ) ReceiptShareResult.Success } catch (e: Exception) { - ReceiptShareResult.Error.Sharing + ReceiptShareResult.Error.Sharing(e) } } sealed class ReceiptShareResult { object Success : ReceiptShareResult() sealed class Error : ReceiptShareResult() { - object Sharing : Error() + data class Sharing(val exception: Exception) : Error() object FileCreation : Error() object FileDownload : Error() } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewFragment.kt index 9523b67ec65..e6da1500a36 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewFragment.kt @@ -52,7 +52,7 @@ class ReceiptPreviewFragment : BaseFragment(R.layout.fragment_receipt_preview), true } R.id.menu_send -> { - viewModel.onSendEmailClicked() + viewModel.onShareClicked() true } else -> false @@ -111,7 +111,7 @@ class ReceiptPreviewFragment : BaseFragment(R.layout.fragment_receipt_preview), private fun composeEmail(event: SendReceipt) { ActivityUtils.sendEmail(requireActivity(), event.address, event.subject, event.content) { - viewModel.onEmailActivityNotFound() + viewModel.onActivityToShareNotFound() } } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt index e609731afa8..f2ae1fcd135 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt @@ -11,9 +11,7 @@ import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_FAILED import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_SUCCESS import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_TAPPED import com.woocommerce.android.analytics.AnalyticsTrackerWrapper -import com.woocommerce.android.model.UiString.UiStringRes -import com.woocommerce.android.model.UiString.UiStringText -import com.woocommerce.android.tools.SelectedSite +import com.woocommerce.android.ui.payments.receipt.PaymentReceiptShare import com.woocommerce.android.ui.payments.receipt.preview.ReceiptPreviewViewModel.ReceiptPreviewViewState.Content import com.woocommerce.android.ui.payments.receipt.preview.ReceiptPreviewViewModel.ReceiptPreviewViewState.Loading import com.woocommerce.android.util.PrintHtmlHelper.PrintJobResult @@ -32,7 +30,7 @@ class ReceiptPreviewViewModel @Inject constructor( savedState: SavedStateHandle, private val tracker: AnalyticsTrackerWrapper, - private val selectedSite: SelectedSite, + private val paymentReceiptShare: PaymentReceiptShare, ) : ScopedViewModel(savedState) { private val args: ReceiptPreviewFragmentArgs by savedState.navArgs() @@ -52,30 +50,44 @@ class ReceiptPreviewViewModel triggerEvent(PrintReceipt(args.receiptUrl, "receipt-order-${args.orderId}")) } - fun onSendEmailClicked() { + fun onShareClicked() { launch { tracker.track(RECEIPT_EMAIL_TAPPED) - triggerEvent( - SendReceipt( - content = UiStringRes( - string.card_reader_payment_receipt_email_content, - listOf(UiStringText(args.receiptUrl)) - ), - subject = UiStringRes( - string.card_reader_payment_receipt_email_subject, - listOf(UiStringText(selectedSite.get().name.orEmpty())) - ), - address = args.billingEmail - ) - ) + when (val sharingResult = paymentReceiptShare(args.receiptUrl)) { + PaymentReceiptShare.ReceiptShareResult.Error.FileCreation -> { + tracker.track( + RECEIPT_EMAIL_FAILED, + errorContext = this@ReceiptPreviewViewModel.javaClass.simpleName, + errorType = "file_creation_failed", + errorDescription = "File creation failed" + ) + triggerEvent(ShowSnackbar(string.card_reader_payment_receipt_can_not_be_stored)) + } + PaymentReceiptShare.ReceiptShareResult.Error.FileDownload -> { + tracker.track( + RECEIPT_EMAIL_FAILED, + errorContext = this@ReceiptPreviewViewModel.javaClass.simpleName, + errorType = "file_download_failed", + errorDescription = "File download failed" + ) + triggerEvent(ShowSnackbar(string.card_reader_payment_receipt_can_not_be_downloaded)) + } + is PaymentReceiptShare.ReceiptShareResult.Error.Sharing -> { + tracker.track( + RECEIPT_EMAIL_FAILED, + errorContext = this@ReceiptPreviewViewModel.javaClass.simpleName, + errorType = "no_app_found", + errorDescription = sharingResult.exception.message + ) + triggerEvent(ShowSnackbar(string.card_reader_payment_receipt_app_to_share_not_found)) + } + PaymentReceiptShare.ReceiptShareResult.Success -> { + // no-op + } + } } } - fun onEmailActivityNotFound() { - tracker.track(RECEIPT_EMAIL_FAILED) - triggerEvent(ShowSnackbar(string.card_reader_payment_email_client_not_found)) - } - fun onPrintResult(result: PrintJobResult) { tracker.track( when (result) { diff --git a/WooCommerce/src/main/res/values-ar/strings.xml b/WooCommerce/src/main/res/values-ar/strings.xml index cc49beffa7f..76aedcc43e9 100644 --- a/WooCommerce/src/main/res/values-ar/strings.xml +++ b/WooCommerce/src/main/res/values-ar/strings.xml @@ -1910,7 +1910,7 @@ Language: ar سمات المجموعات تشغيل بلوتوث الجهاز المحمول حدث خطأ في أثناء إحضار الطلب. قد تكون حالة الطلب الموجودة في التطبيق قديمة. - لا يمكن اكتشاف تطبيق عميل البريد الإلكتروني الخاص بك + لا يمكن اكتشاف تطبيق عميل البريد الإلكتروني الخاص بك إيصالك من %s تحديث الطلب تحديث حالة التطبيق diff --git a/WooCommerce/src/main/res/values-de/strings.xml b/WooCommerce/src/main/res/values-de/strings.xml index f069ba46c0f..cf4d312a3a1 100644 --- a/WooCommerce/src/main/res/values-de/strings.xml +++ b/WooCommerce/src/main/res/values-de/strings.xml @@ -1910,7 +1910,7 @@ Language: de Variantenattribute Aktiviere Bluetooth auf dem Mobilgerät Fehler beim Abrufen der Bestellung Der Bestellstatus in der App ist eventuell nicht aktuell. - E-Mail-Client-App wird nicht erkannt. + E-Mail-Client-App wird nicht erkannt. Dein Beleg vom %s Bestellung aktualisieren App-Status aktualisieren diff --git a/WooCommerce/src/main/res/values-es/strings.xml b/WooCommerce/src/main/res/values-es/strings.xml index 8e32a914850..fe0e0d5bd54 100644 --- a/WooCommerce/src/main/res/values-es/strings.xml +++ b/WooCommerce/src/main/res/values-es/strings.xml @@ -1910,7 +1910,7 @@ Language: es Atributos de variaciones Activa Bluetooth en el dispositivo móvil Error al recuperar el pedido. El estado del pedido en la aplicación podría estar desactualizado. - No se pudo detectar una aplicación cliente de correo electrónico + No se pudo detectar una aplicación cliente de correo electrónico Tu recibo de %s Actualizando pedido Actualizando estado en la aplicación diff --git a/WooCommerce/src/main/res/values-fr/strings.xml b/WooCommerce/src/main/res/values-fr/strings.xml index 2fdba76735a..f18596537c6 100644 --- a/WooCommerce/src/main/res/values-fr/strings.xml +++ b/WooCommerce/src/main/res/values-fr/strings.xml @@ -1910,7 +1910,7 @@ Language: fr Attributs des variantes Activer le Bluetooth de l’appareil mobile Erreur lors de l’extraction de la commande. L’état de la commande dans l’application est peut-être obsolète. - Aucune application de messagerie détectée + Aucune application de messagerie détectée Votre reçu de %s Actualisation de la commande Mise à jour de l’état de l’application diff --git a/WooCommerce/src/main/res/values-he/strings.xml b/WooCommerce/src/main/res/values-he/strings.xml index 4e5a67434b2..8622401ba37 100644 --- a/WooCommerce/src/main/res/values-he/strings.xml +++ b/WooCommerce/src/main/res/values-he/strings.xml @@ -1910,7 +1910,7 @@ Language: he_IL מאפייני סוגים יש להפעיל את חיבור ה-Bluetooth של המכשיר הנייד שגיאה בהבאת ההזמנה. ייתכן שמצב ההזמנה באפליקציה לא מעודכן. - לא ניתן לאתר את האפליקציה של שירות האימייל שלך + לא ניתן לאתר את האפליקציה של שירות האימייל שלך הקבלה שלך מאת %s מרענן את ההזמנה מעדכן את מצב האפליקציה diff --git a/WooCommerce/src/main/res/values-id/strings.xml b/WooCommerce/src/main/res/values-id/strings.xml index fa29a8b2c73..b7315da284f 100644 --- a/WooCommerce/src/main/res/values-id/strings.xml +++ b/WooCommerce/src/main/res/values-id/strings.xml @@ -1910,7 +1910,7 @@ Language: id Atribut variasi Aktifkan bluetooth perangkat seluler Terjadi error saat mengambil pesanan. Status pesanan pada aplikasi mungkin telah usang. - Tidak dapat mendeteksi aplikasi klien email Anda + Tidak dapat mendeteksi aplikasi klien email Anda Tanda terima Anda dari %s Menyegarkan pesanan Memperbarui status aplikasi diff --git a/WooCommerce/src/main/res/values-it/strings.xml b/WooCommerce/src/main/res/values-it/strings.xml index 4f1d803552e..cae8e5ca689 100644 --- a/WooCommerce/src/main/res/values-it/strings.xml +++ b/WooCommerce/src/main/res/values-it/strings.xml @@ -1910,7 +1910,7 @@ Language: it Attributi della variante Attiva il Bluetooth del dispositivo mobile Errore durante il recupero dell\'ordine. Lo stato nell\'ordine dell\'app potrebbe essere obsoleto. - Impossibile rilevare il client e-mail della tua app + Impossibile rilevare il client e-mail della tua app La tua ricevuta da %s Aggiornamento dell\'ordine Aggiornamento dello stato dell\'app diff --git a/WooCommerce/src/main/res/values-ja/strings.xml b/WooCommerce/src/main/res/values-ja/strings.xml index ce4d1990c3b..57f251429ea 100644 --- a/WooCommerce/src/main/res/values-ja/strings.xml +++ b/WooCommerce/src/main/res/values-ja/strings.xml @@ -1910,7 +1910,7 @@ Language: ja_JP バリエーション属性 モバイルデバイスの Bluetooth をオンにする 注文を取得する際にエラーが発生しました。 アプリの注文状態が古い可能性があります。 - メールクライアントアプリを検出できません + メールクライアントアプリを検出できません %s のレシート 注文を更新する アプリの状態を更新する diff --git a/WooCommerce/src/main/res/values-ko/strings.xml b/WooCommerce/src/main/res/values-ko/strings.xml index 70f0935b61d..f445739f0b5 100644 --- a/WooCommerce/src/main/res/values-ko/strings.xml +++ b/WooCommerce/src/main/res/values-ko/strings.xml @@ -1910,7 +1910,7 @@ Language: ko_KR 변형 속성 모바일 장치 블루투스 켜기 주문을 가져오는 중 오류가 발생했습니다. 앱의 주문 상태가 기한이 지났을 수 있습니다. - 이메일 클라이언트 앱을 감지할 수 없습니다. + 이메일 클라이언트 앱을 감지할 수 없습니다. %s에서 받은 영수증 주문 새로 고치기 앱 상태 업데이트하기 diff --git a/WooCommerce/src/main/res/values-nl/strings.xml b/WooCommerce/src/main/res/values-nl/strings.xml index e53a15e7869..b60a6ba5886 100644 --- a/WooCommerce/src/main/res/values-nl/strings.xml +++ b/WooCommerce/src/main/res/values-nl/strings.xml @@ -1903,7 +1903,7 @@ Language: nl Variatie-eigenschappen Zet de Bluetooth aan op je mobiele apparaat Fout bij ophalen bestelling. De bestellingsstatus in de app kan verouderd zijn. - Detectie van mailclient-app mislukt + Detectie van mailclient-app mislukt Je kwitantie van %s Bestelling aan het vernieuwen… Status in de app aan het bijwerken… diff --git a/WooCommerce/src/main/res/values-pt-rBR/strings.xml b/WooCommerce/src/main/res/values-pt-rBR/strings.xml index 09d637b857d..ecde05b45f3 100644 --- a/WooCommerce/src/main/res/values-pt-rBR/strings.xml +++ b/WooCommerce/src/main/res/values-pt-rBR/strings.xml @@ -1910,7 +1910,7 @@ Language: pt_BR Atributos das variações Ativar o Bluetooth do dispositivo móvel Erro ao buscar o pedido. O status do pedido pode estar desatualizado no aplicativo. - Não é possível detectar seu aplicativo cliente de e-mail + Não é possível detectar seu aplicativo cliente de e-mail Seu recibo de %s Atualizando pedido Atualizando estado do aplicativo diff --git a/WooCommerce/src/main/res/values-ru/strings.xml b/WooCommerce/src/main/res/values-ru/strings.xml index a04d66d7e2f..cc35e4e790d 100644 --- a/WooCommerce/src/main/res/values-ru/strings.xml +++ b/WooCommerce/src/main/res/values-ru/strings.xml @@ -1910,7 +1910,7 @@ Language: ru Атрибуты вариантов Включите Bluetooth на мобильном устройстве Ошибка при загрузке заказа. Состояние заказа в приложении могло устареть. - Не удалось определить ваш почтовый клиент + Не удалось определить ваш почтовый клиент Ваша квитанция от %s Обновление заказа Обновление состояния приложения diff --git a/WooCommerce/src/main/res/values-sv/strings.xml b/WooCommerce/src/main/res/values-sv/strings.xml index 977cd03c403..9e4e1cf2148 100644 --- a/WooCommerce/src/main/res/values-sv/strings.xml +++ b/WooCommerce/src/main/res/values-sv/strings.xml @@ -1917,7 +1917,7 @@ Language: sv_SE Börja sälja idag genom att lägga till din första produkt i butiken. Variationsattribut Aktivera Bluetooth på den mobila enheten - Lyckas inte hitta ditt e-postprogram + Lyckas inte hitta ditt e-postprogram Uppdaterar beställning Uppdaterar appstatusen Det gick inte att hämta beställningen. Beställningens status i appen kan vara föråldrad. diff --git a/WooCommerce/src/main/res/values-tr/strings.xml b/WooCommerce/src/main/res/values-tr/strings.xml index 70eec362196..bf6d75a1ca7 100644 --- a/WooCommerce/src/main/res/values-tr/strings.xml +++ b/WooCommerce/src/main/res/values-tr/strings.xml @@ -1910,7 +1910,7 @@ Language: tr Varyasyon nitelikleri. Mobil cihazda Bluetooth\'u açın Sipariş alınırken hata oluştu Uygulamadaki sipariş durumu eski olabilir. - E-posta istemcisi uygulamanızı tespit edemiyoruz + E-posta istemcisi uygulamanızı tespit edemiyoruz %s makbuzunuz Sipariş yenileniyor Uygulama durumu güncelleniyor diff --git a/WooCommerce/src/main/res/values-zh-rCN/strings.xml b/WooCommerce/src/main/res/values-zh-rCN/strings.xml index 2094348f61f..1a835ed6868 100644 --- a/WooCommerce/src/main/res/values-zh-rCN/strings.xml +++ b/WooCommerce/src/main/res/values-zh-rCN/strings.xml @@ -1909,7 +1909,7 @@ Language: zh_CN 添加产品 变体属性 打开移动设备蓝牙 - 未侦测到您的电子邮件客户端应用 + 未侦测到您的电子邮件客户端应用 来自 %s 的收据 刷新订单 更新应用程序状态 diff --git a/WooCommerce/src/main/res/values-zh-rTW/strings.xml b/WooCommerce/src/main/res/values-zh-rTW/strings.xml index 0c9892b1cae..2dcd4dcbef0 100644 --- a/WooCommerce/src/main/res/values-zh-rTW/strings.xml +++ b/WooCommerce/src/main/res/values-zh-rTW/strings.xml @@ -1910,7 +1910,7 @@ Language: zh_TW 款式屬性 開啟行動裝置的藍牙 擷取訂單時發生錯誤。 應用程式中的訂單狀態可能已過期。 - 無法偵測電子郵件用戶端應用程式 + 無法偵測電子郵件用戶端應用程式 你的 %s 收據 正在重新整理訂單 正在更新應用程式狀態 diff --git a/WooCommerce/src/main/res/values/strings.xml b/WooCommerce/src/main/res/values/strings.xml index df91217db7d..7a855966ae3 100644 --- a/WooCommerce/src/main/res/values/strings.xml +++ b/WooCommerce/src/main/res/values/strings.xml @@ -1507,7 +1507,9 @@ In-Person Payment for Order #%1$s for %2$s blog_id %3$s. Your receipt from %s Thank you for your purchase! Click the link below for your payment receipt.\n\n%s - Can\'t detect your email client app + Unable to detect any application to which the receipt can be shared + Unable to download the receipt + Unable to store the receipt Error fetching order. Order state in the app might be outdated. The order is already paid Please make sure that the card reader is connected. diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModelTest.kt index e0845ba0b96..42bd8adf5e1 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModelTest.kt @@ -75,7 +75,7 @@ class ReceiptPreviewViewModelTest : BaseUnitTest() { @Test fun `when user clicks on send email, then send receipt event emitted`() = testBlocking { - viewModel.onSendEmailClicked() + viewModel.onShareClicked() assertThat(viewModel.event.value).isInstanceOf(SendReceipt::class.java) } @@ -83,7 +83,7 @@ class ReceiptPreviewViewModelTest : BaseUnitTest() { @Test fun `when user clicks on send email, then event tracked`() = testBlocking { - viewModel.onSendEmailClicked() + viewModel.onShareClicked() verify(tracker).track(RECEIPT_EMAIL_TAPPED) } @@ -91,7 +91,7 @@ class ReceiptPreviewViewModelTest : BaseUnitTest() { @Test fun `when email application not found, then SnackBar with error shown`() = testBlocking { - viewModel.onEmailActivityNotFound() + viewModel.onActivityToShareNotFound() assertThat(viewModel.event.value).isInstanceOf(ShowSnackbar::class.java) } @@ -99,7 +99,7 @@ class ReceiptPreviewViewModelTest : BaseUnitTest() { @Test fun `when email application not found, then event tracked`() = testBlocking { - viewModel.onEmailActivityNotFound() + viewModel.onActivityToShareNotFound() verify(tracker).track(RECEIPT_EMAIL_FAILED) } From 63d4cb733b9fc8a50f4c6b6bca983c05a874f5c2 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 30 Jan 2024 10:28:14 +0100 Subject: [PATCH 32/74] Show loading when fetching --- .../ui/payments/receipt/preview/ReceiptPreviewViewModel.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt index f2ae1fcd135..0b9f5ad9116 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt @@ -52,6 +52,8 @@ class ReceiptPreviewViewModel fun onShareClicked() { launch { + viewState.value = Loading + tracker.track(RECEIPT_EMAIL_TAPPED) when (val sharingResult = paymentReceiptShare(args.receiptUrl)) { PaymentReceiptShare.ReceiptShareResult.Error.FileCreation -> { @@ -85,6 +87,8 @@ class ReceiptPreviewViewModel // no-op } } + + viewState.value = Content } } From 1c2b05e5cd05e745195ab95f324a5e3aa18dec7a Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 30 Jan 2024 10:32:28 +0100 Subject: [PATCH 33/74] Removed SendReceipt event --- .../ui/payments/receipt/preview/ReceiptPreviewFragment.kt | 8 -------- .../receipt/preview/ReceiptPreviewViewModelEvent.kt | 3 --- 2 files changed, 11 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewFragment.kt index e6da1500a36..30753e17d69 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewFragment.kt @@ -14,7 +14,6 @@ import com.woocommerce.android.analytics.AnalyticsTracker import com.woocommerce.android.databinding.FragmentReceiptPreviewBinding import com.woocommerce.android.ui.base.BaseFragment import com.woocommerce.android.ui.base.UIMessageResolver -import com.woocommerce.android.util.ActivityUtils import com.woocommerce.android.util.PrintHtmlHelper import com.woocommerce.android.util.UiHelpers import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ShowSnackbar @@ -102,16 +101,9 @@ class ReceiptPreviewFragment : BaseFragment(R.layout.fragment_receipt_preview), when (it) { is LoadUrl -> binding.receiptPreviewPreviewWebview.loadUrl(it.url) is PrintReceipt -> printHtmlHelper.printReceipt(requireActivity(), it.receiptUrl, it.documentName) - is SendReceipt -> composeEmail(it) is ShowSnackbar -> uiMessageResolver.showSnack(it.message) else -> it.isHandled = false } } } - - private fun composeEmail(event: SendReceipt) { - ActivityUtils.sendEmail(requireActivity(), event.address, event.subject, event.content) { - viewModel.onActivityToShareNotFound() - } - } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModelEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModelEvent.kt index 5edf89f0559..de027028483 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModelEvent.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModelEvent.kt @@ -1,10 +1,7 @@ package com.woocommerce.android.ui.payments.receipt.preview -import com.woocommerce.android.model.UiString import com.woocommerce.android.viewmodel.MultiLiveEvent data class LoadUrl(val url: String) : MultiLiveEvent.Event() data class PrintReceipt(val receiptUrl: String, val documentName: String) : MultiLiveEvent.Event() - -data class SendReceipt(val content: UiString, val subject: UiString, val address: String) : MultiLiveEvent.Event() From ef7c5ac0c0ce784fb3a337d56cd202aad7b13d67 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 30 Jan 2024 10:53:20 +0100 Subject: [PATCH 34/74] Put order number in the name of a file --- .../android/ui/payments/receipt/PaymentReceiptShare.kt | 4 ++-- .../ui/payments/receipt/preview/ReceiptPreviewViewModel.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptShare.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptShare.kt index 4fd8d553bd9..dfe8f406c7d 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptShare.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptShare.kt @@ -13,11 +13,11 @@ class PaymentReceiptShare @Inject constructor( private val fileDownloader: FileDownloader, private val context: Application, ) { - suspend operator fun invoke(receiptUrl: String): ReceiptShareResult { + suspend operator fun invoke(receiptUrl: String, orderNumber: Long): ReceiptShareResult { val receiptFile = fileUtils.createTempTimeStampedFile( storageDir = context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS) ?: context.filesDir, - prefix = "receipt", + prefix = "receipt_$orderNumber", fileExtension = "html" ) ?: return ReceiptShareResult.Error.FileCreation if (!fileDownloader.downloadFile(receiptUrl, receiptFile)) { diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt index 0b9f5ad9116..3262dc142dd 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt @@ -55,7 +55,7 @@ class ReceiptPreviewViewModel viewState.value = Loading tracker.track(RECEIPT_EMAIL_TAPPED) - when (val sharingResult = paymentReceiptShare(args.receiptUrl)) { + when (val sharingResult = paymentReceiptShare(args.receiptUrl, args.orderId)) { PaymentReceiptShare.ReceiptShareResult.Error.FileCreation -> { tracker.track( RECEIPT_EMAIL_FAILED, From 8b52b5326aa907385f69b3fe64253d0f27daf0bd Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 30 Jan 2024 11:39:38 +0100 Subject: [PATCH 35/74] Share via file after card payment --- .../CardReaderPaymentDialogFragment.kt | 9 ---- .../payment/CardReaderPaymentViewModel.kt | 43 +++++++++++-------- .../CardReaderPaymentViewModelEvent.kt | 3 -- .../payment/CardReaderPaymentViewState.kt | 9 ++++ .../payments/tracking/PaymentsFlowTracker.kt | 27 ++++++++++++ 5 files changed, 62 insertions(+), 29 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentDialogFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentDialogFragment.kt index a317c226d28..ae5b3ce8354 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentDialogFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentDialogFragment.kt @@ -22,7 +22,6 @@ import com.woocommerce.android.R import com.woocommerce.android.analytics.AnalyticsTracker import com.woocommerce.android.databinding.CardReaderPaymentDialogBinding import com.woocommerce.android.extensions.navigateBackWithNotice -import com.woocommerce.android.model.UiString import com.woocommerce.android.support.help.HelpOrigin import com.woocommerce.android.support.requests.SupportRequestFormActivity import com.woocommerce.android.ui.base.UIMessageResolver @@ -32,7 +31,6 @@ import com.woocommerce.android.ui.payments.cardreader.payment.ViewState.BuiltInR import com.woocommerce.android.ui.payments.cardreader.payment.ViewState.ExternalReaderPaymentSuccessfulReceiptSentAutomaticallyState import com.woocommerce.android.ui.payments.cardreader.payment.ViewState.ExternalReaderPaymentSuccessfulState import com.woocommerce.android.ui.payments.refunds.RefundSummaryFragment.Companion.KEY_INTERAC_SUCCESS -import com.woocommerce.android.util.ActivityUtils import com.woocommerce.android.util.PrintHtmlHelper import com.woocommerce.android.util.UiHelpers import com.woocommerce.android.util.UiHelpers.getTextOfUiString @@ -86,7 +84,6 @@ class CardReaderPaymentDialogFragment : PaymentsBaseDialogFragment(R.layout.card event.documentName ) InteracRefundSuccessful -> navigateBackWithNotice(KEY_INTERAC_SUCCESS) - is SendReceipt -> composeEmail(event.address, event.subject, event.content) is ShowSnackbar -> uiMessageResolver.showSnack(event.message) is ShowSnackbarInDialog -> Snackbar.make( requireView(), event.message, BaseTransientBottomBar.LENGTH_LONG @@ -186,12 +183,6 @@ class CardReaderPaymentDialogFragment : PaymentsBaseDialogFragment(R.layout.card mp.start() } - private fun composeEmail(address: String, subject: UiString, content: UiString) { - ActivityUtils.sendEmail(requireActivity(), address, subject, content) { - viewModel.onEmailActivityNotFound() - } - } - override fun onResume() { super.onResume() AnalyticsTracker.trackViewShown(this) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModel.kt index 711062c7a6b..fc52388c3f9 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModel.kt @@ -68,6 +68,7 @@ import com.woocommerce.android.ui.payments.cardreader.payment.ViewState.ReFetchi import com.woocommerce.android.ui.payments.cardreader.payment.ViewState.RefundLoadingDataState import com.woocommerce.android.ui.payments.cardreader.payment.ViewState.RefundSuccessfulState import com.woocommerce.android.ui.payments.receipt.PaymentReceiptHelper +import com.woocommerce.android.ui.payments.receipt.PaymentReceiptShare import com.woocommerce.android.ui.payments.tracking.CardReaderTrackingInfoKeeper import com.woocommerce.android.ui.payments.tracking.PaymentsFlowTracker import com.woocommerce.android.util.CoroutineDispatchers @@ -117,6 +118,7 @@ class CardReaderPaymentViewModel private val paymentReceiptHelper: PaymentReceiptHelper, private val cardReaderOnboardingChecker: CardReaderOnboardingChecker, private val cardReaderConfigProvider: CardReaderCountryConfigProvider, + private val paymentReceiptShare: PaymentReceiptShare, ) : ScopedViewModel(savedState) { private val arguments: CardReaderPaymentDialogFragmentArgs by savedState.navArgs() @@ -611,9 +613,7 @@ class CardReaderPaymentViewModel val onSaveUserClicked = { onSaveForLaterClicked() } - val onSendReceiptClicked = { - onSendReceiptClicked(order.billingAddress.email) - } + val onSendReceiptClicked = { onSendReceiptClicked() } if (order.billingAddress.email.isBlank()) { viewState.postValue( @@ -723,27 +723,36 @@ class CardReaderPaymentViewModel } } - private fun onSendReceiptClicked(billingEmail: String) { + private fun onSendReceiptClicked() { launch { tracker.trackEmailReceiptTapped() + val stateBeforeLoading = viewState.value!! + viewState.postValue(ViewState.SharingReceiptState) val receiptResult = paymentReceiptHelper.getReceiptUrl(orderId) + if (receiptResult.isSuccess) { - triggerEvent( - SendReceipt( - content = UiStringRes( - R.string.card_reader_payment_receipt_email_content, - listOf(UiStringText(receiptResult.getOrThrow())) - ), - subject = UiStringRes( - R.string.card_reader_payment_receipt_email_subject, - listOf(UiStringText(selectedSite.get().name.orEmpty())) - ), - address = billingEmail - ) - ) + when (val sharingResult = paymentReceiptShare(receiptResult.getOrThrow(), orderId)) { + is PaymentReceiptShare.ReceiptShareResult.Error.FileCreation -> { + tracker.trackPaymentsReceiptSharingFailed(sharingResult) + triggerEvent(ShowSnackbar(R.string.card_reader_payment_receipt_can_not_be_stored)) + } + is PaymentReceiptShare.ReceiptShareResult.Error.FileDownload -> { + tracker.trackPaymentsReceiptSharingFailed(sharingResult) + triggerEvent(ShowSnackbar(R.string.card_reader_payment_receipt_can_not_be_downloaded)) + } + is PaymentReceiptShare.ReceiptShareResult.Error.Sharing -> { + tracker.trackPaymentsReceiptSharingFailed(sharingResult) + triggerEvent(ShowSnackbar(R.string.card_reader_payment_receipt_app_to_share_not_found)) + } + PaymentReceiptShare.ReceiptShareResult.Success -> { + // no-op + } + } } else { triggerEvent(ShowSnackbar(R.string.receipt_fetching_error)) } + + viewState.postValue(stateBeforeLoading) } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModelEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModelEvent.kt index 5a44a75ebb3..6ac68063777 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModelEvent.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModelEvent.kt @@ -1,7 +1,6 @@ package com.woocommerce.android.ui.payments.cardreader.payment import androidx.annotation.StringRes -import com.woocommerce.android.model.UiString import com.woocommerce.android.viewmodel.MultiLiveEvent.Event class ShowSnackbarInDialog(@StringRes val message: Int) : Event() @@ -17,5 +16,3 @@ object EnableNfc : Event() data class PurchaseCardReader(val url: String) : Event() data class PrintReceipt(val receiptUrl: String, val documentName: String) : Event() - -data class SendReceipt(val content: UiString, val subject: UiString, val address: String) : Event() diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewState.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewState.kt index d7eaa0c5446..1f73c0126df 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewState.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewState.kt @@ -219,6 +219,15 @@ sealed class ViewState( override val isProgressVisible = true } + object SharingReceiptState : ViewState( + headerLabel = R.string.card_reader_payment_completed_payment_header, + illustration = null, + primaryActionLabel = null, + secondaryActionLabel = null, + ) { + override val isProgressVisible = true + } + object ReFetchingOrderState : ViewState( headerLabel = R.string.card_reader_payment_fetch_order_loading_header, hintLabel = R.string.card_reader_payment_fetch_order_loading_hint, diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt index 0e7b2739a57..9cdce47de8a 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt @@ -73,6 +73,7 @@ import com.woocommerce.android.ui.payments.cardreader.onboarding.PluginType import com.woocommerce.android.ui.payments.cardreader.onboarding.PluginType.STRIPE_EXTENSION_GATEWAY import com.woocommerce.android.ui.payments.cardreader.onboarding.PluginType.WOOCOMMERCE_PAYMENTS import com.woocommerce.android.ui.payments.hub.PaymentsHubViewModel.CashOnDeliverySource +import com.woocommerce.android.ui.payments.receipt.PaymentReceiptShare import com.woocommerce.android.ui.payments.taptopay.TapToPayAvailabilityStatus.Result.NotAvailable import javax.inject.Inject @@ -580,6 +581,32 @@ class PaymentsFlowTracker @Inject constructor( ) } + fun trackPaymentsReceiptSharingFailed(sharingResult: PaymentReceiptShare.ReceiptShareResult.Error) { + when (sharingResult) { + is PaymentReceiptShare.ReceiptShareResult.Error.FileCreation -> { + track( + RECEIPT_EMAIL_FAILED, + errorType = "file_creation_failed", + errorDescription = "File creation failed" + ) + } + is PaymentReceiptShare.ReceiptShareResult.Error.FileDownload -> { + track( + RECEIPT_EMAIL_FAILED, + errorType = "file_download_failed", + errorDescription = "File download failed" + ) + } + is PaymentReceiptShare.ReceiptShareResult.Error.Sharing -> { + track( + RECEIPT_EMAIL_FAILED, + errorType = "no_app_found", + errorDescription = sharingResult.exception.message + ) + } + } + } + private fun getAndResetFlowsDuration(): MutableMap { val result = mutableMapOf() .also { mutableMap -> From 0a299f8c750dee683734fef066f4dc86670a782b Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 30 Jan 2024 11:42:29 +0100 Subject: [PATCH 36/74] Use payment flow tracker to track sharing result --- .../preview/ReceiptPreviewViewModel.kt | 28 +++++-------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt index 3262dc142dd..ed7aa503ec5 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt @@ -4,7 +4,6 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.SavedStateHandle import com.woocommerce.android.R.string -import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_EMAIL_FAILED import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_EMAIL_TAPPED import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_CANCELED import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_FAILED @@ -14,6 +13,7 @@ import com.woocommerce.android.analytics.AnalyticsTrackerWrapper import com.woocommerce.android.ui.payments.receipt.PaymentReceiptShare import com.woocommerce.android.ui.payments.receipt.preview.ReceiptPreviewViewModel.ReceiptPreviewViewState.Content import com.woocommerce.android.ui.payments.receipt.preview.ReceiptPreviewViewModel.ReceiptPreviewViewState.Loading +import com.woocommerce.android.ui.payments.tracking.PaymentsFlowTracker import com.woocommerce.android.util.PrintHtmlHelper.PrintJobResult import com.woocommerce.android.util.PrintHtmlHelper.PrintJobResult.CANCELLED import com.woocommerce.android.util.PrintHtmlHelper.PrintJobResult.FAILED @@ -30,6 +30,7 @@ class ReceiptPreviewViewModel @Inject constructor( savedState: SavedStateHandle, private val tracker: AnalyticsTrackerWrapper, + private val paymentsFlowTracker: PaymentsFlowTracker, private val paymentReceiptShare: PaymentReceiptShare, ) : ScopedViewModel(savedState) { private val args: ReceiptPreviewFragmentArgs by savedState.navArgs() @@ -56,31 +57,16 @@ class ReceiptPreviewViewModel tracker.track(RECEIPT_EMAIL_TAPPED) when (val sharingResult = paymentReceiptShare(args.receiptUrl, args.orderId)) { - PaymentReceiptShare.ReceiptShareResult.Error.FileCreation -> { - tracker.track( - RECEIPT_EMAIL_FAILED, - errorContext = this@ReceiptPreviewViewModel.javaClass.simpleName, - errorType = "file_creation_failed", - errorDescription = "File creation failed" - ) + is PaymentReceiptShare.ReceiptShareResult.Error.FileCreation -> { + paymentsFlowTracker.trackPaymentsReceiptSharingFailed(sharingResult) triggerEvent(ShowSnackbar(string.card_reader_payment_receipt_can_not_be_stored)) } - PaymentReceiptShare.ReceiptShareResult.Error.FileDownload -> { - tracker.track( - RECEIPT_EMAIL_FAILED, - errorContext = this@ReceiptPreviewViewModel.javaClass.simpleName, - errorType = "file_download_failed", - errorDescription = "File download failed" - ) + is PaymentReceiptShare.ReceiptShareResult.Error.FileDownload -> { + paymentsFlowTracker.trackPaymentsReceiptSharingFailed(sharingResult) triggerEvent(ShowSnackbar(string.card_reader_payment_receipt_can_not_be_downloaded)) } is PaymentReceiptShare.ReceiptShareResult.Error.Sharing -> { - tracker.track( - RECEIPT_EMAIL_FAILED, - errorContext = this@ReceiptPreviewViewModel.javaClass.simpleName, - errorType = "no_app_found", - errorDescription = sharingResult.exception.message - ) + paymentsFlowTracker.trackPaymentsReceiptSharingFailed(sharingResult) triggerEvent(ShowSnackbar(string.card_reader_payment_receipt_app_to_share_not_found)) } PaymentReceiptShare.ReceiptShareResult.Success -> { From e3874e745ef88767a063b86cd966cc848125961b Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 30 Jan 2024 11:59:49 +0100 Subject: [PATCH 37/74] Set receipt expiration days to 2 as agreed --- .../android/ui/payments/receipt/PaymentReceiptHelper.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelper.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelper.kt index 53dea128b02..8832e65cfeb 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelper.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelper.kt @@ -110,7 +110,7 @@ class PaymentReceiptHelper @Inject constructor( const val WCPAY_RECEIPTS_SENDING_SUPPORT_VERSION = "4.0.0" const val WC_CAN_GENERATE_RECEIPTS_VERSION = "8.7.0" - const val RECEIPT_EXPIRATION_DAYS = 365 + const val RECEIPT_EXPIRATION_DAYS = 2 } class IsDevSiteSupported @Inject constructor() { From aaf4a97e09c7f395ca79048df5cf8c5c304d55d6 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 30 Jan 2024 12:02:12 +0100 Subject: [PATCH 38/74] Fixed detekt complains --- .../payments/receipt/PaymentReceiptShare.kt | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptShare.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptShare.kt index dfe8f406c7d..d2a1c502470 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptShare.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptShare.kt @@ -13,35 +13,38 @@ class PaymentReceiptShare @Inject constructor( private val fileDownloader: FileDownloader, private val context: Application, ) { + @Suppress("TooGenericExceptionCaught") suspend operator fun invoke(receiptUrl: String, orderNumber: Long): ReceiptShareResult { val receiptFile = fileUtils.createTempTimeStampedFile( storageDir = context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS) ?: context.filesDir, prefix = "receipt_$orderNumber", fileExtension = "html" - ) ?: return ReceiptShareResult.Error.FileCreation - if (!fileDownloader.downloadFile(receiptUrl, receiptFile)) { - return ReceiptShareResult.Error.FileDownload - } - - val uri = FileProvider.getUriForFile( - context, - context.packageName + ".provider", - receiptFile ) - val intent = Intent(Intent.ACTION_SEND).apply { - type = "application/*" - putExtra(Intent.EXTRA_STREAM, uri) - } - return try { - context.startActivity( - Intent.createChooser(intent, null).apply { - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - } + return if (receiptFile == null) { + ReceiptShareResult.Error.FileCreation + } else if (!fileDownloader.downloadFile(receiptUrl, receiptFile)) { + ReceiptShareResult.Error.FileDownload + } else { + val uri = FileProvider.getUriForFile( + context, + context.packageName + ".provider", + receiptFile ) - ReceiptShareResult.Success - } catch (e: Exception) { - ReceiptShareResult.Error.Sharing(e) + val intent = Intent(Intent.ACTION_SEND).apply { + type = "application/*" + putExtra(Intent.EXTRA_STREAM, uri) + } + try { + context.startActivity( + Intent.createChooser(intent, null).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + } + ) + ReceiptShareResult.Success + } catch (e: Exception) { + ReceiptShareResult.Error.Sharing(e) + } } } From dee354de185efb50da67a6fa8cd5ce10941b73b4 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 30 Jan 2024 12:27:36 +0100 Subject: [PATCH 39/74] Tests a compilable --- .../CardReaderPaymentViewModelTest.kt | 17 ++++++---- .../receipt/PaymentReceiptShareTest.kt | 27 ++++++++++++++++ .../preview/ReceiptPreviewViewModelTest.kt | 32 +++---------------- 3 files changed, 43 insertions(+), 33 deletions(-) create mode 100644 WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptShareTest.kt diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/cardreader/CardReaderPaymentViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/cardreader/CardReaderPaymentViewModelTest.kt index 74bab73e34a..0bbf49ce280 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/cardreader/CardReaderPaymentViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/cardreader/CardReaderPaymentViewModelTest.kt @@ -68,7 +68,6 @@ import com.woocommerce.android.ui.payments.cardreader.payment.PaymentFlowError.U import com.woocommerce.android.ui.payments.cardreader.payment.PlayChaChing import com.woocommerce.android.ui.payments.cardreader.payment.PrintReceipt import com.woocommerce.android.ui.payments.cardreader.payment.PurchaseCardReader -import com.woocommerce.android.ui.payments.cardreader.payment.SendReceipt import com.woocommerce.android.ui.payments.cardreader.payment.ViewState.BuiltInReaderCapturingPaymentState import com.woocommerce.android.ui.payments.cardreader.payment.ViewState.BuiltInReaderCollectPaymentState import com.woocommerce.android.ui.payments.cardreader.payment.ViewState.BuiltInReaderFailedPaymentState @@ -90,6 +89,7 @@ import com.woocommerce.android.ui.payments.cardreader.payment.ViewState.ReFetchi import com.woocommerce.android.ui.payments.cardreader.payment.ViewState.RefundLoadingDataState import com.woocommerce.android.ui.payments.cardreader.payment.ViewState.RefundSuccessfulState import com.woocommerce.android.ui.payments.receipt.PaymentReceiptHelper +import com.woocommerce.android.ui.payments.receipt.PaymentReceiptShare import com.woocommerce.android.ui.payments.tracking.CardReaderTrackingInfoKeeper import com.woocommerce.android.ui.payments.tracking.PaymentsFlowTracker import com.woocommerce.android.util.CurrencyFormatter @@ -182,6 +182,7 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() { private val cardReaderOnboardingChecker: CardReaderOnboardingChecker = mock() private val cardReaderConfigProvider: CardReaderCountryConfigProvider = mock() private val cardReaderConfig: CardReaderConfigForSupportedCountry = CardReaderConfigForUSA + private val paymentReceiptShare: PaymentReceiptShare = mock() @Suppress("LongMethod") @Before @@ -209,6 +210,7 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() { paymentReceiptHelper = paymentReceiptHelper, cardReaderOnboardingChecker = cardReaderOnboardingChecker, cardReaderConfigProvider = cardReaderConfigProvider, + paymentReceiptShare = paymentReceiptShare, ) whenever(orderRepository.getOrderById(any())).thenReturn(mockedOrder) @@ -2356,16 +2358,19 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() { } @Test - fun `given external reader and receipt fetching success, when user clicks on send receipt button, then SendReceipt event emitted`() = + fun `given external reader and receipt fetching and sharing success, when user clicks on send receipt button, then nothing emitted`() = testBlocking { whenever(cardReaderManager.collectPayment(any())).thenAnswer { - flow { emit(PaymentCompleted("")) } + flow { emit(PaymentCompleted("url")) } } + whenever(paymentReceiptShare("url", 1L)).thenReturn( + PaymentReceiptShare.ReceiptShareResult.Success + ) viewModel.start() (viewModel.viewStateData.value as ExternalReaderPaymentSuccessfulState).onSecondaryActionClicked.invoke() - assertThat(viewModel.event.value).isInstanceOf(SendReceipt::class.java) + assertThat(viewModel.event.value).isNull() } @Test @@ -2380,8 +2385,6 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() { viewModel.start() (viewModel.viewStateData.value as BuiltInReaderPaymentSuccessfulState).onSecondaryActionClicked.invoke() - - assertThat(viewModel.event.value).isInstanceOf(SendReceipt::class.java) } @Test @@ -4401,6 +4404,7 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() { paymentReceiptHelper = paymentReceiptHelper, cardReaderOnboardingChecker = cardReaderOnboardingChecker, cardReaderConfigProvider = cardReaderConfigProvider, + paymentReceiptShare = paymentReceiptShare, ) } @@ -4433,6 +4437,7 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() { paymentReceiptHelper = paymentReceiptHelper, cardReaderOnboardingChecker = cardReaderOnboardingChecker, cardReaderConfigProvider = cardReaderConfigProvider, + paymentReceiptShare = paymentReceiptShare, ) } } diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptShareTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptShareTest.kt new file mode 100644 index 00000000000..327879d2c3b --- /dev/null +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptShareTest.kt @@ -0,0 +1,27 @@ +package com.woocommerce.android.ui.payments.receipt + +import android.app.Application +import com.woocommerce.android.media.FileUtils +import com.woocommerce.android.util.FileDownloader +import com.woocommerce.android.viewmodel.BaseUnitTest +import kotlinx.coroutines.ExperimentalCoroutinesApi +import org.junit.Test +import org.mockito.kotlin.mock + +@ExperimentalCoroutinesApi +class PaymentReceiptShareTest : BaseUnitTest() { + private val fileUtils: FileUtils = mock() + private val fileDownloader: FileDownloader = mock() + private val context: Application = mock() + + private val sut = PaymentReceiptShare( + fileUtils = fileUtils, + fileDownloader = fileDownloader, + context = context, + ) + + @Test + fun `test receipt share`() = testBlocking { + sut.invoke("receiptUrl", 123L) + } +} diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModelTest.kt index 42bd8adf5e1..3b26237b967 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModelTest.kt @@ -1,7 +1,6 @@ package com.woocommerce.android.ui.payments.receipt.preview import androidx.lifecycle.SavedStateHandle -import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_EMAIL_FAILED import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_EMAIL_TAPPED import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_CANCELED import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_FAILED @@ -9,13 +8,14 @@ import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_SUCCESS import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_TAPPED import com.woocommerce.android.analytics.AnalyticsTrackerWrapper import com.woocommerce.android.tools.SelectedSite +import com.woocommerce.android.ui.payments.receipt.PaymentReceiptShare import com.woocommerce.android.ui.payments.receipt.preview.ReceiptPreviewViewModel.ReceiptPreviewViewState.Content import com.woocommerce.android.ui.payments.receipt.preview.ReceiptPreviewViewModel.ReceiptPreviewViewState.Loading +import com.woocommerce.android.ui.payments.tracking.PaymentsFlowTracker import com.woocommerce.android.util.PrintHtmlHelper.PrintJobResult.CANCELLED import com.woocommerce.android.util.PrintHtmlHelper.PrintJobResult.FAILED import com.woocommerce.android.util.PrintHtmlHelper.PrintJobResult.STARTED import com.woocommerce.android.viewmodel.BaseUnitTest -import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ShowSnackbar import kotlinx.coroutines.ExperimentalCoroutinesApi import org.assertj.core.api.Assertions.assertThat import org.junit.Before @@ -31,6 +31,8 @@ class ReceiptPreviewViewModelTest : BaseUnitTest() { private val selectedSite: SelectedSite = mock() private val tracker: AnalyticsTrackerWrapper = mock() + private val paymentsFlowTracker: PaymentsFlowTracker = mock() + private val paymentReceiptShare: PaymentReceiptShare = mock() private val savedState: SavedStateHandle = ReceiptPreviewFragmentArgs( receiptUrl = "testing url", @@ -40,7 +42,7 @@ class ReceiptPreviewViewModelTest : BaseUnitTest() { @Before fun setUp() { - viewModel = ReceiptPreviewViewModel(savedState, tracker, selectedSite) + viewModel = ReceiptPreviewViewModel(savedState, tracker, paymentsFlowTracker, paymentReceiptShare) whenever(selectedSite.get()).thenReturn(SiteModel().apply { name = "testName" }) } @@ -72,14 +74,6 @@ class ReceiptPreviewViewModelTest : BaseUnitTest() { assertThat(contentState.isContentVisible).isTrue } - @Test - fun `when user clicks on send email, then send receipt event emitted`() = - testBlocking { - viewModel.onShareClicked() - - assertThat(viewModel.event.value).isInstanceOf(SendReceipt::class.java) - } - @Test fun `when user clicks on send email, then event tracked`() = testBlocking { @@ -88,22 +82,6 @@ class ReceiptPreviewViewModelTest : BaseUnitTest() { verify(tracker).track(RECEIPT_EMAIL_TAPPED) } - @Test - fun `when email application not found, then SnackBar with error shown`() = - testBlocking { - viewModel.onActivityToShareNotFound() - - assertThat(viewModel.event.value).isInstanceOf(ShowSnackbar::class.java) - } - - @Test - fun `when email application not found, then event tracked`() = - testBlocking { - viewModel.onActivityToShareNotFound() - - verify(tracker).track(RECEIPT_EMAIL_FAILED) - } - @Test fun `when user clicks on print receipt, then print receipt event emitted`() = testBlocking { From 6bda786190f1cbe96800e8b29179aa16a0562051 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 30 Jan 2024 12:39:22 +0100 Subject: [PATCH 40/74] Partial testing for PaymentReceiptShare --- .../receipt/PaymentReceiptShareTest.kt | 47 +++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptShareTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptShareTest.kt index 327879d2c3b..966918a2284 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptShareTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptShareTest.kt @@ -5,14 +5,23 @@ import com.woocommerce.android.media.FileUtils import com.woocommerce.android.util.FileDownloader import com.woocommerce.android.viewmodel.BaseUnitTest import kotlinx.coroutines.ExperimentalCoroutinesApi +import org.assertj.core.api.Assertions.assertThat import org.junit.Test +import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.eq import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever +import java.io.File @ExperimentalCoroutinesApi class PaymentReceiptShareTest : BaseUnitTest() { private val fileUtils: FileUtils = mock() private val fileDownloader: FileDownloader = mock() - private val context: Application = mock() + + private val file: File = mock() + private val context: Application = mock { + on { getExternalFilesDir(anyOrNull()) }.thenReturn(file) + } private val sut = PaymentReceiptShare( fileUtils = fileUtils, @@ -21,7 +30,39 @@ class PaymentReceiptShareTest : BaseUnitTest() { ) @Test - fun `test receipt share`() = testBlocking { - sut.invoke("receiptUrl", 123L) + fun `given file not created, when invoke, then FileCreation error returned`() = testBlocking { + // GIVEN + whenever( + fileUtils.createTempTimeStampedFile( + storageDir = anyOrNull(), + prefix = eq("receipt_999"), + fileExtension = eq("html"), + ) + ).thenReturn(null) + + // WHEN + val result = sut("receiptUrl", 999L) + + // THEN + assertThat(result).isInstanceOf(PaymentReceiptShare.ReceiptShareResult.Error.FileCreation::class.java) + } + + @Test + fun `given file created but not downloaded, when invoke, then FileDownload error returned`() = testBlocking { + // GIVEN + whenever( + fileUtils.createTempTimeStampedFile( + storageDir = anyOrNull(), + prefix = eq("receipt_999"), + fileExtension = eq("html"), + ) + ).thenReturn(file) + whenever(fileDownloader.downloadFile(eq("receiptUrl"), eq(file))).thenReturn(false) + + // WHEN + val result = sut("receiptUrl", 999L) + + // THEN + assertThat(result).isInstanceOf(PaymentReceiptShare.ReceiptShareResult.Error.FileDownload::class.java) } } From 207803de8676b8d4373f88c19618e7c25e513f49 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 30 Jan 2024 14:37:56 +0100 Subject: [PATCH 41/74] Test for CardReaderPaymentViewModel --- .../CardReaderPaymentViewModelTest.kt | 66 +++++++++++++++++-- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/cardreader/CardReaderPaymentViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/cardreader/CardReaderPaymentViewModelTest.kt index 0bbf49ce280..a9cbfa61c5f 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/cardreader/CardReaderPaymentViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/cardreader/CardReaderPaymentViewModelTest.kt @@ -2358,23 +2358,23 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() { } @Test - fun `given external reader and receipt fetching and sharing success, when user clicks on send receipt button, then nothing emitted`() = + fun `given external reader and receipt fetching and sharing success, when user clicks on send receipt button, then PlayChaChing emitted`() = testBlocking { whenever(cardReaderManager.collectPayment(any())).thenAnswer { flow { emit(PaymentCompleted("url")) } } - whenever(paymentReceiptShare("url", 1L)).thenReturn( + whenever(paymentReceiptShare("test url", 1L)).thenReturn( PaymentReceiptShare.ReceiptShareResult.Success ) viewModel.start() (viewModel.viewStateData.value as ExternalReaderPaymentSuccessfulState).onSecondaryActionClicked.invoke() - assertThat(viewModel.event.value).isNull() + assertThat(viewModel.event.value).isEqualTo(PlayChaChing) } @Test - fun `given built in reader and receipt fetching success, when user clicks on send receipt button, then SendReceipt event emitted`() = + fun `given built in reader and receipt fetching and sharing success, when user clicks on send receipt button, then PlayChaChing emitted`() = testBlocking { whenever(cardReaderManager.collectPayment(any())).thenAnswer { flow { emit(PaymentCompleted("")) } @@ -2385,6 +2385,64 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() { viewModel.start() (viewModel.viewStateData.value as BuiltInReaderPaymentSuccessfulState).onSecondaryActionClicked.invoke() + + assertThat(viewModel.event.value).isEqualTo(PlayChaChing) + } + + @Test + fun `given receipt fetching success and receipt file not created, when user clicks on send receipt button, then ShowSnackbar emitted`() = + testBlocking { + whenever(cardReaderManager.collectPayment(any())).thenAnswer { + flow { emit(PaymentCompleted("url")) } + } + whenever(paymentReceiptShare("test url", 1L)).thenReturn( + PaymentReceiptShare.ReceiptShareResult.Error.FileCreation + ) + viewModel.start() + + (viewModel.viewStateData.value as ExternalReaderPaymentSuccessfulState).onSecondaryActionClicked.invoke() + + assertThat((viewModel.event.value as ShowSnackbar).message).isEqualTo( + R.string.card_reader_payment_receipt_can_not_be_stored + ) + verify(tracker).trackPaymentsReceiptSharingFailed(PaymentReceiptShare.ReceiptShareResult.Error.FileCreation) + } + + @Test + fun `given receipt fetching success and receipt file not downloaded, when user clicks on send receipt button, then ShowSnackbar emitted`() = + testBlocking { + whenever(cardReaderManager.collectPayment(any())).thenAnswer { + flow { emit(PaymentCompleted("url")) } + } + whenever(paymentReceiptShare("test url", 1L)).thenReturn( + PaymentReceiptShare.ReceiptShareResult.Error.FileDownload + ) + viewModel.start() + + (viewModel.viewStateData.value as ExternalReaderPaymentSuccessfulState).onSecondaryActionClicked.invoke() + + assertThat((viewModel.event.value as ShowSnackbar).message).isEqualTo( + R.string.card_reader_payment_receipt_can_not_be_downloaded + ) + verify(tracker).trackPaymentsReceiptSharingFailed(PaymentReceiptShare.ReceiptShareResult.Error.FileDownload) + } + + @Test + fun `given receipt fetching success and receipt file not shared, when user clicks on send receipt button, then ShowSnackbar emitted`() = + testBlocking { + whenever(cardReaderManager.collectPayment(any())).thenAnswer { + flow { emit(PaymentCompleted("url")) } + } + val sharing = PaymentReceiptShare.ReceiptShareResult.Error.Sharing(Exception()) + whenever(paymentReceiptShare("test url", 1L)).thenReturn(sharing) + viewModel.start() + + (viewModel.viewStateData.value as ExternalReaderPaymentSuccessfulState).onSecondaryActionClicked.invoke() + + assertThat((viewModel.event.value as ShowSnackbar).message).isEqualTo( + R.string.card_reader_payment_receipt_app_to_share_not_found + ) + verify(tracker).trackPaymentsReceiptSharingFailed(sharing) } @Test From c2d1be0b728be040cef9a0a5ce69988c302ac8ad Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 30 Jan 2024 14:39:25 +0100 Subject: [PATCH 42/74] Fixed PaymentReceiptHelperTest --- .../ui/payments/receipt/PaymentReceiptHelperTest.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelperTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelperTest.kt index 8aa5087bcc1..18b25902f08 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelperTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelperTest.kt @@ -102,7 +102,7 @@ class PaymentReceiptHelperTest : BaseUnitTest() { WooCommerceStore.WooPlugin.WOO_CORE ) ).thenReturn(plugin) - whenever(orderStore.fetchOrdersReceipt(site, 1, expirationDays = 365)).thenReturn( + whenever(orderStore.fetchOrdersReceipt(site, 1, expirationDays = 2)).thenReturn( WooPayload(OrderReceiptResponse("url", "date")) ) @@ -126,7 +126,7 @@ class PaymentReceiptHelperTest : BaseUnitTest() { WooCommerceStore.WooPlugin.WOO_CORE ) ).thenReturn(plugin) - whenever(orderStore.fetchOrdersReceipt(site, 1, expirationDays = 365)).thenReturn( + whenever(orderStore.fetchOrdersReceipt(site, 1, expirationDays = 2)).thenReturn( WooPayload( WooError( type = WooErrorType.API_ERROR, @@ -156,7 +156,7 @@ class PaymentReceiptHelperTest : BaseUnitTest() { selectedSite.get(), ) ).thenReturn(listOf(plugin)) - whenever(orderStore.fetchOrdersReceipt(site, 1, expirationDays = 365)).thenReturn( + whenever(orderStore.fetchOrdersReceipt(site, 1, expirationDays = 2)).thenReturn( WooPayload( WooError( type = WooErrorType.API_ERROR, @@ -187,7 +187,7 @@ class PaymentReceiptHelperTest : BaseUnitTest() { selectedSite.get(), ) ).thenReturn(listOf(plugin)) - whenever(orderStore.fetchOrdersReceipt(site, 1, expirationDays = 365)).thenReturn( + whenever(orderStore.fetchOrdersReceipt(site, 1, expirationDays = 2)).thenReturn( WooPayload(OrderReceiptResponse("url", "date")) ) whenever(isDevSiteSupported()).thenReturn(true) From 6e6bca9ab61f9c59d751df2e220a8829a4edbe98 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 30 Jan 2024 14:46:11 +0100 Subject: [PATCH 43/74] Test for ReceiptPreviewViewModel --- .../preview/ReceiptPreviewViewModelTest.kt | 80 ++++++++++++++++++- 1 file changed, 76 insertions(+), 4 deletions(-) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModelTest.kt index 3b26237b967..3a8d0fa2a80 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModelTest.kt @@ -1,13 +1,13 @@ package com.woocommerce.android.ui.payments.receipt.preview import androidx.lifecycle.SavedStateHandle +import com.woocommerce.android.R import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_EMAIL_TAPPED import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_CANCELED import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_FAILED import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_SUCCESS import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_TAPPED import com.woocommerce.android.analytics.AnalyticsTrackerWrapper -import com.woocommerce.android.tools.SelectedSite import com.woocommerce.android.ui.payments.receipt.PaymentReceiptShare import com.woocommerce.android.ui.payments.receipt.preview.ReceiptPreviewViewModel.ReceiptPreviewViewState.Content import com.woocommerce.android.ui.payments.receipt.preview.ReceiptPreviewViewModel.ReceiptPreviewViewState.Loading @@ -16,6 +16,7 @@ import com.woocommerce.android.util.PrintHtmlHelper.PrintJobResult.CANCELLED import com.woocommerce.android.util.PrintHtmlHelper.PrintJobResult.FAILED import com.woocommerce.android.util.PrintHtmlHelper.PrintJobResult.STARTED import com.woocommerce.android.viewmodel.BaseUnitTest +import com.woocommerce.android.viewmodel.MultiLiveEvent import kotlinx.coroutines.ExperimentalCoroutinesApi import org.assertj.core.api.Assertions.assertThat import org.junit.Before @@ -23,13 +24,11 @@ import org.junit.Test import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever -import org.wordpress.android.fluxc.model.SiteModel @ExperimentalCoroutinesApi class ReceiptPreviewViewModelTest : BaseUnitTest() { private lateinit var viewModel: ReceiptPreviewViewModel - private val selectedSite: SelectedSite = mock() private val tracker: AnalyticsTrackerWrapper = mock() private val paymentsFlowTracker: PaymentsFlowTracker = mock() private val paymentReceiptShare: PaymentReceiptShare = mock() @@ -43,7 +42,6 @@ class ReceiptPreviewViewModelTest : BaseUnitTest() { @Before fun setUp() { viewModel = ReceiptPreviewViewModel(savedState, tracker, paymentsFlowTracker, paymentReceiptShare) - whenever(selectedSite.get()).thenReturn(SiteModel().apply { name = "testName" }) } @Test @@ -82,6 +80,80 @@ class ReceiptPreviewViewModelTest : BaseUnitTest() { verify(tracker).track(RECEIPT_EMAIL_TAPPED) } + @Test + fun `given sharing success, when onShareClicked, then no events emitted`() = + testBlocking { + // GIVEN + whenever(paymentReceiptShare("testing url", 999L)).thenReturn( + PaymentReceiptShare.ReceiptShareResult.Success + ) + + // WHEN + viewModel.onShareClicked() + + // THEN + assertThat(viewModel.event.value).isInstanceOf(LoadUrl::class.java) + } + + @Test + fun `given sharing failed with file cretion, when onShareClicked, then ShowSnackbar emitted`() = + testBlocking { + // GIVEN + whenever(paymentReceiptShare("testing url", 999L)).thenReturn( + PaymentReceiptShare.ReceiptShareResult.Error.FileCreation + ) + + // WHEN + viewModel.onShareClicked() + + // THEN + assertThat((viewModel.event.value as MultiLiveEvent.Event.ShowSnackbar).message).isEqualTo( + R.string.card_reader_payment_receipt_can_not_be_stored + ) + verify(paymentsFlowTracker).trackPaymentsReceiptSharingFailed( + PaymentReceiptShare.ReceiptShareResult.Error.FileCreation + ) + } + + @Test + fun `given sharing failed with file downloading, when onShareClicked, then ShowSnackbar emitted`() = + testBlocking { + // GIVEN + whenever(paymentReceiptShare("testing url", 999L)).thenReturn( + PaymentReceiptShare.ReceiptShareResult.Error.FileDownload + ) + + // WHEN + viewModel.onShareClicked() + + // THEN + assertThat((viewModel.event.value as MultiLiveEvent.Event.ShowSnackbar).message).isEqualTo( + R.string.card_reader_payment_receipt_can_not_be_downloaded + ) + verify(paymentsFlowTracker).trackPaymentsReceiptSharingFailed( + PaymentReceiptShare.ReceiptShareResult.Error.FileDownload + ) + } + + @Test + fun `given sharing failed with file sharing, when onShareClicked, then ShowSnackbar emitted`() = + testBlocking { + // GIVEN + val sharing = PaymentReceiptShare.ReceiptShareResult.Error.Sharing(Exception()) + whenever(paymentReceiptShare("testing url", 999L)).thenReturn(sharing) + + // WHEN + viewModel.onShareClicked() + + // THEN + assertThat((viewModel.event.value as MultiLiveEvent.Event.ShowSnackbar).message).isEqualTo( + R.string.card_reader_payment_receipt_app_to_share_not_found + ) + verify(paymentsFlowTracker).trackPaymentsReceiptSharingFailed( + sharing + ) + } + @Test fun `when user clicks on print receipt, then print receipt event emitted`() = testBlocking { From ed6b58bbebec1080c8d247a748d45ad750eb0709 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 30 Jan 2024 15:06:16 +0100 Subject: [PATCH 44/74] Updated release notes --- RELEASE-NOTES.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 463fb94b49d..091f9a955bd 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -1,4 +1,8 @@ *** PLEASE FOLLOW THIS FORMAT: [] [] +17.2 +----- +- [**] Every order have a receipt now. The receipts can be shared via many apps installed on the phone [https://github.com/woocommerce/woocommerce-android/pull/10650] + 17.1 ----- From 76ce02213f2bbedbfd9406ddc6eae851997fe9b3 Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 31 Jan 2024 11:57:18 +0100 Subject: [PATCH 45/74] Attach receipt source inside of PaymentFlowTracker --- .../payments/receipt/PaymentReceiptHelper.kt | 2 +- .../payments/tracking/PaymentsFlowTracker.kt | 22 ++++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelper.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelper.kt index 8832e65cfeb..daf1bbffaee 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelper.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/PaymentReceiptHelper.kt @@ -77,7 +77,7 @@ class PaymentReceiptHelper @Inject constructor( } } - private suspend fun isWCCanGenerateReceipts(): Boolean { + suspend fun isWCCanGenerateReceipts(): Boolean { val currentWooCoreVersion = getWoocommerceCorePluginVersion() return currentWooCoreVersion.semverCompareTo(WC_CAN_GENERATE_RECEIPTS_VERSION) >= 0 diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt index 9cdce47de8a..b6959450f9c 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt @@ -73,6 +73,7 @@ import com.woocommerce.android.ui.payments.cardreader.onboarding.PluginType import com.woocommerce.android.ui.payments.cardreader.onboarding.PluginType.STRIPE_EXTENSION_GATEWAY import com.woocommerce.android.ui.payments.cardreader.onboarding.PluginType.WOOCOMMERCE_PAYMENTS import com.woocommerce.android.ui.payments.hub.PaymentsHubViewModel.CashOnDeliverySource +import com.woocommerce.android.ui.payments.receipt.PaymentReceiptHelper import com.woocommerce.android.ui.payments.receipt.PaymentReceiptShare import com.woocommerce.android.ui.payments.taptopay.TapToPayAvailabilityStatus.Result.NotAvailable import javax.inject.Inject @@ -81,7 +82,8 @@ class PaymentsFlowTracker @Inject constructor( private val trackerWrapper: AnalyticsTrackerWrapper, private val appPrefsWrapper: AppPrefsWrapper, private val selectedSite: SelectedSite, - private val cardReaderTrackingInfoProvider: CardReaderTrackingInfoProvider + private val cardReaderTrackingInfoProvider: CardReaderTrackingInfoProvider, + private val paymentReceiptHelper: PaymentReceiptHelper, ) { @VisibleForTesting fun track( @@ -156,6 +158,13 @@ class PaymentsFlowTracker @Inject constructor( } } + private suspend fun getReceiptSource(): Pair = + if (paymentReceiptHelper.isWCCanGenerateReceipts()) { + "source" to "backend" + } else { + "source" to "local" + } + @Suppress("ComplexMethod") private fun getOnboardingNotCompletedReason(state: CardReaderOnboardingState): String? = when (state) { @@ -581,27 +590,30 @@ class PaymentsFlowTracker @Inject constructor( ) } - fun trackPaymentsReceiptSharingFailed(sharingResult: PaymentReceiptShare.ReceiptShareResult.Error) { + suspend fun trackPaymentsReceiptSharingFailed(sharingResult: PaymentReceiptShare.ReceiptShareResult.Error) { when (sharingResult) { is PaymentReceiptShare.ReceiptShareResult.Error.FileCreation -> { track( RECEIPT_EMAIL_FAILED, errorType = "file_creation_failed", - errorDescription = "File creation failed" + errorDescription = "File creation failed", + properties = mutableMapOf(getReceiptSource()) ) } is PaymentReceiptShare.ReceiptShareResult.Error.FileDownload -> { track( RECEIPT_EMAIL_FAILED, errorType = "file_download_failed", - errorDescription = "File download failed" + errorDescription = "File download failed", + properties = mutableMapOf(getReceiptSource()) ) } is PaymentReceiptShare.ReceiptShareResult.Error.Sharing -> { track( RECEIPT_EMAIL_FAILED, errorType = "no_app_found", - errorDescription = sharingResult.exception.message + errorDescription = sharingResult.exception.message, + properties = mutableMapOf(getReceiptSource()) ) } } From 976762d5fef4b3a3a2b76ca049f53e0e072f792c Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 31 Jan 2024 12:20:28 +0100 Subject: [PATCH 46/74] Added source prop to the all methods that track receipt events --- .../payments/tracking/PaymentsFlowTracker.kt | 49 ++++++++++++++----- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt index b6959450f9c..38b58c005a3 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt @@ -421,28 +421,53 @@ class PaymentsFlowTracker @Inject constructor( ) } - fun trackPrintReceiptTapped() { - track(RECEIPT_PRINT_TAPPED) + suspend fun trackPrintReceiptTapped() { + track( + RECEIPT_PRINT_TAPPED, + properties = mutableMapOf(getReceiptSource()) + ) + } + + suspend fun trackEmailReceiptTapped() { + track( + RECEIPT_EMAIL_TAPPED, + properties = mutableMapOf(getReceiptSource()) + ) } - fun trackEmailReceiptTapped() { - track(RECEIPT_EMAIL_TAPPED) + suspend fun trackEmailReceiptFailed() { + track( + RECEIPT_EMAIL_FAILED, + properties = mutableMapOf(getReceiptSource()) + ) } - fun trackEmailReceiptFailed() { - track(RECEIPT_EMAIL_FAILED) + suspend fun trackPrintReceiptCancelled() { + track( + RECEIPT_PRINT_CANCELED, + properties = mutableMapOf(getReceiptSource()) + ) } - fun trackPrintReceiptCancelled() { - track(RECEIPT_PRINT_CANCELED) + suspend fun trackPrintReceiptFailed() { + track( + RECEIPT_PRINT_FAILED, + properties = mutableMapOf(getReceiptSource()) + ) } - fun trackPrintReceiptFailed() { - track(RECEIPT_PRINT_FAILED) + suspend fun trackPrintReceiptSucceeded() { + track( + RECEIPT_PRINT_SUCCESS, + properties = mutableMapOf(getReceiptSource()) + ) } - fun trackPrintReceiptSucceeded() { - track(RECEIPT_PRINT_SUCCESS) + suspend fun trackReceiptViewTapped() { + track( + AnalyticsEvent.RECEIPT_VIEW_TAPPED, + properties = mutableMapOf(getReceiptSource()) + ) } fun trackPaymentCancelled(currentPaymentState: String?) { From d510551fb907e5418180f44320425b0b5ae1503e Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 31 Jan 2024 12:21:32 +0100 Subject: [PATCH 47/74] Removed redundant method --- .../cardreader/payment/CardReaderPaymentViewModel.kt | 5 ----- .../payments/cardreader/CardReaderPaymentViewModelTest.kt | 8 -------- 2 files changed, 13 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModel.kt index fc52388c3f9..c42ffc05783 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModel.kt @@ -756,11 +756,6 @@ class CardReaderPaymentViewModel } } - fun onEmailActivityNotFound() { - tracker.trackEmailReceiptFailed() - triggerEvent(ShowSnackbarInDialog(R.string.card_reader_payment_receipt_app_to_share_not_found)) - } - fun onPrintResult(result: PrintJobResult) { showPaymentSuccessfulState() diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/cardreader/CardReaderPaymentViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/cardreader/CardReaderPaymentViewModelTest.kt index a9cbfa61c5f..3b47aaaf39c 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/cardreader/CardReaderPaymentViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/cardreader/CardReaderPaymentViewModelTest.kt @@ -2568,14 +2568,6 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() { assertThat(viewModel.event.value).isInstanceOf(Exit::class.java) } - @Test - fun `when email activity not found, then event tracked`() = - testBlocking { - viewModel.onEmailActivityNotFound() - - verify(tracker).trackEmailReceiptFailed() - } - @Test fun `given user presses back button, when re-fetching order, then ReFetchingOrderState shown`() = testBlocking { From 862156d6bed5a2d982f4e15a21d4946fa52b533e Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 31 Jan 2024 12:23:55 +0100 Subject: [PATCH 48/74] Launch to track receipt events --- .../cardreader/payment/CardReaderPaymentViewModel.kt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModel.kt index c42ffc05783..53ed4f52590 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModel.kt @@ -759,10 +759,12 @@ class CardReaderPaymentViewModel fun onPrintResult(result: PrintJobResult) { showPaymentSuccessfulState() - when (result) { - CANCELLED -> tracker.trackPrintReceiptCancelled() - FAILED -> tracker.trackPrintReceiptFailed() - STARTED -> tracker.trackPrintReceiptSucceeded() + launch { + when (result) { + CANCELLED -> tracker.trackPrintReceiptCancelled() + FAILED -> tracker.trackPrintReceiptFailed() + STARTED -> tracker.trackPrintReceiptSucceeded() + } } } From a466cc3d8a7f8cb02bd2fb5a19489cd3c35bce76 Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 31 Jan 2024 12:30:53 +0100 Subject: [PATCH 49/74] Track all receipt related events via tracker class --- .../preview/ReceiptPreviewViewModel.kt | 25 ++++++++----------- .../payments/tracking/PaymentsFlowTracker.kt | 7 ------ 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt index ed7aa503ec5..1a24d00ab66 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt @@ -4,12 +4,6 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.SavedStateHandle import com.woocommerce.android.R.string -import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_EMAIL_TAPPED -import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_CANCELED -import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_FAILED -import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_SUCCESS -import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_TAPPED -import com.woocommerce.android.analytics.AnalyticsTrackerWrapper import com.woocommerce.android.ui.payments.receipt.PaymentReceiptShare import com.woocommerce.android.ui.payments.receipt.preview.ReceiptPreviewViewModel.ReceiptPreviewViewState.Content import com.woocommerce.android.ui.payments.receipt.preview.ReceiptPreviewViewModel.ReceiptPreviewViewState.Loading @@ -29,7 +23,6 @@ import javax.inject.Inject class ReceiptPreviewViewModel @Inject constructor( savedState: SavedStateHandle, - private val tracker: AnalyticsTrackerWrapper, private val paymentsFlowTracker: PaymentsFlowTracker, private val paymentReceiptShare: PaymentReceiptShare, ) : ScopedViewModel(savedState) { @@ -47,15 +40,17 @@ class ReceiptPreviewViewModel } fun onPrintClicked() { - tracker.track(RECEIPT_PRINT_TAPPED) - triggerEvent(PrintReceipt(args.receiptUrl, "receipt-order-${args.orderId}")) + launch { + paymentsFlowTracker.trackPrintReceiptTapped() + triggerEvent(PrintReceipt(args.receiptUrl, "receipt-order-${args.orderId}")) + } } fun onShareClicked() { launch { viewState.value = Loading - tracker.track(RECEIPT_EMAIL_TAPPED) + paymentsFlowTracker.trackEmailReceiptTapped() when (val sharingResult = paymentReceiptShare(args.receiptUrl, args.orderId)) { is PaymentReceiptShare.ReceiptShareResult.Error.FileCreation -> { paymentsFlowTracker.trackPaymentsReceiptSharingFailed(sharingResult) @@ -79,13 +74,13 @@ class ReceiptPreviewViewModel } fun onPrintResult(result: PrintJobResult) { - tracker.track( + launch { when (result) { - CANCELLED -> RECEIPT_PRINT_CANCELED - FAILED -> RECEIPT_PRINT_FAILED - STARTED -> RECEIPT_PRINT_SUCCESS + CANCELLED -> paymentsFlowTracker.trackPrintReceiptCancelled() + FAILED -> paymentsFlowTracker.trackPrintReceiptFailed() + STARTED -> paymentsFlowTracker.trackPrintReceiptSucceeded() } - ) + } } sealed class ReceiptPreviewViewState( diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt index 38b58c005a3..52271edf0cb 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt @@ -435,13 +435,6 @@ class PaymentsFlowTracker @Inject constructor( ) } - suspend fun trackEmailReceiptFailed() { - track( - RECEIPT_EMAIL_FAILED, - properties = mutableMapOf(getReceiptSource()) - ) - } - suspend fun trackPrintReceiptCancelled() { track( RECEIPT_PRINT_CANCELED, From 70635adf45e0abc73d80c4a57679abe9a4fb681f Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 31 Jan 2024 12:33:28 +0100 Subject: [PATCH 50/74] RECEIPT_URL_FETCHING_FAILS added an event --- .../kotlin/com/woocommerce/android/analytics/AnalyticsEvent.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/analytics/AnalyticsEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/analytics/AnalyticsEvent.kt index 6b269a756c9..a6a748927a7 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/analytics/AnalyticsEvent.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/analytics/AnalyticsEvent.kt @@ -452,6 +452,7 @@ enum class AnalyticsEvent(val siteless: Boolean = false) { RECEIPT_PRINT_CANCELED, RECEIPT_PRINT_SUCCESS, RECEIPT_VIEW_TAPPED, + RECEIPT_URL_FETCHING_FAILS, // -- Top-level navigation MAIN_MENU_SETTINGS_TAPPED, From 53adb50ef2d5975806726e8eae344849bf3ce9ed Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 31 Jan 2024 12:38:48 +0100 Subject: [PATCH 51/74] RECEIPT_VIEW_TAPPED tracked --- .../android/ui/orders/details/OrderDetailTracker.kt | 9 +++++---- .../android/ui/payments/tracking/PaymentsFlowTracker.kt | 8 ++++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailTracker.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailTracker.kt index b86ef2b5a92..195b3b88586 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailTracker.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailTracker.kt @@ -4,11 +4,13 @@ import com.woocommerce.android.analytics.AnalyticsEvent import com.woocommerce.android.analytics.AnalyticsTracker import com.woocommerce.android.analytics.AnalyticsTrackerWrapper import com.woocommerce.android.model.Order +import com.woocommerce.android.ui.payments.tracking.PaymentsFlowTracker import org.wordpress.android.fluxc.store.WCOrderStore import javax.inject.Inject class OrderDetailTracker @Inject constructor( - private val trackerWrapper: AnalyticsTrackerWrapper + private val trackerWrapper: AnalyticsTrackerWrapper, + private val paymentsFlowTracker: PaymentsFlowTracker, ) { fun trackCustomFieldsTapped() { trackerWrapper.track(AnalyticsEvent.ORDER_VIEW_CUSTOM_FIELDS_TAPPED) @@ -24,9 +26,8 @@ class OrderDetailTracker @Inject constructor( ) } - fun trackReceiptViewTapped(orderId: Long, orderStatus: Order.Status) { - trackerWrapper.track( - AnalyticsEvent.RECEIPT_VIEW_TAPPED, + suspend fun trackReceiptViewTapped(orderId: Long, orderStatus: Order.Status) { + paymentsFlowTracker.trackReceiptViewTapped( mapOf( AnalyticsTracker.KEY_ORDER_ID to orderId, AnalyticsTracker.KEY_STATUS to orderStatus diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt index 52271edf0cb..105f4efa171 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt @@ -456,10 +456,14 @@ class PaymentsFlowTracker @Inject constructor( ) } - suspend fun trackReceiptViewTapped() { + suspend fun trackReceiptViewTapped(properties: Map) { track( AnalyticsEvent.RECEIPT_VIEW_TAPPED, - properties = mutableMapOf(getReceiptSource()) + properties = properties.toMutableMap().also { + it.putAll( + mapOf(getReceiptSource()) + ) + } ) } From fd3966a94bfca3898ab0de69bd4d24809cea1488 Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 31 Jan 2024 12:40:53 +0100 Subject: [PATCH 52/74] trackReceiptUrlFetchingFails method --- .../ui/payments/tracking/PaymentsFlowTracker.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt index 105f4efa171..eea12f6ad83 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt @@ -53,6 +53,7 @@ import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_CANCELED import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_FAILED import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_SUCCESS import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_TAPPED +import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_URL_FETCHING_FAILS import com.woocommerce.android.analytics.AnalyticsTracker import com.woocommerce.android.analytics.AnalyticsTracker.Companion.KEY_CASH_ON_DELIVERY_SOURCE import com.woocommerce.android.analytics.AnalyticsTracker.Companion.KEY_ERROR_DESC @@ -467,6 +468,18 @@ class PaymentsFlowTracker @Inject constructor( ) } + suspend fun trackReceiptUrlFetchingFails( + errorDescription: String, + errorType: String, + ) { + track( + RECEIPT_URL_FETCHING_FAILS, + properties = mutableMapOf(getReceiptSource()), + errorDescription = errorDescription, + errorType = errorType, + ) + } + fun trackPaymentCancelled(currentPaymentState: String?) { track( CARD_PRESENT_COLLECT_PAYMENT_CANCELLED, From bf278d4a0b9474f0b59ec99b62ca7b5108588788 Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 31 Jan 2024 12:41:52 +0100 Subject: [PATCH 53/74] RECEIPT_VIEW_TAPPED imported statically --- .../android/ui/payments/tracking/PaymentsFlowTracker.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt index eea12f6ad83..bdbcc421ee9 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt @@ -54,6 +54,7 @@ import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_FAILED import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_SUCCESS import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_TAPPED import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_URL_FETCHING_FAILS +import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_VIEW_TAPPED import com.woocommerce.android.analytics.AnalyticsTracker import com.woocommerce.android.analytics.AnalyticsTracker.Companion.KEY_CASH_ON_DELIVERY_SOURCE import com.woocommerce.android.analytics.AnalyticsTracker.Companion.KEY_ERROR_DESC @@ -459,7 +460,7 @@ class PaymentsFlowTracker @Inject constructor( suspend fun trackReceiptViewTapped(properties: Map) { track( - AnalyticsEvent.RECEIPT_VIEW_TAPPED, + RECEIPT_VIEW_TAPPED, properties = properties.toMutableMap().also { it.putAll( mapOf(getReceiptSource()) @@ -635,6 +636,7 @@ class PaymentsFlowTracker @Inject constructor( properties = mutableMapOf(getReceiptSource()) ) } + is PaymentReceiptShare.ReceiptShareResult.Error.FileDownload -> { track( RECEIPT_EMAIL_FAILED, @@ -643,6 +645,7 @@ class PaymentsFlowTracker @Inject constructor( properties = mutableMapOf(getReceiptSource()) ) } + is PaymentReceiptShare.ReceiptShareResult.Error.Sharing -> { track( RECEIPT_EMAIL_FAILED, From 08122c25215cc9e68bfce5b70df417d3ff1049ca Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 31 Jan 2024 12:44:46 +0100 Subject: [PATCH 54/74] trackReceiptUrlFetchingFails in the order details screen --- .../android/ui/orders/details/OrderDetailViewModel.kt | 3 +++ .../android/ui/payments/tracking/PaymentsFlowTracker.kt | 6 +----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailViewModel.kt index b018d182f78..6b3df9c207f 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailViewModel.kt @@ -351,6 +351,9 @@ class OrderDetailViewModel @Inject constructor( if (receiptResult.isSuccess) { triggerEvent(PreviewReceipt(order.billingAddress.email, receiptResult.getOrThrow(), order.id)) } else { + paymentsFlowTracker.trackReceiptUrlFetchingFails( + errorDescription = receiptResult.exceptionOrNull()?.message ?: "Unknown error", + ) triggerEvent(ShowSnackbar(string.receipt_fetching_error)) } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt index bdbcc421ee9..ffd264fd820 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt @@ -469,15 +469,11 @@ class PaymentsFlowTracker @Inject constructor( ) } - suspend fun trackReceiptUrlFetchingFails( - errorDescription: String, - errorType: String, - ) { + suspend fun trackReceiptUrlFetchingFails(errorDescription: String) { track( RECEIPT_URL_FETCHING_FAILS, properties = mutableMapOf(getReceiptSource()), errorDescription = errorDescription, - errorType = errorType, ) } From efbed421734c0b6d1750d197c67dc6889f6cc28d Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 31 Jan 2024 12:45:40 +0100 Subject: [PATCH 55/74] On card card reader restult screen --- .../cardreader/payment/CardReaderPaymentViewModel.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModel.kt index 53ed4f52590..1bcbfff35aa 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModel.kt @@ -718,6 +718,9 @@ class CardReaderPaymentViewModel ) ) } else { + tracker.trackReceiptUrlFetchingFails( + errorDescription = receiptResult.exceptionOrNull()?.message ?: "Unknown error", + ) triggerEvent(ShowSnackbar(R.string.receipt_fetching_error)) } } @@ -749,6 +752,9 @@ class CardReaderPaymentViewModel } } } else { + tracker.trackReceiptUrlFetchingFails( + errorDescription = receiptResult.exceptionOrNull()?.message ?: "Unknown error", + ) triggerEvent(ShowSnackbar(R.string.receipt_fetching_error)) } From c6059f361b4f43ac21236a2eda80ad815e657f6b Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 31 Jan 2024 16:02:14 +0100 Subject: [PATCH 56/74] Tests compilable --- .../CardReaderPaymentViewModelTest.kt | 6 +++--- .../preview/ReceiptPreviewViewModelTest.kt | 19 ++++++------------- .../tracking/PaymentsFlowTrackerTest.kt | 12 ++++++++---- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/cardreader/CardReaderPaymentViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/cardreader/CardReaderPaymentViewModelTest.kt index 3b47aaaf39c..5f1f98229d4 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/cardreader/CardReaderPaymentViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/cardreader/CardReaderPaymentViewModelTest.kt @@ -2337,21 +2337,21 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() { } @Test - fun `when OS accepts the print request, then print success event tracked`() { + fun `when OS accepts the print request, then print success event tracked`() = testBlocking { viewModel.onPrintResult(STARTED) verify(tracker).trackPrintReceiptSucceeded() } @Test - fun `when OS refuses the print request, then print failed event tracked`() { + fun `when OS refuses the print request, then print failed event tracked`() = testBlocking { viewModel.onPrintResult(FAILED) verify(tracker).trackPrintReceiptFailed() } @Test - fun `when manually cancels the print request, then print cancelled event tracked`() { + fun `when manually cancels the print request, then print cancelled event tracked`() = testBlocking { viewModel.onPrintResult(CANCELLED) verify(tracker).trackPrintReceiptCancelled() diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModelTest.kt index 3a8d0fa2a80..a76365c0dbc 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModelTest.kt @@ -2,12 +2,6 @@ package com.woocommerce.android.ui.payments.receipt.preview import androidx.lifecycle.SavedStateHandle import com.woocommerce.android.R -import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_EMAIL_TAPPED -import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_CANCELED -import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_FAILED -import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_SUCCESS -import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_TAPPED -import com.woocommerce.android.analytics.AnalyticsTrackerWrapper import com.woocommerce.android.ui.payments.receipt.PaymentReceiptShare import com.woocommerce.android.ui.payments.receipt.preview.ReceiptPreviewViewModel.ReceiptPreviewViewState.Content import com.woocommerce.android.ui.payments.receipt.preview.ReceiptPreviewViewModel.ReceiptPreviewViewState.Loading @@ -29,7 +23,6 @@ import org.mockito.kotlin.whenever class ReceiptPreviewViewModelTest : BaseUnitTest() { private lateinit var viewModel: ReceiptPreviewViewModel - private val tracker: AnalyticsTrackerWrapper = mock() private val paymentsFlowTracker: PaymentsFlowTracker = mock() private val paymentReceiptShare: PaymentReceiptShare = mock() @@ -41,7 +34,7 @@ class ReceiptPreviewViewModelTest : BaseUnitTest() { @Before fun setUp() { - viewModel = ReceiptPreviewViewModel(savedState, tracker, paymentsFlowTracker, paymentReceiptShare) + viewModel = ReceiptPreviewViewModel(savedState, paymentsFlowTracker, paymentReceiptShare) } @Test @@ -77,7 +70,7 @@ class ReceiptPreviewViewModelTest : BaseUnitTest() { testBlocking { viewModel.onShareClicked() - verify(tracker).track(RECEIPT_EMAIL_TAPPED) + verify(paymentsFlowTracker).trackEmailReceiptTapped() } @Test @@ -167,7 +160,7 @@ class ReceiptPreviewViewModelTest : BaseUnitTest() { testBlocking { viewModel.onPrintClicked() - verify(tracker).track(RECEIPT_PRINT_TAPPED) + verify(paymentsFlowTracker).trackPrintReceiptTapped() } @Test @@ -175,7 +168,7 @@ class ReceiptPreviewViewModelTest : BaseUnitTest() { testBlocking { viewModel.onPrintResult(FAILED) - verify(tracker).track(RECEIPT_PRINT_FAILED) + verify(paymentsFlowTracker).trackPrintReceiptFailed() } @Test @@ -183,7 +176,7 @@ class ReceiptPreviewViewModelTest : BaseUnitTest() { testBlocking { viewModel.onPrintResult(CANCELLED) - verify(tracker).track(RECEIPT_PRINT_CANCELED) + verify(paymentsFlowTracker).trackPrintReceiptCancelled() } @Test @@ -191,6 +184,6 @@ class ReceiptPreviewViewModelTest : BaseUnitTest() { testBlocking { viewModel.onPrintResult(STARTED) - verify(tracker).track(RECEIPT_PRINT_SUCCESS) + verify(paymentsFlowTracker).trackPrintReceiptSucceeded() } } diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTrackerTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTrackerTest.kt index 14e117d6353..e59c768f428 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTrackerTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTrackerTest.kt @@ -42,6 +42,7 @@ import com.woocommerce.android.ui.payments.cardreader.onboarding.PluginType.STRI import com.woocommerce.android.ui.payments.cardreader.onboarding.PluginType.WOOCOMMERCE_PAYMENTS import com.woocommerce.android.ui.payments.hub.PaymentsHubViewModel.CashOnDeliverySource.ONBOARDING import com.woocommerce.android.ui.payments.hub.PaymentsHubViewModel.CashOnDeliverySource.PAYMENTS_HUB +import com.woocommerce.android.ui.payments.receipt.PaymentReceiptHelper import com.woocommerce.android.viewmodel.BaseUnitTest import kotlinx.coroutines.ExperimentalCoroutinesApi import org.assertj.core.api.Assertions.assertThat @@ -93,11 +94,14 @@ class PaymentsFlowTrackerTest : BaseUnitTest() { ) } + private val paymentReceiptHelper: PaymentReceiptHelper = mock() + private val paymentsFlowTracker = PaymentsFlowTracker( trackerWrapper, appPrefsWrapper, selectedSite, - cardReaderTrackingInfoProvider + cardReaderTrackingInfoProvider, + paymentReceiptHelper, ) @Test @@ -1047,21 +1051,21 @@ class PaymentsFlowTrackerTest : BaseUnitTest() { } @Test - fun `when OS accepts the print request, then RECEIPT_PRINT_SUCCESS tracked`() { + fun `when OS accepts the print request, then RECEIPT_PRINT_SUCCESS tracked`() = testBlocking { paymentsFlowTracker.trackPrintReceiptSucceeded() verify(trackerWrapper).track(eq(RECEIPT_PRINT_SUCCESS), any()) } @Test - fun `when OS refuses the print request, then RECEIPT_PRINT_FAILED tracked`() { + fun `when OS refuses the print request, then RECEIPT_PRINT_FAILED tracked`() = testBlocking { paymentsFlowTracker.trackPrintReceiptFailed() verify(trackerWrapper).track(eq(RECEIPT_PRINT_FAILED), any()) } @Test - fun `when manually cancels the print request, then RECEIPT_PRINT_CANCELED tracked`() { + fun `when manually cancels the print request, then RECEIPT_PRINT_CANCELED tracked`() = testBlocking { paymentsFlowTracker.trackPrintReceiptCancelled() verify(trackerWrapper).track(eq(RECEIPT_PRINT_CANCELED), any()) From 6957125a316062db89ed81dfe6625ae5b0f0d3f9 Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 31 Jan 2024 16:36:17 +0100 Subject: [PATCH 57/74] Tests of PaymentsFlowTracker --- .../payments/tracking/PaymentsFlowTracker.kt | 4 +- .../tracking/PaymentsFlowTrackerTest.kt | 157 +++++++++++++++--- 2 files changed, 140 insertions(+), 21 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt index ffd264fd820..0ffc92e1cb3 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTracker.kt @@ -162,9 +162,9 @@ class PaymentsFlowTracker @Inject constructor( private suspend fun getReceiptSource(): Pair = if (paymentReceiptHelper.isWCCanGenerateReceipts()) { - "source" to "backend" + AnalyticsTracker.KEY_SOURCE to "backend" } else { - "source" to "local" + AnalyticsTracker.KEY_SOURCE to "local" } @Suppress("ComplexMethod") diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTrackerTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTrackerTest.kt index e59c768f428..4f78bbd6d19 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTrackerTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/tracking/PaymentsFlowTrackerTest.kt @@ -26,7 +26,6 @@ import com.woocommerce.android.analytics.AnalyticsEvent.ENABLE_CASH_ON_DELIVERY_ import com.woocommerce.android.analytics.AnalyticsEvent.PAYMENTS_FLOW_ORDER_COLLECT_PAYMENT_TAPPED import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_EMAIL_TAPPED import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_CANCELED -import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_FAILED import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_SUCCESS import com.woocommerce.android.analytics.AnalyticsEvent.RECEIPT_PRINT_TAPPED import com.woocommerce.android.analytics.AnalyticsTracker @@ -77,13 +76,13 @@ class PaymentsFlowTrackerTest : BaseUnitTest() { } private val trackerWrapper: AnalyticsTrackerWrapper = mock() - private val appPrefsWrapper: AppPrefsWrapper = mock() { + private val appPrefsWrapper: AppPrefsWrapper = mock { on(it.getCardReaderPreferredPlugin(anyInt(), anyLong(), anyLong())).thenReturn(WOOCOMMERCE_PAYMENTS) } private val selectedSite: SelectedSite = mock { on(it.get()).thenReturn(SiteModel()) } - private val cardReaderTrackingInfoProvider: CardReaderTrackingInfoProvider = mock() { + private val cardReaderTrackingInfoProvider: CardReaderTrackingInfoProvider = mock { on { trackingInfo }.thenReturn( TrackingInfo( country = COUNTRY_CODE, @@ -1043,40 +1042,160 @@ class PaymentsFlowTrackerTest : BaseUnitTest() { } @Test - fun `when user clicks on print receipt button, then RECEIPT_PRINT_TAPPED tracked`() = + fun `given wc core can generate receipts, when trackPrintReceiptTapped, then RECEIPT_PRINT_TAPPED tracked`() = testBlocking { + whenever(paymentReceiptHelper.isWCCanGenerateReceipts()).thenReturn(true) + paymentsFlowTracker.trackPrintReceiptTapped() - verify(trackerWrapper).track(eq(RECEIPT_PRINT_TAPPED), any()) + verify(trackerWrapper).track( + eq(RECEIPT_PRINT_TAPPED), + check { assertThat(it["source"]).isEqualTo("backend") } + ) } @Test - fun `when OS accepts the print request, then RECEIPT_PRINT_SUCCESS tracked`() = testBlocking { - paymentsFlowTracker.trackPrintReceiptSucceeded() + fun `given new wc core can not generate receipts, when trackPrintReceiptTapped, then RECEIPT_PRINT_TAPPED tracked`() = + testBlocking { + whenever(paymentReceiptHelper.isWCCanGenerateReceipts()).thenReturn(false) - verify(trackerWrapper).track(eq(RECEIPT_PRINT_SUCCESS), any()) - } + paymentsFlowTracker.trackPrintReceiptTapped() + + verify(trackerWrapper).track( + eq(RECEIPT_PRINT_TAPPED), + check { assertThat(it["source"]).isEqualTo("local") } + ) + } @Test - fun `when OS refuses the print request, then RECEIPT_PRINT_FAILED tracked`() = testBlocking { - paymentsFlowTracker.trackPrintReceiptFailed() + fun `given wc core can generate receipts, when trackPrintReceiptSucceeded, then RECEIPT_PRINT_SUCCESS tracked`() = + testBlocking { + whenever(paymentReceiptHelper.isWCCanGenerateReceipts()).thenReturn(true) - verify(trackerWrapper).track(eq(RECEIPT_PRINT_FAILED), any()) - } + paymentsFlowTracker.trackPrintReceiptSucceeded() + + verify(trackerWrapper).track( + eq(RECEIPT_PRINT_SUCCESS), + check { assertThat(it["source"]).isEqualTo("backend") } + ) + } @Test - fun `when manually cancels the print request, then RECEIPT_PRINT_CANCELED tracked`() = testBlocking { - paymentsFlowTracker.trackPrintReceiptCancelled() + fun `given wc core can not generate receipts, when trackPrintReceiptSucceeded, then RECEIPT_PRINT_SUCCESS tracked`() = + testBlocking { + whenever(paymentReceiptHelper.isWCCanGenerateReceipts()).thenReturn(false) - verify(trackerWrapper).track(eq(RECEIPT_PRINT_CANCELED), any()) - } + paymentsFlowTracker.trackPrintReceiptSucceeded() + + verify(trackerWrapper).track( + eq(RECEIPT_PRINT_SUCCESS), + check { assertThat(it["source"]).isEqualTo("local") } + ) + } + + @Test + fun `given wc core can generate receipts, when trackPrintReceiptCancelled, then RECEIPT_PRINT_CANCELED tracked`() = + testBlocking { + whenever(paymentReceiptHelper.isWCCanGenerateReceipts()).thenReturn(true) + + paymentsFlowTracker.trackPrintReceiptCancelled() + + verify(trackerWrapper).track( + eq(RECEIPT_PRINT_CANCELED), + check { assertThat(it["source"]).isEqualTo("backend") } + ) + } + + @Test + fun `given wc core can not generate receipts, when trackPrintReceiptCancelled, then RECEIPT_PRINT_CANCELED tracked`() = + testBlocking { + whenever(paymentReceiptHelper.isWCCanGenerateReceipts()).thenReturn(false) + + paymentsFlowTracker.trackPrintReceiptCancelled() + + verify(trackerWrapper).track( + eq(RECEIPT_PRINT_CANCELED), + check { assertThat(it["source"]).isEqualTo("local") } + ) + } @Test - fun `when user clicks on send receipt button, then RECEIPT_EMAIL_TAPPED tracked`() = + fun `given wc core can generate receipts, when trackEmailReceiptTapped, then RECEIPT_EMAIL_TAPPED tracked`() = testBlocking { + whenever(paymentReceiptHelper.isWCCanGenerateReceipts()).thenReturn(true) + paymentsFlowTracker.trackEmailReceiptTapped() - verify(trackerWrapper).track(eq(RECEIPT_EMAIL_TAPPED), any()) + verify(trackerWrapper).track( + eq(RECEIPT_EMAIL_TAPPED), + check { assertThat(it["source"]).isEqualTo("backend") } + ) + } + + @Test + fun `given wc core can generate receipts, when trackPrintReceiptFailed, then RECEIPT_PRINT_FAILED tracked`() = + testBlocking { + whenever(paymentReceiptHelper.isWCCanGenerateReceipts()).thenReturn(true) + + paymentsFlowTracker.trackPrintReceiptFailed() + + verify(trackerWrapper).track( + eq(AnalyticsEvent.RECEIPT_PRINT_FAILED), + check { assertThat(it["source"]).isEqualTo("backend") } + ) + } + + @Test + fun `given wc core can not generate receipts, when trackPrintReceiptFailed, then RECEIPT_PRINT_FAILED tracked`() = + testBlocking { + whenever(paymentReceiptHelper.isWCCanGenerateReceipts()).thenReturn(false) + + paymentsFlowTracker.trackPrintReceiptFailed() + + verify(trackerWrapper).track( + eq(AnalyticsEvent.RECEIPT_PRINT_FAILED), + check { assertThat(it["source"]).isEqualTo("local") } + ) + } + + @Test + fun `given wc core can not generate receipts, when trackReceiptViewTapped, then RECEIPT_VIEW_TAPPED tracked`() = + testBlocking { + whenever(paymentReceiptHelper.isWCCanGenerateReceipts()).thenReturn(false) + + paymentsFlowTracker.trackReceiptViewTapped( + properties = mapOf( + "key" to "value" + ) + ) + + verify(trackerWrapper).track( + eq(AnalyticsEvent.RECEIPT_VIEW_TAPPED), + check { + assertThat(it["source"]).isEqualTo("local") + assertThat(it["key"]).isEqualTo("value") + } + ) + } + + @Test + fun `given wc core can generate receipts, when trackReceiptViewTapped, then RECEIPT_VIEW_TAPPED tracked`() = + testBlocking { + whenever(paymentReceiptHelper.isWCCanGenerateReceipts()).thenReturn(true) + + paymentsFlowTracker.trackReceiptViewTapped( + properties = mapOf( + "key" to "value" + ) + ) + + verify(trackerWrapper).track( + eq(AnalyticsEvent.RECEIPT_VIEW_TAPPED), + check { + assertThat(it["source"]).isEqualTo("backend") + assertThat(it["key"]).isEqualTo("value") + } + ) } @Test From 05560ccebb2ee6b369a1220ffa20a29875d2e69c Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 31 Jan 2024 16:40:39 +0100 Subject: [PATCH 58/74] Added a test of OrderDetailViewModel --- .../android/ui/orders/OrderDetailViewModelTest.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/orders/OrderDetailViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/orders/OrderDetailViewModelTest.kt index bdbafe61f95..9edc1eec8cf 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/orders/OrderDetailViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/orders/OrderDetailViewModelTest.kt @@ -1243,7 +1243,8 @@ class OrderDetailViewModelTest : BaseUnitTest() { whenever(orderDetailRepository.fetchOrderNotes(any())).thenReturn(false) whenever(addonsRepository.containsAddonsFrom(any())).thenReturn(false) - whenever(paymentReceiptHelper.getReceiptUrl(order.id)).thenReturn(Result.failure(Exception(""))) + val errorMessage = "error" + whenever(paymentReceiptHelper.getReceiptUrl(order.id)).thenReturn(Result.failure(Exception(errorMessage))) // WHEN viewModel.start() @@ -1252,6 +1253,9 @@ class OrderDetailViewModelTest : BaseUnitTest() { // THEN assertThat((viewModel.event.value as ShowSnackbar).message).isEqualTo(string.receipt_fetching_error) + verify(paymentsFlowTracker).trackReceiptUrlFetchingFails( + errorDescription = errorMessage + ) } @Test From d143c97c57c9581cd1affb66d1034534ac167fd6 Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 31 Jan 2024 18:15:56 +0100 Subject: [PATCH 59/74] Updated release notes --- RELEASE-NOTES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 091f9a955bd..68bab4ccc5a 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -1,7 +1,7 @@ *** PLEASE FOLLOW THIS FORMAT: [] [] 17.2 ----- -- [**] Every order have a receipt now. The receipts can be shared via many apps installed on the phone [https://github.com/woocommerce/woocommerce-android/pull/10650] +- [**] (For users with WooCommerce version of 8.7+) Every order have a receipt now. The receipts can be shared via many apps installed on the phone [https://github.com/woocommerce/woocommerce-android/pull/10650] 17.1 ----- From 217f8d692d9e21dde790ce3bb095310e6b469a16 Mon Sep 17 00:00:00 2001 From: Joel Dean Date: Wed, 31 Jan 2024 17:31:47 -0500 Subject: [PATCH 60/74] Created a dialog fragment that can survive config changes. --- .../android/ui/dialog/WooDialogFragment.kt | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dialog/WooDialogFragment.kt diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dialog/WooDialogFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dialog/WooDialogFragment.kt new file mode 100644 index 00000000000..83dd8d2c247 --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dialog/WooDialogFragment.kt @@ -0,0 +1,60 @@ +package com.woocommerce.android.ui.dialog + +import android.app.Dialog +import android.os.Bundle +import androidx.fragment.app.DialogFragment +import com.google.android.material.dialog.MaterialAlertDialogBuilder + +class WooDialogFragment : DialogFragment() { + + private var dialogInteractionListener: DialogInteractionListener? = null + + fun setDialogInteractionListener(listener: DialogInteractionListener) { + dialogInteractionListener = listener + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + @Suppress("DEPRECATION") val params = requireArguments().getParcelable(ARG_DIALOG_PARAMS)!! + + val builder = MaterialAlertDialogBuilder(requireContext()) + .setCancelable(params.cancelable) + + params.titleId?.let { builder.setTitle(it) } + params.messageId?.let { builder.setMessage(it) } + params.positiveButtonId?.let { posId -> + builder.setPositiveButton(posId) { _, _ -> + dialogInteractionListener?.onPositiveButtonClicked() + } + } + params.negativeButtonId?.let { negId -> + builder.setNegativeButton(negId) { _, _ -> + dialogInteractionListener?.onNegativeButtonClicked() + } + } + params.neutralButtonId?.let { neutId -> + builder.setNeutralButton(neutId) { _, _ -> + dialogInteractionListener?.onNeutralButtonClicked() + } + } + + return builder.create() + } + + interface DialogInteractionListener { + fun onPositiveButtonClicked() + fun onNegativeButtonClicked() + fun onNeutralButtonClicked() + } + + companion object { + const val ARG_DIALOG_PARAMS = "dialog_params" + const val TAG = "WooDialogFragment" + fun newInstance(params: DialogParams): WooDialogFragment { + return WooDialogFragment().apply { + arguments = Bundle().apply { + putParcelable(ARG_DIALOG_PARAMS, params) + } + } + } + } +} From 1dc828047f9caeb65087da08727a85f5fb04fc83 Mon Sep 17 00:00:00 2001 From: Joel Dean Date: Wed, 31 Jan 2024 17:32:07 -0500 Subject: [PATCH 61/74] Added it to the MultiLiveEvent --- .../android/viewmodel/MultiLiveEvent.kt | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/viewmodel/MultiLiveEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/viewmodel/MultiLiveEvent.kt index 2c8dbcca8d8..8622d77c3de 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/viewmodel/MultiLiveEvent.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/viewmodel/MultiLiveEvent.kt @@ -12,7 +12,9 @@ import com.google.android.material.snackbar.Snackbar import com.woocommerce.android.R.string import com.woocommerce.android.model.UiString import com.woocommerce.android.support.help.HelpOrigin +import com.woocommerce.android.ui.dialog.DialogParams import com.woocommerce.android.ui.dialog.WooDialog +import com.woocommerce.android.ui.dialog.WooDialogFragment import com.woocommerce.android.viewmodel.MultiLiveEvent.Event import java.util.concurrent.atomic.AtomicBoolean @@ -129,6 +131,33 @@ open class MultiLiveEvent : MutableLiveData() { data class LaunchUrlInChromeTab(val url: String) : Event() + data class ShowDialogFragment( + @StringRes val titleId: Int? = null, + @StringRes val messageId: Int? = null, + @StringRes val positiveButtonId: Int? = null, + @StringRes val negativeButtonId: Int? = null, + @StringRes val neutralButtonId: Int? = null, + val positiveActionName: String? = null, + val negativeActionName: String? = null, + val neutralActionName: String? = null, + val cancelable: Boolean = true + ) : Event() { + fun showIn(fragmentManager: androidx.fragment.app.FragmentManager, listener: WooDialogFragment.DialogInteractionListener) { + val dialogParams = DialogParams( + titleId = titleId, + messageId = messageId, + positiveButtonId = positiveButtonId, + negativeButtonId = negativeButtonId, + neutralButtonId = neutralButtonId, + cancelable = cancelable + ) + val dialogFragment = WooDialogFragment.newInstance(dialogParams).apply { + setDialogInteractionListener(listener) + } + dialogFragment.show(fragmentManager, WooDialogFragment.TAG) + } + } + data class ShowDialog( @StringRes val titleId: Int? = null, @StringRes val messageId: Int? = null, From 3ed4e859ed5b1886e3862c06c041e54b206d7130 Mon Sep 17 00:00:00 2001 From: Joel Dean Date: Wed, 31 Jan 2024 17:32:20 -0500 Subject: [PATCH 62/74] Created dialog params for the dialog fragment --- .../woocommerce/android/ui/dialog/DialogParams.kt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dialog/DialogParams.kt diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dialog/DialogParams.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dialog/DialogParams.kt new file mode 100644 index 00000000000..e484ec323b0 --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dialog/DialogParams.kt @@ -0,0 +1,15 @@ +package com.woocommerce.android.ui.dialog + +import android.os.Parcelable +import androidx.annotation.StringRes +import kotlinx.parcelize.Parcelize + +@Parcelize +data class DialogParams( + @StringRes val titleId: Int? = null, + @StringRes val messageId: Int? = null, + @StringRes val positiveButtonId: Int? = null, + @StringRes val negativeButtonId: Int? = null, + @StringRes val neutralButtonId: Int? = null, + val cancelable: Boolean = true +) : Parcelable From f7dccacea64e001af14b2fbb95d8de766f230bdc Mon Sep 17 00:00:00 2001 From: Joel Dean Date: Wed, 31 Jan 2024 17:40:09 -0500 Subject: [PATCH 63/74] Fixed detekt error. --- .../com/woocommerce/android/viewmodel/MultiLiveEvent.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/viewmodel/MultiLiveEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/viewmodel/MultiLiveEvent.kt index 8622d77c3de..6bc238d610b 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/viewmodel/MultiLiveEvent.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/viewmodel/MultiLiveEvent.kt @@ -142,7 +142,10 @@ open class MultiLiveEvent : MutableLiveData() { val neutralActionName: String? = null, val cancelable: Boolean = true ) : Event() { - fun showIn(fragmentManager: androidx.fragment.app.FragmentManager, listener: WooDialogFragment.DialogInteractionListener) { + fun showIn( + fragmentManager: androidx.fragment.app.FragmentManager, + listener: WooDialogFragment.DialogInteractionListener + ) { val dialogParams = DialogParams( titleId = titleId, messageId = messageId, From e7e651ca19b60d8f346b760e2b25db9d78f50afd Mon Sep 17 00:00:00 2001 From: Joel Dean Date: Wed, 31 Jan 2024 17:40:29 -0500 Subject: [PATCH 64/74] Implemented the show dialog fragment for variations. --- .../variations/VariationDetailFragment.kt | 26 ++++++++++++++++++- .../variations/VariationDetailViewModel.kt | 26 ++++++++++--------- 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/variations/VariationDetailFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/variations/VariationDetailFragment.kt index 20f3f4a9d24..ccf61aacef7 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/variations/VariationDetailFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/variations/VariationDetailFragment.kt @@ -31,6 +31,8 @@ import com.woocommerce.android.model.VariantOption import com.woocommerce.android.ui.aztec.AztecEditorFragment import com.woocommerce.android.ui.base.BaseFragment import com.woocommerce.android.ui.base.UIMessageResolver +import com.woocommerce.android.ui.dialog.WooDialogFragment +import com.woocommerce.android.ui.dialog.WooDialogFragment.DialogInteractionListener import com.woocommerce.android.ui.main.MainActivity.Companion.BackPressListener import com.woocommerce.android.ui.products.BaseProductEditorFragment import com.woocommerce.android.ui.products.ProductInventoryViewModel.InventoryData @@ -48,6 +50,7 @@ import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.Exit import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ExitWithResult import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ShowActionSnackbar import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ShowDialog +import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ShowDialogFragment import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ShowSnackbar import com.woocommerce.android.widgets.CustomProgressDialog import com.woocommerce.android.widgets.SkeletonView @@ -61,7 +64,8 @@ class VariationDetailFragment : BaseFragment(R.layout.fragment_variation_detail), BackPressListener, OnGalleryImageInteractionListener, - MenuProvider { + MenuProvider, + DialogInteractionListener { companion object { private const val LIST_STATE_KEY = "list_state" const val KEY_VARIATION_DETAILS_RESULT = "key_variation_details_result" @@ -95,11 +99,18 @@ class VariationDetailFragment : _binding = FragmentVariationDetailBinding.bind(view) + reattachDialogInteractionListener() + requireActivity().addMenuProvider(this, viewLifecycleOwner) initializeViews(savedInstanceState) initializeViewModel() } + private fun reattachDialogInteractionListener() { + val dialogFragment = parentFragmentManager.findFragmentByTag(WooDialogFragment.TAG) as? WooDialogFragment + dialogFragment?.setDialogInteractionListener(this) + } + override fun onDestroyView() { skeletonView.hide() imageUploadErrorsSnackbar?.dismiss() @@ -276,12 +287,25 @@ class VariationDetailFragment : is ExitWithResult<*> -> navigateBackWithResult(KEY_VARIATION_DETAILS_RESULT, event.data) is ShowDialog -> event.showDialog() + is ShowDialogFragment -> event.showIn(parentFragmentManager, this) is Exit -> requireActivity().onBackPressedDispatcher.onBackPressed() else -> event.isHandled = false } } } + override fun onPositiveButtonClicked() { + viewModel.onDeleteVariationConfirmed() + } + + override fun onNegativeButtonClicked() { + viewModel.onDeleteVariationCancelled() + } + + override fun onNeutralButtonClicked() { + // no-op + } + private fun showVariationDetails(variation: ProductVariation) { if (variation.image == null && !viewModel.isUploadingImages()) { binding.imageGallery.hide() diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/variations/VariationDetailViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/variations/VariationDetailViewModel.kt index f3d5941718f..8b502da15bd 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/variations/VariationDetailViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/variations/VariationDetailViewModel.kt @@ -131,18 +131,7 @@ class VariationDetailViewModel @Inject constructor( fun onDeleteVariationClicked() { triggerEvent( - Event.ShowDialog( - positiveBtnAction = { _, _ -> - AnalyticsTracker.track( - AnalyticsEvent.PRODUCT_VARIATION_REMOVE_BUTTON_TAPPED, - mapOf(KEY_PRODUCT_ID to viewState.parentProduct?.remoteId) - ) - viewState = viewState.copy(isConfirmingDeletion = false) - deleteVariation() - }, - negativeBtnAction = { _, _ -> - viewState = viewState.copy(isConfirmingDeletion = false) - }, + Event.ShowDialogFragment( messageId = string.variation_confirm_delete, positiveButtonId = string.delete, negativeButtonId = string.cancel @@ -150,6 +139,19 @@ class VariationDetailViewModel @Inject constructor( ) } + fun onDeleteVariationConfirmed() { + AnalyticsTracker.track( + AnalyticsEvent.PRODUCT_VARIATION_REMOVE_BUTTON_TAPPED, + mapOf(KEY_PRODUCT_ID to viewState.parentProduct?.remoteId) + ) + viewState = viewState.copy(isConfirmingDeletion = false) + deleteVariation() + } + + fun onDeleteVariationCancelled() { + viewState = viewState.copy(isConfirmingDeletion = false) + } + fun onExit() { when { isUploadingImages() -> { From f7dea512e6e20565c5b7a96638c12db833d59bc0 Mon Sep 17 00:00:00 2001 From: Joel Dean Date: Wed, 31 Jan 2024 18:32:15 -0500 Subject: [PATCH 65/74] Disable refresh during search. --- .../com/woocommerce/android/ui/products/ProductListFragment.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductListFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductListFragment.kt index 084ef52d3a6..49866a9841a 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductListFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductListFragment.kt @@ -417,6 +417,9 @@ class ProductListFragment : one = R.string.product_selection_count_single ) } + new.isSearchActive?.takeIfNotEqualTo(old?.isSearchActive) { isSearchActive -> + binding.productsRefreshLayout.isEnabled = !isSearchActive + } } viewModel.productList.observe(viewLifecycleOwner) { From 113660e15c0b907d63bfb958eef4c2bcbf363aef Mon Sep 17 00:00:00 2001 From: malinajirka Date: Thu, 1 Feb 2024 05:21:13 +0100 Subject: [PATCH 66/74] Revert "Implemented the show dialog fragment for variations." This reverts commit e7e651ca19b60d8f346b760e2b25db9d78f50afd. --- .../variations/VariationDetailFragment.kt | 26 +------------------ .../variations/VariationDetailViewModel.kt | 26 +++++++++---------- 2 files changed, 13 insertions(+), 39 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/variations/VariationDetailFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/variations/VariationDetailFragment.kt index ccf61aacef7..20f3f4a9d24 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/variations/VariationDetailFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/variations/VariationDetailFragment.kt @@ -31,8 +31,6 @@ import com.woocommerce.android.model.VariantOption import com.woocommerce.android.ui.aztec.AztecEditorFragment import com.woocommerce.android.ui.base.BaseFragment import com.woocommerce.android.ui.base.UIMessageResolver -import com.woocommerce.android.ui.dialog.WooDialogFragment -import com.woocommerce.android.ui.dialog.WooDialogFragment.DialogInteractionListener import com.woocommerce.android.ui.main.MainActivity.Companion.BackPressListener import com.woocommerce.android.ui.products.BaseProductEditorFragment import com.woocommerce.android.ui.products.ProductInventoryViewModel.InventoryData @@ -50,7 +48,6 @@ import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.Exit import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ExitWithResult import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ShowActionSnackbar import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ShowDialog -import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ShowDialogFragment import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ShowSnackbar import com.woocommerce.android.widgets.CustomProgressDialog import com.woocommerce.android.widgets.SkeletonView @@ -64,8 +61,7 @@ class VariationDetailFragment : BaseFragment(R.layout.fragment_variation_detail), BackPressListener, OnGalleryImageInteractionListener, - MenuProvider, - DialogInteractionListener { + MenuProvider { companion object { private const val LIST_STATE_KEY = "list_state" const val KEY_VARIATION_DETAILS_RESULT = "key_variation_details_result" @@ -99,18 +95,11 @@ class VariationDetailFragment : _binding = FragmentVariationDetailBinding.bind(view) - reattachDialogInteractionListener() - requireActivity().addMenuProvider(this, viewLifecycleOwner) initializeViews(savedInstanceState) initializeViewModel() } - private fun reattachDialogInteractionListener() { - val dialogFragment = parentFragmentManager.findFragmentByTag(WooDialogFragment.TAG) as? WooDialogFragment - dialogFragment?.setDialogInteractionListener(this) - } - override fun onDestroyView() { skeletonView.hide() imageUploadErrorsSnackbar?.dismiss() @@ -287,25 +276,12 @@ class VariationDetailFragment : is ExitWithResult<*> -> navigateBackWithResult(KEY_VARIATION_DETAILS_RESULT, event.data) is ShowDialog -> event.showDialog() - is ShowDialogFragment -> event.showIn(parentFragmentManager, this) is Exit -> requireActivity().onBackPressedDispatcher.onBackPressed() else -> event.isHandled = false } } } - override fun onPositiveButtonClicked() { - viewModel.onDeleteVariationConfirmed() - } - - override fun onNegativeButtonClicked() { - viewModel.onDeleteVariationCancelled() - } - - override fun onNeutralButtonClicked() { - // no-op - } - private fun showVariationDetails(variation: ProductVariation) { if (variation.image == null && !viewModel.isUploadingImages()) { binding.imageGallery.hide() diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/variations/VariationDetailViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/variations/VariationDetailViewModel.kt index 8b502da15bd..f3d5941718f 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/variations/VariationDetailViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/variations/VariationDetailViewModel.kt @@ -131,7 +131,18 @@ class VariationDetailViewModel @Inject constructor( fun onDeleteVariationClicked() { triggerEvent( - Event.ShowDialogFragment( + Event.ShowDialog( + positiveBtnAction = { _, _ -> + AnalyticsTracker.track( + AnalyticsEvent.PRODUCT_VARIATION_REMOVE_BUTTON_TAPPED, + mapOf(KEY_PRODUCT_ID to viewState.parentProduct?.remoteId) + ) + viewState = viewState.copy(isConfirmingDeletion = false) + deleteVariation() + }, + negativeBtnAction = { _, _ -> + viewState = viewState.copy(isConfirmingDeletion = false) + }, messageId = string.variation_confirm_delete, positiveButtonId = string.delete, negativeButtonId = string.cancel @@ -139,19 +150,6 @@ class VariationDetailViewModel @Inject constructor( ) } - fun onDeleteVariationConfirmed() { - AnalyticsTracker.track( - AnalyticsEvent.PRODUCT_VARIATION_REMOVE_BUTTON_TAPPED, - mapOf(KEY_PRODUCT_ID to viewState.parentProduct?.remoteId) - ) - viewState = viewState.copy(isConfirmingDeletion = false) - deleteVariation() - } - - fun onDeleteVariationCancelled() { - viewState = viewState.copy(isConfirmingDeletion = false) - } - fun onExit() { when { isUploadingImages() -> { From df916a05044997e507e881f1ee21c1798128c906 Mon Sep 17 00:00:00 2001 From: malinajirka Date: Thu, 1 Feb 2024 05:21:13 +0100 Subject: [PATCH 67/74] Revert "Fixed detekt error." This reverts commit f7dccacea64e001af14b2fbb95d8de766f230bdc. --- .../com/woocommerce/android/viewmodel/MultiLiveEvent.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/viewmodel/MultiLiveEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/viewmodel/MultiLiveEvent.kt index 6bc238d610b..8622d77c3de 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/viewmodel/MultiLiveEvent.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/viewmodel/MultiLiveEvent.kt @@ -142,10 +142,7 @@ open class MultiLiveEvent : MutableLiveData() { val neutralActionName: String? = null, val cancelable: Boolean = true ) : Event() { - fun showIn( - fragmentManager: androidx.fragment.app.FragmentManager, - listener: WooDialogFragment.DialogInteractionListener - ) { + fun showIn(fragmentManager: androidx.fragment.app.FragmentManager, listener: WooDialogFragment.DialogInteractionListener) { val dialogParams = DialogParams( titleId = titleId, messageId = messageId, From 6e23fb353e812d77d83872fb3819e3dc17b0cee1 Mon Sep 17 00:00:00 2001 From: malinajirka Date: Thu, 1 Feb 2024 05:21:13 +0100 Subject: [PATCH 68/74] Revert "Created dialog params for the dialog fragment" This reverts commit 3ed4e859ed5b1886e3862c06c041e54b206d7130. --- .../woocommerce/android/ui/dialog/DialogParams.kt | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dialog/DialogParams.kt diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dialog/DialogParams.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dialog/DialogParams.kt deleted file mode 100644 index e484ec323b0..00000000000 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dialog/DialogParams.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.woocommerce.android.ui.dialog - -import android.os.Parcelable -import androidx.annotation.StringRes -import kotlinx.parcelize.Parcelize - -@Parcelize -data class DialogParams( - @StringRes val titleId: Int? = null, - @StringRes val messageId: Int? = null, - @StringRes val positiveButtonId: Int? = null, - @StringRes val negativeButtonId: Int? = null, - @StringRes val neutralButtonId: Int? = null, - val cancelable: Boolean = true -) : Parcelable From 94e32eec2538d9c678830d604af30f6013172ae5 Mon Sep 17 00:00:00 2001 From: malinajirka Date: Thu, 1 Feb 2024 05:21:13 +0100 Subject: [PATCH 69/74] Revert "Added it to the MultiLiveEvent" This reverts commit 1dc828047f9caeb65087da08727a85f5fb04fc83. --- .../android/viewmodel/MultiLiveEvent.kt | 29 ------------------- 1 file changed, 29 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/viewmodel/MultiLiveEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/viewmodel/MultiLiveEvent.kt index 8622d77c3de..2c8dbcca8d8 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/viewmodel/MultiLiveEvent.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/viewmodel/MultiLiveEvent.kt @@ -12,9 +12,7 @@ import com.google.android.material.snackbar.Snackbar import com.woocommerce.android.R.string import com.woocommerce.android.model.UiString import com.woocommerce.android.support.help.HelpOrigin -import com.woocommerce.android.ui.dialog.DialogParams import com.woocommerce.android.ui.dialog.WooDialog -import com.woocommerce.android.ui.dialog.WooDialogFragment import com.woocommerce.android.viewmodel.MultiLiveEvent.Event import java.util.concurrent.atomic.AtomicBoolean @@ -131,33 +129,6 @@ open class MultiLiveEvent : MutableLiveData() { data class LaunchUrlInChromeTab(val url: String) : Event() - data class ShowDialogFragment( - @StringRes val titleId: Int? = null, - @StringRes val messageId: Int? = null, - @StringRes val positiveButtonId: Int? = null, - @StringRes val negativeButtonId: Int? = null, - @StringRes val neutralButtonId: Int? = null, - val positiveActionName: String? = null, - val negativeActionName: String? = null, - val neutralActionName: String? = null, - val cancelable: Boolean = true - ) : Event() { - fun showIn(fragmentManager: androidx.fragment.app.FragmentManager, listener: WooDialogFragment.DialogInteractionListener) { - val dialogParams = DialogParams( - titleId = titleId, - messageId = messageId, - positiveButtonId = positiveButtonId, - negativeButtonId = negativeButtonId, - neutralButtonId = neutralButtonId, - cancelable = cancelable - ) - val dialogFragment = WooDialogFragment.newInstance(dialogParams).apply { - setDialogInteractionListener(listener) - } - dialogFragment.show(fragmentManager, WooDialogFragment.TAG) - } - } - data class ShowDialog( @StringRes val titleId: Int? = null, @StringRes val messageId: Int? = null, From 6bdba76c76667282f4add6c3930b063fb9bf6433 Mon Sep 17 00:00:00 2001 From: malinajirka Date: Thu, 1 Feb 2024 05:21:14 +0100 Subject: [PATCH 70/74] Revert "Created a dialog fragment that can survive config changes." This reverts commit 217f8d692d9e21dde790ce3bb095310e6b469a16. --- .../android/ui/dialog/WooDialogFragment.kt | 60 ------------------- 1 file changed, 60 deletions(-) delete mode 100644 WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dialog/WooDialogFragment.kt diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dialog/WooDialogFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dialog/WooDialogFragment.kt deleted file mode 100644 index 83dd8d2c247..00000000000 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dialog/WooDialogFragment.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.woocommerce.android.ui.dialog - -import android.app.Dialog -import android.os.Bundle -import androidx.fragment.app.DialogFragment -import com.google.android.material.dialog.MaterialAlertDialogBuilder - -class WooDialogFragment : DialogFragment() { - - private var dialogInteractionListener: DialogInteractionListener? = null - - fun setDialogInteractionListener(listener: DialogInteractionListener) { - dialogInteractionListener = listener - } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - @Suppress("DEPRECATION") val params = requireArguments().getParcelable(ARG_DIALOG_PARAMS)!! - - val builder = MaterialAlertDialogBuilder(requireContext()) - .setCancelable(params.cancelable) - - params.titleId?.let { builder.setTitle(it) } - params.messageId?.let { builder.setMessage(it) } - params.positiveButtonId?.let { posId -> - builder.setPositiveButton(posId) { _, _ -> - dialogInteractionListener?.onPositiveButtonClicked() - } - } - params.negativeButtonId?.let { negId -> - builder.setNegativeButton(negId) { _, _ -> - dialogInteractionListener?.onNegativeButtonClicked() - } - } - params.neutralButtonId?.let { neutId -> - builder.setNeutralButton(neutId) { _, _ -> - dialogInteractionListener?.onNeutralButtonClicked() - } - } - - return builder.create() - } - - interface DialogInteractionListener { - fun onPositiveButtonClicked() - fun onNegativeButtonClicked() - fun onNeutralButtonClicked() - } - - companion object { - const val ARG_DIALOG_PARAMS = "dialog_params" - const val TAG = "WooDialogFragment" - fun newInstance(params: DialogParams): WooDialogFragment { - return WooDialogFragment().apply { - arguments = Bundle().apply { - putParcelable(ARG_DIALOG_PARAMS, params) - } - } - } - } -} From 6898b8354fdf0c3d1ce39252bb7fb40f5bdc02e4 Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 1 Feb 2024 10:55:26 +0100 Subject: [PATCH 71/74] Reverted string renaming to avoid unnecessary changes --- .../payments/cardreader/payment/CardReaderPaymentViewModel.kt | 4 ++-- .../ui/payments/receipt/preview/ReceiptPreviewViewModel.kt | 2 +- WooCommerce/src/main/res/values-ar/strings.xml | 2 +- WooCommerce/src/main/res/values-de/strings.xml | 2 +- WooCommerce/src/main/res/values-es/strings.xml | 2 +- WooCommerce/src/main/res/values-fr/strings.xml | 2 +- WooCommerce/src/main/res/values-he/strings.xml | 2 +- WooCommerce/src/main/res/values-id/strings.xml | 2 +- WooCommerce/src/main/res/values-it/strings.xml | 2 +- WooCommerce/src/main/res/values-ja/strings.xml | 2 +- WooCommerce/src/main/res/values-ko/strings.xml | 2 +- WooCommerce/src/main/res/values-nl/strings.xml | 2 +- WooCommerce/src/main/res/values-pt-rBR/strings.xml | 2 +- WooCommerce/src/main/res/values-ru/strings.xml | 2 +- WooCommerce/src/main/res/values-sv/strings.xml | 2 +- WooCommerce/src/main/res/values-tr/strings.xml | 2 +- WooCommerce/src/main/res/values-zh-rCN/strings.xml | 2 +- WooCommerce/src/main/res/values-zh-rTW/strings.xml | 2 +- WooCommerce/src/main/res/values/strings.xml | 2 +- .../ui/payments/cardreader/CardReaderPaymentViewModelTest.kt | 2 +- .../payments/receipt/preview/ReceiptPreviewViewModelTest.kt | 2 +- 21 files changed, 22 insertions(+), 22 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModel.kt index fc52388c3f9..de62a7ab603 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/payment/CardReaderPaymentViewModel.kt @@ -742,7 +742,7 @@ class CardReaderPaymentViewModel } is PaymentReceiptShare.ReceiptShareResult.Error.Sharing -> { tracker.trackPaymentsReceiptSharingFailed(sharingResult) - triggerEvent(ShowSnackbar(R.string.card_reader_payment_receipt_app_to_share_not_found)) + triggerEvent(ShowSnackbar(R.string.card_reader_payment_email_client_not_found)) } PaymentReceiptShare.ReceiptShareResult.Success -> { // no-op @@ -758,7 +758,7 @@ class CardReaderPaymentViewModel fun onEmailActivityNotFound() { tracker.trackEmailReceiptFailed() - triggerEvent(ShowSnackbarInDialog(R.string.card_reader_payment_receipt_app_to_share_not_found)) + triggerEvent(ShowSnackbarInDialog(R.string.card_reader_payment_email_client_not_found)) } fun onPrintResult(result: PrintJobResult) { diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt index ed7aa503ec5..b74846b8e71 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModel.kt @@ -67,7 +67,7 @@ class ReceiptPreviewViewModel } is PaymentReceiptShare.ReceiptShareResult.Error.Sharing -> { paymentsFlowTracker.trackPaymentsReceiptSharingFailed(sharingResult) - triggerEvent(ShowSnackbar(string.card_reader_payment_receipt_app_to_share_not_found)) + triggerEvent(ShowSnackbar(string.card_reader_payment_email_client_not_found)) } PaymentReceiptShare.ReceiptShareResult.Success -> { // no-op diff --git a/WooCommerce/src/main/res/values-ar/strings.xml b/WooCommerce/src/main/res/values-ar/strings.xml index 76aedcc43e9..cc49beffa7f 100644 --- a/WooCommerce/src/main/res/values-ar/strings.xml +++ b/WooCommerce/src/main/res/values-ar/strings.xml @@ -1910,7 +1910,7 @@ Language: ar سمات المجموعات تشغيل بلوتوث الجهاز المحمول حدث خطأ في أثناء إحضار الطلب. قد تكون حالة الطلب الموجودة في التطبيق قديمة. - لا يمكن اكتشاف تطبيق عميل البريد الإلكتروني الخاص بك + لا يمكن اكتشاف تطبيق عميل البريد الإلكتروني الخاص بك إيصالك من %s تحديث الطلب تحديث حالة التطبيق diff --git a/WooCommerce/src/main/res/values-de/strings.xml b/WooCommerce/src/main/res/values-de/strings.xml index cf4d312a3a1..f069ba46c0f 100644 --- a/WooCommerce/src/main/res/values-de/strings.xml +++ b/WooCommerce/src/main/res/values-de/strings.xml @@ -1910,7 +1910,7 @@ Language: de Variantenattribute Aktiviere Bluetooth auf dem Mobilgerät Fehler beim Abrufen der Bestellung Der Bestellstatus in der App ist eventuell nicht aktuell. - E-Mail-Client-App wird nicht erkannt. + E-Mail-Client-App wird nicht erkannt. Dein Beleg vom %s Bestellung aktualisieren App-Status aktualisieren diff --git a/WooCommerce/src/main/res/values-es/strings.xml b/WooCommerce/src/main/res/values-es/strings.xml index fe0e0d5bd54..8e32a914850 100644 --- a/WooCommerce/src/main/res/values-es/strings.xml +++ b/WooCommerce/src/main/res/values-es/strings.xml @@ -1910,7 +1910,7 @@ Language: es Atributos de variaciones Activa Bluetooth en el dispositivo móvil Error al recuperar el pedido. El estado del pedido en la aplicación podría estar desactualizado. - No se pudo detectar una aplicación cliente de correo electrónico + No se pudo detectar una aplicación cliente de correo electrónico Tu recibo de %s Actualizando pedido Actualizando estado en la aplicación diff --git a/WooCommerce/src/main/res/values-fr/strings.xml b/WooCommerce/src/main/res/values-fr/strings.xml index f18596537c6..2fdba76735a 100644 --- a/WooCommerce/src/main/res/values-fr/strings.xml +++ b/WooCommerce/src/main/res/values-fr/strings.xml @@ -1910,7 +1910,7 @@ Language: fr Attributs des variantes Activer le Bluetooth de l’appareil mobile Erreur lors de l’extraction de la commande. L’état de la commande dans l’application est peut-être obsolète. - Aucune application de messagerie détectée + Aucune application de messagerie détectée Votre reçu de %s Actualisation de la commande Mise à jour de l’état de l’application diff --git a/WooCommerce/src/main/res/values-he/strings.xml b/WooCommerce/src/main/res/values-he/strings.xml index 8622401ba37..4e5a67434b2 100644 --- a/WooCommerce/src/main/res/values-he/strings.xml +++ b/WooCommerce/src/main/res/values-he/strings.xml @@ -1910,7 +1910,7 @@ Language: he_IL מאפייני סוגים יש להפעיל את חיבור ה-Bluetooth של המכשיר הנייד שגיאה בהבאת ההזמנה. ייתכן שמצב ההזמנה באפליקציה לא מעודכן. - לא ניתן לאתר את האפליקציה של שירות האימייל שלך + לא ניתן לאתר את האפליקציה של שירות האימייל שלך הקבלה שלך מאת %s מרענן את ההזמנה מעדכן את מצב האפליקציה diff --git a/WooCommerce/src/main/res/values-id/strings.xml b/WooCommerce/src/main/res/values-id/strings.xml index b7315da284f..fa29a8b2c73 100644 --- a/WooCommerce/src/main/res/values-id/strings.xml +++ b/WooCommerce/src/main/res/values-id/strings.xml @@ -1910,7 +1910,7 @@ Language: id Atribut variasi Aktifkan bluetooth perangkat seluler Terjadi error saat mengambil pesanan. Status pesanan pada aplikasi mungkin telah usang. - Tidak dapat mendeteksi aplikasi klien email Anda + Tidak dapat mendeteksi aplikasi klien email Anda Tanda terima Anda dari %s Menyegarkan pesanan Memperbarui status aplikasi diff --git a/WooCommerce/src/main/res/values-it/strings.xml b/WooCommerce/src/main/res/values-it/strings.xml index cae8e5ca689..4f1d803552e 100644 --- a/WooCommerce/src/main/res/values-it/strings.xml +++ b/WooCommerce/src/main/res/values-it/strings.xml @@ -1910,7 +1910,7 @@ Language: it Attributi della variante Attiva il Bluetooth del dispositivo mobile Errore durante il recupero dell\'ordine. Lo stato nell\'ordine dell\'app potrebbe essere obsoleto. - Impossibile rilevare il client e-mail della tua app + Impossibile rilevare il client e-mail della tua app La tua ricevuta da %s Aggiornamento dell\'ordine Aggiornamento dello stato dell\'app diff --git a/WooCommerce/src/main/res/values-ja/strings.xml b/WooCommerce/src/main/res/values-ja/strings.xml index 57f251429ea..ce4d1990c3b 100644 --- a/WooCommerce/src/main/res/values-ja/strings.xml +++ b/WooCommerce/src/main/res/values-ja/strings.xml @@ -1910,7 +1910,7 @@ Language: ja_JP バリエーション属性 モバイルデバイスの Bluetooth をオンにする 注文を取得する際にエラーが発生しました。 アプリの注文状態が古い可能性があります。 - メールクライアントアプリを検出できません + メールクライアントアプリを検出できません %s のレシート 注文を更新する アプリの状態を更新する diff --git a/WooCommerce/src/main/res/values-ko/strings.xml b/WooCommerce/src/main/res/values-ko/strings.xml index f445739f0b5..70f0935b61d 100644 --- a/WooCommerce/src/main/res/values-ko/strings.xml +++ b/WooCommerce/src/main/res/values-ko/strings.xml @@ -1910,7 +1910,7 @@ Language: ko_KR 변형 속성 모바일 장치 블루투스 켜기 주문을 가져오는 중 오류가 발생했습니다. 앱의 주문 상태가 기한이 지났을 수 있습니다. - 이메일 클라이언트 앱을 감지할 수 없습니다. + 이메일 클라이언트 앱을 감지할 수 없습니다. %s에서 받은 영수증 주문 새로 고치기 앱 상태 업데이트하기 diff --git a/WooCommerce/src/main/res/values-nl/strings.xml b/WooCommerce/src/main/res/values-nl/strings.xml index b60a6ba5886..e53a15e7869 100644 --- a/WooCommerce/src/main/res/values-nl/strings.xml +++ b/WooCommerce/src/main/res/values-nl/strings.xml @@ -1903,7 +1903,7 @@ Language: nl Variatie-eigenschappen Zet de Bluetooth aan op je mobiele apparaat Fout bij ophalen bestelling. De bestellingsstatus in de app kan verouderd zijn. - Detectie van mailclient-app mislukt + Detectie van mailclient-app mislukt Je kwitantie van %s Bestelling aan het vernieuwen… Status in de app aan het bijwerken… diff --git a/WooCommerce/src/main/res/values-pt-rBR/strings.xml b/WooCommerce/src/main/res/values-pt-rBR/strings.xml index ecde05b45f3..09d637b857d 100644 --- a/WooCommerce/src/main/res/values-pt-rBR/strings.xml +++ b/WooCommerce/src/main/res/values-pt-rBR/strings.xml @@ -1910,7 +1910,7 @@ Language: pt_BR Atributos das variações Ativar o Bluetooth do dispositivo móvel Erro ao buscar o pedido. O status do pedido pode estar desatualizado no aplicativo. - Não é possível detectar seu aplicativo cliente de e-mail + Não é possível detectar seu aplicativo cliente de e-mail Seu recibo de %s Atualizando pedido Atualizando estado do aplicativo diff --git a/WooCommerce/src/main/res/values-ru/strings.xml b/WooCommerce/src/main/res/values-ru/strings.xml index cc35e4e790d..a04d66d7e2f 100644 --- a/WooCommerce/src/main/res/values-ru/strings.xml +++ b/WooCommerce/src/main/res/values-ru/strings.xml @@ -1910,7 +1910,7 @@ Language: ru Атрибуты вариантов Включите Bluetooth на мобильном устройстве Ошибка при загрузке заказа. Состояние заказа в приложении могло устареть. - Не удалось определить ваш почтовый клиент + Не удалось определить ваш почтовый клиент Ваша квитанция от %s Обновление заказа Обновление состояния приложения diff --git a/WooCommerce/src/main/res/values-sv/strings.xml b/WooCommerce/src/main/res/values-sv/strings.xml index 9e4e1cf2148..977cd03c403 100644 --- a/WooCommerce/src/main/res/values-sv/strings.xml +++ b/WooCommerce/src/main/res/values-sv/strings.xml @@ -1917,7 +1917,7 @@ Language: sv_SE Börja sälja idag genom att lägga till din första produkt i butiken. Variationsattribut Aktivera Bluetooth på den mobila enheten - Lyckas inte hitta ditt e-postprogram + Lyckas inte hitta ditt e-postprogram Uppdaterar beställning Uppdaterar appstatusen Det gick inte att hämta beställningen. Beställningens status i appen kan vara föråldrad. diff --git a/WooCommerce/src/main/res/values-tr/strings.xml b/WooCommerce/src/main/res/values-tr/strings.xml index bf6d75a1ca7..70eec362196 100644 --- a/WooCommerce/src/main/res/values-tr/strings.xml +++ b/WooCommerce/src/main/res/values-tr/strings.xml @@ -1910,7 +1910,7 @@ Language: tr Varyasyon nitelikleri. Mobil cihazda Bluetooth\'u açın Sipariş alınırken hata oluştu Uygulamadaki sipariş durumu eski olabilir. - E-posta istemcisi uygulamanızı tespit edemiyoruz + E-posta istemcisi uygulamanızı tespit edemiyoruz %s makbuzunuz Sipariş yenileniyor Uygulama durumu güncelleniyor diff --git a/WooCommerce/src/main/res/values-zh-rCN/strings.xml b/WooCommerce/src/main/res/values-zh-rCN/strings.xml index 1a835ed6868..2094348f61f 100644 --- a/WooCommerce/src/main/res/values-zh-rCN/strings.xml +++ b/WooCommerce/src/main/res/values-zh-rCN/strings.xml @@ -1909,7 +1909,7 @@ Language: zh_CN 添加产品 变体属性 打开移动设备蓝牙 - 未侦测到您的电子邮件客户端应用 + 未侦测到您的电子邮件客户端应用 来自 %s 的收据 刷新订单 更新应用程序状态 diff --git a/WooCommerce/src/main/res/values-zh-rTW/strings.xml b/WooCommerce/src/main/res/values-zh-rTW/strings.xml index 2dcd4dcbef0..0c9892b1cae 100644 --- a/WooCommerce/src/main/res/values-zh-rTW/strings.xml +++ b/WooCommerce/src/main/res/values-zh-rTW/strings.xml @@ -1910,7 +1910,7 @@ Language: zh_TW 款式屬性 開啟行動裝置的藍牙 擷取訂單時發生錯誤。 應用程式中的訂單狀態可能已過期。 - 無法偵測電子郵件用戶端應用程式 + 無法偵測電子郵件用戶端應用程式 你的 %s 收據 正在重新整理訂單 正在更新應用程式狀態 diff --git a/WooCommerce/src/main/res/values/strings.xml b/WooCommerce/src/main/res/values/strings.xml index 7a855966ae3..7ef7a5426aa 100644 --- a/WooCommerce/src/main/res/values/strings.xml +++ b/WooCommerce/src/main/res/values/strings.xml @@ -1507,7 +1507,7 @@ In-Person Payment for Order #%1$s for %2$s blog_id %3$s. Your receipt from %s Thank you for your purchase! Click the link below for your payment receipt.\n\n%s - Unable to detect any application to which the receipt can be shared + Unable to detect any application to which the receipt can be shared Unable to download the receipt Unable to store the receipt Error fetching order. Order state in the app might be outdated. diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/cardreader/CardReaderPaymentViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/cardreader/CardReaderPaymentViewModelTest.kt index a9cbfa61c5f..d735c8f6aa2 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/cardreader/CardReaderPaymentViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/cardreader/CardReaderPaymentViewModelTest.kt @@ -2440,7 +2440,7 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() { (viewModel.viewStateData.value as ExternalReaderPaymentSuccessfulState).onSecondaryActionClicked.invoke() assertThat((viewModel.event.value as ShowSnackbar).message).isEqualTo( - R.string.card_reader_payment_receipt_app_to_share_not_found + R.string.card_reader_payment_email_client_not_found ) verify(tracker).trackPaymentsReceiptSharingFailed(sharing) } diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModelTest.kt index 3a8d0fa2a80..b4e002726cd 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/receipt/preview/ReceiptPreviewViewModelTest.kt @@ -147,7 +147,7 @@ class ReceiptPreviewViewModelTest : BaseUnitTest() { // THEN assertThat((viewModel.event.value as MultiLiveEvent.Event.ShowSnackbar).message).isEqualTo( - R.string.card_reader_payment_receipt_app_to_share_not_found + R.string.card_reader_payment_email_client_not_found ) verify(paymentsFlowTracker).trackPaymentsReceiptSharingFailed( sharing From c283af71e87b8160fe9955471c0e3b9c332e92f9 Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 1 Feb 2024 11:00:59 +0100 Subject: [PATCH 72/74] Better wording for release notes so its clear for the release manager --- RELEASE-NOTES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 68bab4ccc5a..f72f48c6e92 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -1,7 +1,7 @@ *** PLEASE FOLLOW THIS FORMAT: [] [] 17.2 ----- -- [**] (For users with WooCommerce version of 8.7+) Every order have a receipt now. The receipts can be shared via many apps installed on the phone [https://github.com/woocommerce/woocommerce-android/pull/10650] +- [**] [Available for users with WooCommerce version of 8.7+, which is not released yet] Every order have a receipt now. The receipts can be shared via many apps installed on the phone [https://github.com/woocommerce/woocommerce-android/pull/10650] 17.1 ----- From 169758b11d516d2b4547fd9b0189f95fdd61a9c4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 14:35:15 +0000 Subject: [PATCH 73/74] Bump androidx.core:core-ktx from 1.8.0 to 1.12.0 Bumps androidx.core:core-ktx from 1.8.0 to 1.12.0. --- updated-dependencies: - dependency-name: androidx.core:core-ktx dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 55a46108b90..06dba49ced5 100644 --- a/build.gradle +++ b/build.gradle @@ -108,7 +108,7 @@ ext { aztecVersion = 'v1.3.45' flipperVersion = '0.176.1' stateMachineVersion = '0.2.0' - coreKtxVersion = '1.8.0' + coreKtxVersion = '1.12.0' appCompatVersion = '1.4.2' materialVersion = '1.6.1' hiltJetpackVersion = '1.0.0' From bd89dfb4c2229dc1e227daf8638acb3358e08666 Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 2 Feb 2024 09:08:18 +0100 Subject: [PATCH 74/74] Updated fluxc release --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 55a46108b90..47c8eb55add 100644 --- a/build.gradle +++ b/build.gradle @@ -96,7 +96,7 @@ tasks.register("installGitHooks", Copy) { } ext { - fluxCVersion = '2948-235834ae9467ae3ca367a4b13c4f9c6aea0f31bf' + fluxCVersion = 'trunk-b34cc5eed609ca5274fbb442991f657f2380de17' glideVersion = '4.13.2' coilVersion = '2.1.0' constraintLayoutVersion = '1.2.0'