Skip to content

Commit

Permalink
feat(health-sdk): Create the ReviewFragment synchronously
Browse files Browse the repository at this point in the history
Loading the payment information is handled in the ReviewFragment by showing a loading indicator
until the payment information has been loaded.

IPC-271
  • Loading branch information
a-szotyori committed Jun 6, 2024
1 parent 593c074 commit 85822a5
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 98 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,37 +99,6 @@ class InvoicesActivity : AppCompatActivity() {

}
}
launch {
viewModel.paymentReviewFragmentStateFlow.collect { paymentReviewFragmentState ->
when (paymentReviewFragmentState) {
is PaymentReviewFragmentState.Error -> {
AlertDialog.Builder(this@InvoicesActivity)
.setTitle(getString(R.string.could_not_start_payment_review))
.setMessage(paymentReviewFragmentState.throwable.message)
.setPositiveButton(android.R.string.ok, null)
.show()
}

PaymentReviewFragmentState.Idle -> {}
PaymentReviewFragmentState.Loading -> {
showLoadingIndicator(binding)
}

is PaymentReviewFragmentState.Success -> {
hideLoadingIndicator(binding)

val paymentReviewFragment = paymentReviewFragmentState.fragment

paymentReviewFragment.listener = reviewFragmentListener

supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, paymentReviewFragment, REVIEW_FRAGMENT_TAG)
.addToBackStack(null)
.commit()
}
}
}
}
launch {
viewModel.openBankState.collect { paymentState ->
when (paymentState) {
Expand Down Expand Up @@ -172,6 +141,22 @@ class InvoicesActivity : AppCompatActivity() {
LOG.debug("Pay invoice clicked")

viewModel.getPaymentReviewFragment(documentId)
.onSuccess { reviewFragment ->
reviewFragment.listener = reviewFragmentListener

supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, reviewFragment, REVIEW_FRAGMENT_TAG)
.addToBackStack(null)
.commit()
}
.onFailure { throwable ->
LOG.error("Error getting payment review fragment", throwable)
AlertDialog.Builder(this@InvoicesActivity)
.setTitle(getString(R.string.could_not_start_payment_review))
.setMessage(throwable.message)
.setPositiveButton(android.R.string.ok, null)
.show()
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package net.gini.android.health.sdk.exampleapp.invoices.ui
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import net.gini.android.health.sdk.exampleapp.invoices.data.InvoicesRepository
Expand All @@ -28,9 +26,6 @@ class InvoicesViewModel(
}
val paymentProviderAppsFlow = paymentComponent.paymentProviderAppsFlow

val _paymentReviewFragmentFlow = MutableStateFlow<PaymentReviewFragmentState>(PaymentReviewFragmentState.Idle)
val paymentReviewFragmentStateFlow = _paymentReviewFragmentFlow.asStateFlow()

val openBankState = invoicesRepository.giniHealth.openBankState

fun updateDocument() {
Expand Down Expand Up @@ -60,31 +55,24 @@ class InvoicesViewModel(
}
}

fun getPaymentReviewFragment(documentId: String) {
viewModelScope.launch {
LOG.debug("Getting payment review fragment for id: {}", documentId)

_paymentReviewFragmentFlow.value = PaymentReviewFragmentState.Loading

val documentWithExtractions =
invoicesRepository.invoicesFlow.value.find { it.documentId == documentId }

if (documentWithExtractions != null) {
try {
val paymentReviewFragment = paymentComponent.getPaymentReviewFragment(
documentWithExtractions.documentId,
ReviewConfiguration(showCloseButton = true)
)
_paymentReviewFragmentFlow.value = PaymentReviewFragmentState.Success(paymentReviewFragment)
} catch (e: Exception) {
LOG.error("Error getting payment review fragment", e)
_paymentReviewFragmentFlow.value = PaymentReviewFragmentState.Error(e)
}
} else {
LOG.error("Document with id {} not found", documentId)
_paymentReviewFragmentFlow.value = PaymentReviewFragmentState.Error(IllegalStateException("Document with id $documentId not found"))
fun getPaymentReviewFragment(documentId: String): Result<ReviewFragment> {
val documentWithExtractions =
invoicesRepository.invoicesFlow.value.find { it.documentId == documentId }

return if (documentWithExtractions != null) {
return try {
val paymentReviewFragment = paymentComponent.getPaymentReviewFragment(
documentWithExtractions.documentId,
ReviewConfiguration(showCloseButton = true)
)
Result.success(paymentReviewFragment)
} catch (e: Exception) {
LOG.error("Error getting payment review fragment", e)
Result.failure(e)
}
_paymentReviewFragmentFlow.emit(PaymentReviewFragmentState.Idle)
} else {
LOG.error("Document with id {} not found", documentId)
Result.failure(IllegalStateException("Document with id $documentId not found"))
}
}

Expand All @@ -93,10 +81,3 @@ class InvoicesViewModel(

}
}

sealed class PaymentReviewFragmentState {
object Loading : PaymentReviewFragmentState()
data class Success(val fragment: ReviewFragment) : PaymentReviewFragmentState()
data class Error(val throwable: Throwable) : PaymentReviewFragmentState()
object Idle : PaymentReviewFragmentState()
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package net.gini.android.health.sdk.paymentcomponent

import android.content.Context
import android.util.Log
import androidx.annotation.VisibleForTesting
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
Expand Down Expand Up @@ -198,19 +197,18 @@ class PaymentComponent(private val context: Context, private val giniHealth: Gin
* @param configuration The configuration for the [ReviewFragment]
* @throws IllegalStateException If no payment provider app has been selected
*/
suspend fun getPaymentReviewFragment(documentId: String, configuration: ReviewConfiguration): ReviewFragment {
fun getPaymentReviewFragment(documentId: String, configuration: ReviewConfiguration): ReviewFragment {
LOG.debug("Getting payment review fragment for id: {}", documentId)

giniHealth.setDocumentForReview(documentId)

when (val selectedPaymentProviderAppState = _selectedPaymentProviderAppFlow.value) {
is SelectedPaymentProviderAppState.AppSelected -> {
LOG.debug("Creating ReviewFragment for selected payment provider app: {}", selectedPaymentProviderAppState.paymentProviderApp.name)

return ReviewFragment.newInstance(
giniHealth,
configuration = configuration,
paymentComponent = this@PaymentComponent
paymentComponent = this@PaymentComponent,
documentId = documentId
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ class ReviewFragment private constructor(
viewModel.userPreferences = UserPreferences(requireContext())
viewModel.openWithPreferences = OpenWithPreferences(requireContext())
viewModel.startObservingOpenWithCount()
viewModel.loadPaymentDetails()

with(binding) {
setStateListeners()
Expand Down Expand Up @@ -607,7 +608,8 @@ class ReviewFragment private constructor(
configuration: ReviewConfiguration = ReviewConfiguration(),
listener: ReviewFragmentListener? = null,
paymentComponent: PaymentComponent,
viewModelFactory: ViewModelProvider.Factory = ReviewViewModel.Factory(giniHealth, configuration, paymentComponent),
documentId: String,
viewModelFactory: ViewModelProvider.Factory = ReviewViewModel.Factory(giniHealth, configuration, paymentComponent, documentId),
): ReviewFragment = ReviewFragment(listener, paymentComponent, viewModelFactory)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ import org.slf4j.LoggerFactory
import java.io.File


internal class ReviewViewModel(val giniHealth: GiniHealth, val configuration: ReviewConfiguration, val paymentComponent: PaymentComponent) : ViewModel() {
internal class ReviewViewModel(
val giniHealth: GiniHealth,
val configuration: ReviewConfiguration,
val paymentComponent: PaymentComponent,
val documentId: String
) : ViewModel() {

internal var userPreferences: UserPreferences? = null
internal var openWithPreferences: OpenWithPreferences? = null
Expand Down Expand Up @@ -325,6 +330,12 @@ internal class ReviewViewModel(val giniHealth: GiniHealth, val configuration: Re
sendFeedback()
}

fun loadPaymentDetails() {
viewModelScope.launch {
giniHealth.setDocumentForReview(documentId)
}
}

sealed class PaymentNextStep {
object RedirectToBank: PaymentNextStep()
object ShowOpenWithSheet: PaymentNextStep()
Expand All @@ -333,11 +344,16 @@ internal class ReviewViewModel(val giniHealth: GiniHealth, val configuration: Re
data class SetLoadingVisibility(val isVisible: Boolean): PaymentNextStep()
}

class Factory(private val giniHealth: GiniHealth, private val configuration: ReviewConfiguration, private val paymentComponent: PaymentComponent) :
class Factory(
private val giniHealth: GiniHealth,
private val configuration: ReviewConfiguration,
private val paymentComponent: PaymentComponent,
private val documentId: String
) :
ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return ReviewViewModel(giniHealth, configuration, paymentComponent) as T
return ReviewViewModel(giniHealth, configuration, paymentComponent, documentId) as T
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ class PaymentComponentTest {
}

@Test(expected = IllegalStateException::class)
fun `throws exception when trying to create ReviewFragment if no payment provider app is set`() = runTest {
fun `throws exception when trying to create ReviewFragment if no payment provider app is set`() {
// Given
val reviewConfiguration: ReviewConfiguration = mockk(relaxed = true)
val paymentComponent = PaymentComponent(context!!, giniHealth!!)
Expand All @@ -440,7 +440,7 @@ class PaymentComponentTest {
}

@Test
fun `instantiates review fragment if payment provider app is set`() = runTest {
fun `instantiates review fragment if payment provider app is set`() {
// Given
val reviewConfiguration: ReviewConfiguration = mockk(relaxed = true)
val paymentComponent: PaymentComponent = mockk(relaxed = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import androidx.test.espresso.intent.matcher.IntentMatchers.hasType
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
Expand Down Expand Up @@ -92,6 +93,28 @@ class ReviewFragmentTest {
}
}

@Test
fun `loads payment details for documentId`() {
// Given
val documentId = "1234"

// When
launchFragmentInContainer(themeResId = R.style.GiniHealthTheme) {
ReviewFragment.newInstance(
giniHealth = mockk(relaxed = true),
listener = mockk(relaxed = true),
viewModelFactory = viewModelFactory,
paymentComponent = mockk(relaxed = true),
documentId = documentId
)
}

// Then
verify {
viewModel.loadPaymentDetails()
}
}

@Test
fun `calls onNextClicked() listener when 'Next' ('Pay') button is clicked`() {
// Given
Expand All @@ -113,7 +136,8 @@ class ReviewFragmentTest {
giniHealth = mockk(relaxed = true),
listener = listener,
viewModelFactory = viewModelFactory,
paymentComponent = paymentComponent
paymentComponent = paymentComponent,
documentId = ""
)
}

Expand Down Expand Up @@ -153,7 +177,8 @@ class ReviewFragmentTest {
giniHealth = mockk(relaxed = true),
listener = listener,
viewModelFactory = viewModelFactory,
paymentComponent = paymentComponent
paymentComponent = paymentComponent,
documentId = ""
)
}

Expand Down Expand Up @@ -196,7 +221,8 @@ class ReviewFragmentTest {
giniHealth = mockk(relaxed = true),
listener = listener,
viewModelFactory = viewModelFactory,
paymentComponent = paymentComponent
paymentComponent = paymentComponent,
documentId = ""
)
}

Expand Down Expand Up @@ -237,7 +263,8 @@ class ReviewFragmentTest {
giniHealth = mockk(relaxed = true),
listener = mockk(relaxed = true),
viewModelFactory = viewModelFactory,
paymentComponent = paymentComponent
paymentComponent = paymentComponent,
documentId = ""
)
}

Expand Down Expand Up @@ -277,7 +304,8 @@ class ReviewFragmentTest {
giniHealth = mockk(relaxed = true),
listener = listener,
viewModelFactory = viewModelFactory,
paymentComponent = paymentComponent
paymentComponent = paymentComponent,
documentId = ""
)
launchFragmentInContainer(themeResId = R.style.GiniHealthTheme) {
fragment
Expand Down
Loading

0 comments on commit 85822a5

Please sign in to comment.