Skip to content

Commit 4ac19d8

Browse files
committed
Show loading states in purchasing flow a minimum amount of time
1 parent e859f00 commit 4ac19d8

File tree

2 files changed

+34
-1
lines changed

2 files changed

+34
-1
lines changed

android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/PaymentUseCase.kt

+20-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package net.mullvad.mullvadvpn.usecase
22

33
import android.app.Activity
4+
import kotlinx.coroutines.FlowPreview
45
import kotlinx.coroutines.flow.Flow
56
import kotlinx.coroutines.flow.MutableStateFlow
67
import kotlinx.coroutines.flow.asStateFlow
@@ -12,6 +13,7 @@ import net.mullvad.mullvadvpn.lib.payment.model.PaymentAvailability
1213
import net.mullvad.mullvadvpn.lib.payment.model.ProductId
1314
import net.mullvad.mullvadvpn.lib.payment.model.PurchaseResult
1415
import net.mullvad.mullvadvpn.lib.payment.model.VerificationResult
16+
import net.mullvad.mullvadvpn.util.delayAtLeast
1517
import net.mullvad.mullvadvpn.util.retryWithExponentialBackOff
1618

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

39+
@OptIn(FlowPreview::class)
3740
override suspend fun purchaseProduct(productId: ProductId, activityProvider: () -> Activity) {
38-
paymentRepository.purchaseProduct(productId, activityProvider).collect(_purchaseResult)
41+
paymentRepository
42+
.purchaseProduct(productId, activityProvider)
43+
.delayAtLeast {
44+
if (purchaseResult.value.shouldDelayLoading()) {
45+
MINIMUM_LOADING_DELAY_MS
46+
} else {
47+
0
48+
}
49+
}
50+
.collect(_purchaseResult)
3951
}
4052

4153
override suspend fun queryPaymentAvailability() {
@@ -64,6 +76,13 @@ class PlayPaymentUseCase(private val paymentRepository: PaymentRepository) : Pay
6476
}
6577
}
6678
}
79+
80+
private fun PurchaseResult?.shouldDelayLoading() =
81+
this is PurchaseResult.FetchingProducts || this is PurchaseResult.VerificationStarted
82+
83+
companion object {
84+
const val MINIMUM_LOADING_DELAY_MS = 500L
85+
}
6786
}
6887

6988
class EmptyPaymentUseCase : PaymentUseCase {

android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/FlowUtils.kt

+14
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import kotlinx.coroutines.flow.callbackFlow
1010
import kotlinx.coroutines.flow.catch
1111
import kotlinx.coroutines.flow.firstOrNull
1212
import kotlinx.coroutines.flow.flatMapLatest
13+
import kotlinx.coroutines.flow.flow
1314
import kotlinx.coroutines.flow.map
1415
import kotlinx.coroutines.flow.retryWhen
1516
import kotlinx.coroutines.withTimeoutOrNull
@@ -154,3 +155,16 @@ class ExceptionWrapper(val item: Any) : Throwable()
154155
suspend fun <T> Flow<T>.firstOrNullWithTimeout(timeMillis: Long): T? {
155156
return withTimeoutOrNull(timeMillis) { firstOrNull() }
156157
}
158+
159+
suspend fun <T> Flow<T>.delayAtLeast(timeMillis: (T) -> Long) = flow {
160+
var lastEmittedTime = System.currentTimeMillis()
161+
collect {
162+
val timeSinceLastEmit = System.currentTimeMillis() - lastEmittedTime
163+
val minimumTime = timeMillis(it)
164+
if (timeSinceLastEmit < minimumTime) {
165+
delay(minimumTime - timeSinceLastEmit)
166+
}
167+
lastEmittedTime = System.currentTimeMillis()
168+
emit(it)
169+
}
170+
}

0 commit comments

Comments
 (0)