Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[POS] Custom payment UI — Part 2 | Separating CardReaderPaymentController's events from MultiLiveEvent #12870

Merged
merged 4 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package com.woocommerce.android.ui.payments.cardreader.payment
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.LiveData
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.asLiveData
import androidx.lifecycle.map
import androidx.lifecycle.viewModelScope
import com.woocommerce.android.AppPrefs
import com.woocommerce.android.cardreader.CardReaderManager
Expand All @@ -12,6 +14,7 @@ import com.woocommerce.android.ui.orders.details.OrderDetailRepository
import com.woocommerce.android.ui.payments.cardreader.CardReaderCountryConfigProvider
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderOnboardingChecker
import com.woocommerce.android.ui.payments.cardreader.payment.controller.CardReaderPaymentController
import com.woocommerce.android.ui.payments.cardreader.payment.controller.CardReaderPaymentEvent
import com.woocommerce.android.ui.payments.receipt.PaymentReceiptHelper
import com.woocommerce.android.ui.payments.receipt.PaymentReceiptShare
import com.woocommerce.android.ui.payments.tracking.CardReaderTrackingInfoKeeper
Expand Down Expand Up @@ -87,7 +90,23 @@ class CardReaderPaymentViewModel @Inject constructor(

val viewStateData: LiveData<ViewState> = paymentController.viewStateData

override val event: LiveData<MultiLiveEvent.Event> = paymentController.event
override val event: LiveData<MultiLiveEvent.Event> =
paymentController.event.asLiveData(coroutineContext).map {
when (it) {
CardReaderPaymentEvent.ContactSupportTapped -> ContactSupport
CardReaderPaymentEvent.EnableNfcTapped -> EnableNfc
CardReaderPaymentEvent.Exit -> MultiLiveEvent.Event.Exit
CardReaderPaymentEvent.InteracRefundSuccessful -> InteracRefundSuccessful
CardReaderPaymentEvent.PlaySuccessfulPaymentSound -> PlayChaChing
is CardReaderPaymentEvent.PrintReceiptTapped -> PrintReceipt(
it.receiptUrl,
it.documentName
)
is CardReaderPaymentEvent.PurchaseCardReaderTapped -> PurchaseCardReader(it.url)
is CardReaderPaymentEvent.ShowPaymentErrorMessage -> ShowSnackbarInDialog(it.message)
is CardReaderPaymentEvent.ShowErrorMessage -> MultiLiveEvent.Event.ShowSnackbar(it.message)
}
}

fun start() = paymentController.start()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,10 @@ import com.woocommerce.android.ui.payments.cardreader.payment.CardReaderPaymentC
import com.woocommerce.android.ui.payments.cardreader.payment.CardReaderPaymentErrorMapper
import com.woocommerce.android.ui.payments.cardreader.payment.CardReaderPaymentOrderHelper
import com.woocommerce.android.ui.payments.cardreader.payment.CardReaderPaymentReaderTypeStateProvider
import com.woocommerce.android.ui.payments.cardreader.payment.ContactSupport
import com.woocommerce.android.ui.payments.cardreader.payment.EnableNfc
import com.woocommerce.android.ui.payments.cardreader.payment.InteracRefundFlow
import com.woocommerce.android.ui.payments.cardreader.payment.InteracRefundFlowError
import com.woocommerce.android.ui.payments.cardreader.payment.InteracRefundSuccessful
import com.woocommerce.android.ui.payments.cardreader.payment.PaymentFlow
import com.woocommerce.android.ui.payments.cardreader.payment.PaymentFlowError
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.ViewState
import com.woocommerce.android.ui.payments.cardreader.payment.ViewState.BuiltInReaderCollectPaymentState
import com.woocommerce.android.ui.payments.cardreader.payment.ViewState.BuiltInReaderFailedPaymentState
Expand All @@ -93,13 +87,11 @@ 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.util.WooLog
import com.woocommerce.android.viewmodel.MultiLiveEvent
import com.woocommerce.android.viewmodel.MultiLiveEvent.Event
import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.Exit
import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ShowSnackbar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.wordpress.android.fluxc.store.WooCommerceStore
Expand Down Expand Up @@ -147,12 +139,11 @@ class CardReaderPaymentController(
get() = this is CardReaderFlowParam.PaymentOrRefund.Payment &&
this.paymentType == CardReaderFlowParam.PaymentOrRefund.Payment.PaymentType.WOO_POS

private val _event: MutableLiveData<Event> = MultiLiveEvent()
val event: LiveData<Event> = _event
private val _event: MutableSharedFlow<CardReaderPaymentEvent> = MutableSharedFlow()
val event: Flow<CardReaderPaymentEvent> = _event

private fun triggerEvent(event: Event) {
event.isHandled = false
_event.value = event
private fun triggerEvent(event: CardReaderPaymentEvent) = scope.launch {
_event.emit(event)
}

fun start() {
Expand Down Expand Up @@ -455,7 +446,7 @@ class CardReaderPaymentController(
ProcessingInteracRefund -> viewState.postValue(ProcessingRefundState(amountLabel))
is InteracRefundSuccess -> {
viewState.postValue(RefundSuccessfulState(amountLabel))
triggerEvent(InteracRefundSuccessful)
triggerEvent(CardReaderPaymentEvent.InteracRefundSuccessful)
}

is InteracRefundFailure -> {
Expand All @@ -481,10 +472,10 @@ class CardReaderPaymentController(
if (paymentOrRefund.isPOS) {
scope.launch {
syncOrderStatus(orderId)
triggerEvent(Exit)
triggerEvent(CardReaderPaymentEvent.Exit)
}
} else {
triggerEvent(PlayChaChing)
triggerEvent(CardReaderPaymentEvent.PlaySuccessfulPaymentSound)
showPaymentSuccessfulState()
reFetchOrder()
}
Expand All @@ -497,9 +488,13 @@ class CardReaderPaymentController(
@VisibleForTesting
fun reFetchOrder() {
refetchOrderJob = scope.launch {
fetchOrder() ?: triggerEvent(ShowSnackbar(R.string.card_reader_refetching_order_failed))
fetchOrder() ?: triggerEvent(
CardReaderPaymentEvent.ShowErrorMessage(
R.string.card_reader_refetching_order_failed
)
)
if (viewState.value == ReFetchingOrderState) {
triggerEvent(Exit)
triggerEvent(CardReaderPaymentEvent.Exit)
}
}
}
Expand Down Expand Up @@ -749,7 +744,7 @@ class CardReaderPaymentController(
val receiptResult = paymentReceiptHelper.getReceiptUrl(paymentOrRefund.orderId)
if (receiptResult.isSuccess) {
triggerEvent(
PrintReceipt(
CardReaderPaymentEvent.PrintReceiptTapped(
receiptResult.getOrThrow(),
cardReaderPaymentOrderHelper.getReceiptDocumentName(paymentOrRefund.orderId)
)
Expand All @@ -758,7 +753,7 @@ class CardReaderPaymentController(
tracker.trackReceiptUrlFetchingFails(
errorDescription = receiptResult.exceptionOrNull()?.message ?: "Unknown error",
)
triggerEvent(ShowSnackbar(R.string.receipt_fetching_error))
triggerEvent(CardReaderPaymentEvent.ShowErrorMessage(R.string.receipt_fetching_error))
}
}
}
Expand All @@ -777,17 +772,27 @@ class CardReaderPaymentController(
) {
is PaymentReceiptShare.ReceiptShareResult.Error.FileCreation -> {
tracker.trackPaymentsReceiptSharingFailed(sharingResult)
triggerEvent(ShowSnackbar(R.string.card_reader_payment_receipt_can_not_be_stored))
triggerEvent(
CardReaderPaymentEvent.ShowErrorMessage(
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))
triggerEvent(
CardReaderPaymentEvent.ShowErrorMessage(
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_email_client_not_found))
triggerEvent(
CardReaderPaymentEvent.ShowErrorMessage(R.string.card_reader_payment_email_client_not_found)
)
}

PaymentReceiptShare.ReceiptShareResult.Success -> {
Expand All @@ -798,7 +803,7 @@ class CardReaderPaymentController(
tracker.trackReceiptUrlFetchingFails(
errorDescription = receiptResult.exceptionOrNull()?.message ?: "Unknown error",
)
triggerEvent(ShowSnackbar(R.string.receipt_fetching_error))
triggerEvent(CardReaderPaymentEvent.ShowErrorMessage(R.string.receipt_fetching_error))
}

viewState.postValue(stateBeforeLoading)
Expand Down Expand Up @@ -831,20 +836,20 @@ class CardReaderPaymentController(
private fun onContactSupportClicked() {
tracker.trackPaymentFailedContactSupportTapped()
onCancelPaymentFlow()
triggerEvent(ContactSupport)
triggerEvent(CardReaderPaymentEvent.ContactSupportTapped)
}

private fun onEnableNfcClicked() {
tracker.trackPaymentFailedEnabledNfcTapped()
onCancelPaymentFlow()
triggerEvent(EnableNfc)
triggerEvent(CardReaderPaymentEvent.EnableNfcTapped)
}

private fun onPurchaseCardReaderClicked() {
onCancelPaymentFlow()
val storeCountryCode = wooStore.getStoreCountryCode(selectedSite.get())
triggerEvent(
PurchaseCardReader(
CardReaderPaymentEvent.PurchaseCardReaderTapped(
"${AppUrls.WOOCOMMERCE_PURCHASE_CARD_READER_IN_COUNTRY}$storeCountryCode"
)
)
Expand All @@ -862,7 +867,7 @@ class CardReaderPaymentController(
viewState.value?.let { state ->
trackCancelledFlow(state)
}
triggerEvent(Exit)
triggerEvent(CardReaderPaymentEvent.Exit)
}
}

Expand Down Expand Up @@ -892,8 +897,8 @@ class CardReaderPaymentController(
}

private fun exitWithSnackbar(@StringRes message: Int) {
triggerEvent(ShowSnackbar(message))
triggerEvent(Exit)
triggerEvent(CardReaderPaymentEvent.ShowErrorMessage(message))
triggerEvent(CardReaderPaymentEvent.Exit)
}

private suspend fun getStoreCountryCode(): String {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.woocommerce.android.ui.payments.cardreader.payment.controller

import androidx.annotation.StringRes

sealed class CardReaderPaymentEvent {

data class ShowPaymentErrorMessage(@StringRes val message: Int) : CardReaderPaymentEvent()

data class ShowErrorMessage(@StringRes val message: Int) : CardReaderPaymentEvent()

data object PlaySuccessfulPaymentSound : CardReaderPaymentEvent()

data object InteracRefundSuccessful : CardReaderPaymentEvent()

data object ContactSupportTapped : CardReaderPaymentEvent()

data object EnableNfcTapped : CardReaderPaymentEvent()

data class PurchaseCardReaderTapped(val url: String) : CardReaderPaymentEvent()

data class PrintReceiptTapped(val receiptUrl: String, val documentName: String) : CardReaderPaymentEvent()

data object Exit : CardReaderPaymentEvent()
}
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,8 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() {
.thenReturn("$DUMMY_CURRENCY_SYMBOL$DUMMY_TOTAL")
whenever(cardReaderPaymentOrderHelper.getReceiptDocumentName(mockedOrder.id)).thenReturn("receipt-order-1")
whenever(cardReaderManager.batteryStatus).thenAnswer { flow { emit(CardReaderBatteryStatus.Unknown) } }

viewModel.event.observeForever {}
}

//region - Payments tests
Expand Down Expand Up @@ -785,7 +787,6 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() {
whenever(cardReaderManager.collectPayment(any())).thenAnswer {
flow { emit(PaymentCompleted("")) }
}

viewModel.start()
val events = mutableListOf<Event>()
viewModel.event.observeForever {
Expand Down Expand Up @@ -1398,7 +1399,6 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() {
}
whenever(wooStore.getStoreCountryCode(siteModel)).thenReturn("US")
initViewModel(BUILT_IN)

viewModel.start()
(viewModel.viewStateData.value as BuiltInReaderFailedPaymentState).onPrimaryActionClicked.invoke()

Expand All @@ -1422,7 +1422,6 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() {
whenever(cardReaderManager.collectPayment(any())).thenAnswer {
flow { emit(paymentFailedWithAmountTooSmall) }
}

viewModel.start()
(viewModel.viewStateData.value as ExternalReaderFailedPaymentState).onPrimaryActionClicked.invoke()

Expand Down Expand Up @@ -1628,7 +1627,6 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() {
whenever(cardReaderManager.collectPayment(any())).thenAnswer {
flow { emit(PaymentFailed(Generic, paymentData, "dummy msg")) }
}

viewModel.start()

(viewModel.viewStateData.value as ExternalReaderFailedPaymentState).onSecondaryActionClicked!!.invoke()
Expand Down Expand Up @@ -2021,7 +2019,6 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() {
}

initViewModel(BUILT_IN)

viewModel.start()

(viewModel.viewStateData.value as BuiltInReaderPaymentSuccessfulState).onPrimaryActionClicked.invoke()
Expand Down Expand Up @@ -2053,7 +2050,6 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() {
}

initViewModel(BUILT_IN)

viewModel.start()

(viewModel.viewStateData.value as BuiltInReaderPaymentSuccessfulReceiptSentAutomaticallyState)
Expand Down Expand Up @@ -2218,7 +2214,6 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() {
}

initViewModel(BUILT_IN)

viewModel.start()
(viewModel.viewStateData.value as BuiltInReaderPaymentSuccessfulState).onPrimaryActionClicked.invoke()

Expand Down Expand Up @@ -2387,7 +2382,6 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() {
}

initViewModel(BUILT_IN)

viewModel.start()

(viewModel.viewStateData.value as BuiltInReaderPaymentSuccessfulState).onSecondaryActionClicked.invoke()
Expand Down Expand Up @@ -2458,7 +2452,6 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() {
flow { emit(PaymentCompleted("")) }
}
whenever(paymentReceiptHelper.getReceiptUrl(any())).thenReturn(Result.failure(Exception()))

viewModel.start()

(viewModel.viewStateData.value as ExternalReaderPaymentSuccessfulState).onSecondaryActionClicked.invoke()
Expand All @@ -2475,7 +2468,6 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() {
whenever(paymentReceiptHelper.getReceiptUrl(any())).thenReturn(Result.failure(Exception()))

initViewModel(BUILT_IN)

viewModel.start()

(viewModel.viewStateData.value as BuiltInReaderPaymentSuccessfulState).onSecondaryActionClicked.invoke()
Expand Down Expand Up @@ -2533,7 +2525,6 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() {
}

initViewModel(BUILT_IN)

viewModel.start()

(viewModel.viewStateData.value as BuiltInReaderPaymentSuccessfulState).onTertiaryActionClicked.invoke()
Expand Down Expand Up @@ -2565,7 +2556,6 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() {
}

initViewModel(BUILT_IN)

viewModel.start()

(viewModel.viewStateData.value as BuiltInReaderPaymentSuccessfulReceiptSentAutomaticallyState)
Expand Down Expand Up @@ -3340,7 +3330,6 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() {
)
}
}

viewModel.start()

val externalReaderFailedPaymentState = viewModel.viewStateData.value as FailedRefundState
Expand Down Expand Up @@ -3694,7 +3683,6 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() {
CardInteracRefundStatus.RefundStatusErrorType.NonRetryable
)
).thenReturn(InteracRefundFlowError.NonRetryableGeneric)

viewModel.start()
val viewState = viewModel.viewStateData.value!!
(viewState as FailedRefundState).onPrimaryActionClicked.invoke()
Expand Down Expand Up @@ -4514,6 +4502,7 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() {
cardReaderConfigProvider = cardReaderConfigProvider,
paymentReceiptShare = paymentReceiptShare,
)
viewModel.event.observeForever {}
}

private fun initViewModel(
Expand Down Expand Up @@ -4549,5 +4538,6 @@ class CardReaderPaymentViewModelTest : BaseUnitTest() {
cardReaderConfigProvider = cardReaderConfigProvider,
paymentReceiptShare = paymentReceiptShare,
)
viewModel.event.observeForever {}
}
}
Loading