diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailFragment.kt index f652d0c4dd5..9590be95f3b 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailFragment.kt @@ -254,7 +254,7 @@ class OrderDetailFragment : private fun setupObservers(viewModel: OrderDetailViewModel) { viewModel.viewStateData.observe(viewLifecycleOwner) { old, new -> new.orderInfo?.takeIfNotEqualTo(old?.orderInfo) { - showOrderDetail(it.order!!, it.isPaymentCollectableWithCardReader, it.isReceiptButtonsVisible) + showOrderDetail(it.order!!, it.isPaymentCollectableWithCardReader, it.receiptButtonStatus) requireActivity().invalidateOptionsMenu() } new.orderStatus?.takeIfNotEqualTo(old?.orderStatus) { showOrderStatus(it) } @@ -437,7 +437,7 @@ class OrderDetailFragment : private fun showOrderDetail( order: Order, isPaymentCollectableWithCardReader: Boolean, - isReceiptButtonsVisible: Boolean + receiptButtonStatus: OrderDetailViewState.ReceiptButtonStatus ) { binding.orderDetailOrderStatus.updateOrder(order) binding.orderDetailShippingMethodNotice.isVisible = order.hasMultipleShippingLines @@ -449,7 +449,7 @@ class OrderDetailFragment : binding.orderDetailPaymentInfo.updatePaymentInfo( order = order, isPaymentCollectableWithCardReader = isPaymentCollectableWithCardReader, - isReceiptAvailable = isReceiptButtonsVisible, + receiptButtonStatus = receiptButtonStatus, formatCurrencyForDisplay = currencyFormatter.buildBigDecimalFormatter(order.currency), onIssueRefundClickListener = { viewModel.onIssueOrderRefundClicked() }, onSeeReceiptClickListener = { 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..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 ) ) } @@ -347,7 +351,21 @@ class OrderDetailViewModel @Inject constructor( fun onSeeReceiptClicked() { launch { tracker.trackReceiptViewTapped(order.id, order.status) + + viewState = viewState.copy( + orderInfo = viewState.orderInfo?.copy( + receiptButtonStatus = OrderDetailViewState.ReceiptButtonStatus.Loading + ) + ) + val receiptResult = paymentReceiptHelper.getReceiptUrl(order.id) + + viewState = viewState.copy( + orderInfo = viewState.orderInfo?.copy( + receiptButtonStatus = OrderDetailViewState.ReceiptButtonStatus.Visible + ) + ) + if (receiptResult.isSuccess) { triggerEvent(PreviewReceipt(order.billingAddress.email, receiptResult.getOrThrow(), order.id)) } else { @@ -581,7 +599,11 @@ class OrderDetailViewModel @Inject constructor( orderInfo = OrderDetailViewState.OrderInfo( order = order, isPaymentCollectableWithCardReader = isPaymentCollectable, - isReceiptButtonsVisible = paymentReceiptHelper.isReceiptAvailable(order.id) && order.isOrderPaid, + receiptButtonStatus = if (paymentReceiptHelper.isReceiptAvailable(order.id) && order.isOrderPaid) { + OrderDetailViewState.ReceiptButtonStatus.Visible + } else { + OrderDetailViewState.ReceiptButtonStatus.Hidden + } ), orderStatus = orderStatus, toolbarTitle = resourceProvider.getString( diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailViewState.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailViewState.kt index 77a0558726a..78d2d9736fb 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailViewState.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/details/OrderDetailViewState.kt @@ -33,6 +33,10 @@ data class OrderDetailViewState( data class OrderInfo( val order: Order? = null, val isPaymentCollectableWithCardReader: Boolean = false, - val isReceiptButtonsVisible: Boolean = false + val receiptButtonStatus: ReceiptButtonStatus = ReceiptButtonStatus.Hidden, ) : Parcelable + + enum class ReceiptButtonStatus { + Loading, Hidden, Visible + } } 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 bc5384b9fc2..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 @@ -17,6 +17,7 @@ import com.woocommerce.android.extensions.show import com.woocommerce.android.model.GiftCardSummary import com.woocommerce.android.model.Order import com.woocommerce.android.model.Refund +import com.woocommerce.android.ui.orders.details.OrderDetailViewState import com.woocommerce.android.ui.orders.details.adapter.OrderDetailRefundsAdapter import com.woocommerce.android.ui.orders.details.adapter.OrderDetailRefundsLineBuilder import dagger.hilt.android.AndroidEntryPoint @@ -36,7 +37,7 @@ class OrderDetailPaymentInfoView @JvmOverloads constructor( @Suppress("LongParameterList") fun updatePaymentInfo( order: Order, - isReceiptAvailable: Boolean, + receiptButtonStatus: OrderDetailViewState.ReceiptButtonStatus, isPaymentCollectableWithCardReader: Boolean, formatCurrencyForDisplay: (BigDecimal) -> String, onSeeReceiptClickListener: (view: View) -> Unit, @@ -88,7 +89,7 @@ class OrderDetailPaymentInfoView @JvmOverloads constructor( updateFeesSection(order, formatCurrencyForDisplay) updateRefundSection(order, formatCurrencyForDisplay, onIssueRefundClickListener) updateCollectPaymentSection(order, onCollectPaymentClickListener) - updateSeeReceiptSection(isReceiptAvailable, onSeeReceiptClickListener) + updateSeeReceiptSection(receiptButtonStatus, onSeeReceiptClickListener) updatePrintingInstructionSection(isPaymentCollectableWithCardReader, onPrintingInstructionsClickListener) } @@ -192,16 +193,26 @@ class OrderDetailPaymentInfoView @JvmOverloads constructor( } private fun updateSeeReceiptSection( - isReceiptAvailable: Boolean, + receiptButtonStatus: OrderDetailViewState.ReceiptButtonStatus, onSeeReceiptClickListener: (view: View) -> Unit ) { - if (isReceiptAvailable) { - binding.paymentInfoSeeReceiptButton.visibility = VISIBLE - binding.paymentInfoSeeReceiptButton.setOnClickListener( - onSeeReceiptClickListener - ) - } else { - binding.paymentInfoSeeReceiptButton.visibility = GONE + 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.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 71df778356f..0566a777164 100644 --- a/WooCommerce/src/main/res/layout/order_detail_payment_info.xml +++ b/WooCommerce/src/main/res/layout/order_detail_payment_info.xml @@ -1,7 +1,7 @@ - @@ -370,65 +370,83 @@ tools:text="$34.00"/> - - - - - - - - - + - - - + android:layout_height="wrap_content"> + + + + + + + + + + + + + + + - - + 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 {