Skip to content

Commit

Permalink
Show loading states in purchasing flow a minimum amount of time
Browse files Browse the repository at this point in the history
  • Loading branch information
Pururun committed Apr 8, 2024
1 parent e859f00 commit 4ac19d8
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package net.mullvad.mullvadvpn.usecase

import android.app.Activity
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
Expand All @@ -12,6 +13,7 @@ import net.mullvad.mullvadvpn.lib.payment.model.PaymentAvailability
import net.mullvad.mullvadvpn.lib.payment.model.ProductId
import net.mullvad.mullvadvpn.lib.payment.model.PurchaseResult
import net.mullvad.mullvadvpn.lib.payment.model.VerificationResult
import net.mullvad.mullvadvpn.util.delayAtLeast
import net.mullvad.mullvadvpn.util.retryWithExponentialBackOff

interface PaymentUseCase {
Expand All @@ -34,8 +36,18 @@ class PlayPaymentUseCase(private val paymentRepository: PaymentRepository) : Pay
override val paymentAvailability = _paymentAvailability.asStateFlow()
override val purchaseResult = _purchaseResult.asStateFlow()

@OptIn(FlowPreview::class)
override suspend fun purchaseProduct(productId: ProductId, activityProvider: () -> Activity) {
paymentRepository.purchaseProduct(productId, activityProvider).collect(_purchaseResult)
paymentRepository
.purchaseProduct(productId, activityProvider)
.delayAtLeast {
if (purchaseResult.value.shouldDelayLoading()) {
MINIMUM_LOADING_DELAY_MS
} else {
0
}
}
.collect(_purchaseResult)
}

override suspend fun queryPaymentAvailability() {
Expand Down Expand Up @@ -64,6 +76,13 @@ class PlayPaymentUseCase(private val paymentRepository: PaymentRepository) : Pay
}
}
}

private fun PurchaseResult?.shouldDelayLoading() =
this is PurchaseResult.FetchingProducts || this is PurchaseResult.VerificationStarted

companion object {
const val MINIMUM_LOADING_DELAY_MS = 500L
}
}

class EmptyPaymentUseCase : PaymentUseCase {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.retryWhen
import kotlinx.coroutines.withTimeoutOrNull
Expand Down Expand Up @@ -154,3 +155,16 @@ class ExceptionWrapper(val item: Any) : Throwable()
suspend fun <T> Flow<T>.firstOrNullWithTimeout(timeMillis: Long): T? {
return withTimeoutOrNull(timeMillis) { firstOrNull() }
}

suspend fun <T> Flow<T>.delayAtLeast(timeMillis: (T) -> Long) = flow {
var lastEmittedTime = System.currentTimeMillis()
collect {
val timeSinceLastEmit = System.currentTimeMillis() - lastEmittedTime
val minimumTime = timeMillis(it)
if (timeSinceLastEmit < minimumTime) {
delay(minimumTime - timeSinceLastEmit)
}
lastEmittedTime = System.currentTimeMillis()
emit(it)
}
}

0 comments on commit 4ac19d8

Please sign in to comment.