Skip to content

Commit

Permalink
Merge pull request #10646 from woocommerce/10634-backend-receipts-loa…
Browse files Browse the repository at this point in the history
…ding-indicator-when-receipt-is-fetching-on-the-order-details-screen

[Backend Receipts] Loading indicator when receipt is fetching on the order details screen
  • Loading branch information
kidinov authored Feb 1, 2024
2 parents eb38367 + 3821eb7 commit 49bead2
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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) }
Expand Down Expand Up @@ -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
Expand All @@ -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 = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
)
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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
)
}
}
}

Expand Down
134 changes: 76 additions & 58 deletions WooCommerce/src/main/res/layout/order_detail_payment_info.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">

Expand Down Expand Up @@ -370,65 +370,83 @@
tools:text="$34.00"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>

<!-- Refund button & In-Person Payments section -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<!-- See Receipt button -->
<com.google.android.material.button.MaterialButton
android:id="@+id/paymentInfo_seeReceiptButton"
style="@style/Woo.Button.TextButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:layout_marginTop="@dimen/minor_100"
android:gravity="start|center_vertical"
android:paddingStart="@dimen/major_100"
android:paddingEnd="@dimen/major_100"
android:text="@string/orderdetail_see_receipt_button"
android:textAllCaps="false" />

<!-- Accept Card Present Payment button -->
<com.google.android.material.button.MaterialButton
android:id="@+id/paymentInfo_collectCardPresentPaymentButton"
style="@style/Woo.Button.Colored"
<!-- Refund button & In-Person Payments section -->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/major_100"
android:layout_marginEnd="@dimen/major_100"
android:layout_marginTop="@dimen/minor_100"
android:layout_gravity="end|center_vertical"
android:text="@string/orderdetail_collect_payment_button"/>

<!-- Refund button -->
<com.google.android.material.button.MaterialButton
android:id="@+id/paymentInfo_issueRefundButton"
style="@style/Woo.Button.Outlined"
android:layout_width="match_parent"
android:layout_marginStart="@dimen/major_100"
android:layout_marginEnd="@dimen/major_100"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:text="@string/orderdetail_issue_refund_button"/>
android:layout_height="wrap_content">

<!-- See Receipt button -->
<com.google.android.material.button.MaterialButton
android:id="@+id/paymentInfo_seeReceiptButton"
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:text="@string/orderdetail_see_receipt_button"
android:textAllCaps="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<ProgressBar
android:id="@+id/paymentInfo_seeReceiptButton_progressBar"
android:layout_width="@dimen/major_100"
android:layout_height="@dimen/major_100"
android:layout_marginEnd="@dimen/major_100"
app:layout_constraintBottom_toBottomOf="@id/paymentInfo_seeReceiptButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/paymentInfo_seeReceiptButton" />

<!-- Accept Card Present Payment button -->
<com.google.android.material.button.MaterialButton
android:id="@+id/paymentInfo_collectCardPresentPaymentButton"
style="@style/Woo.Button.Colored"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/major_100"
android:layout_marginTop="@dimen/minor_100"
android:layout_marginEnd="@dimen/major_100"
android:text="@string/orderdetail_collect_payment_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/paymentInfo_seeReceiptButton" />

<!-- Refund button -->
<com.google.android.material.button.MaterialButton
android:id="@+id/paymentInfo_issueRefundButton"
style="@style/Woo.Button.Outlined"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/major_100"
android:layout_marginEnd="@dimen/major_100"
android:text="@string/orderdetail_issue_refund_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/paymentInfo_collectCardPresentPaymentButton" />

<!-- Show Printing Instructions button -->
<com.google.android.material.textview.MaterialTextView
android:id="@+id/paymentInfo_printingInstructions"
style="@style/Woo.TextView.Caption"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginVertical="@dimen/major_100"
android:layout_marginTop="@dimen/major_100"
android:background="?attr/selectableItemBackground"
android:drawableStart="@drawable/ic_deprecated_info_outline_24dp"
android:drawablePadding="@dimen/major_100"
android:gravity="center_vertical"
android:text="@string/orderdetail_printing_instructions_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/paymentInfo_issueRefundButton" />
</androidx.constraintlayout.widget.ConstraintLayout>

<!-- Show Printing Instructions button -->
<com.google.android.material.textview.MaterialTextView
android:id="@+id/paymentInfo_printingInstructions"
android:gravity="center_vertical"
style="@style/Woo.TextView.Caption"
android:layout_width="match_parent"
android:textColor="@color/color_on_surface_medium"
android:layout_marginVertical="@dimen/major_100"
android:layout_height="wrap_content"
android:drawableStart="@drawable/ic_deprecated_info_outline_24dp"
android:drawablePadding="@dimen/major_100"
android:background="?attr/selectableItemBackground"
android:layout_marginEnd="@dimen/major_100"
android:text="@string/orderdetail_printing_instructions_button"/>
</LinearLayout>

</LinearLayout>
</merge>
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -402,7 +407,9 @@ class OrderDetailViewModelTest : BaseUnitTest() {
viewModel.start()

// THEN
assertThat(detailViewState!!.orderInfo!!.isReceiptButtonsVisible).isFalse()
assertThat(detailViewState!!.orderInfo!!.receiptButtonStatus).isEqualTo(
OrderDetailViewState.ReceiptButtonStatus.Hidden
)
}

@Test
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit 49bead2

Please sign in to comment.