Add currency selection logic update.
This commit is contained in:
parent
055b4691d7
commit
1e2f7f0775
8 changed files with 96 additions and 30 deletions
|
@ -95,11 +95,15 @@ fun DonationsConfiguration.getMinimumDonationAmounts(paymentMethodAvailability:
|
||||||
.mapValues { FiatMoney(it.value.minimum, it.key) }
|
.mapValues { FiatMoney(it.value.minimum, it.key) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun DonationsConfiguration.getAvailablePaymentMethods(currencyCode: String): Set<String> {
|
||||||
|
return currencies[currencyCode.lowercase()]?.supportedPaymentMethods ?: emptySet()
|
||||||
|
}
|
||||||
|
|
||||||
private fun DonationsConfiguration.getFilteredCurrencies(paymentMethodAvailability: PaymentMethodAvailability): Map<String, DonationsConfiguration.CurrencyConfiguration> {
|
private fun DonationsConfiguration.getFilteredCurrencies(paymentMethodAvailability: PaymentMethodAvailability): Map<String, DonationsConfiguration.CurrencyConfiguration> {
|
||||||
val userPaymentMethods = paymentMethodAvailability.toSet()
|
val userPaymentMethods = paymentMethodAvailability.toSet()
|
||||||
val availableCurrencyCodes = PlatformCurrencyUtil.getAvailableCurrencyCodes()
|
val availableCurrencyCodes = PlatformCurrencyUtil.getAvailableCurrencyCodes()
|
||||||
return currencies.filter { (code, config) ->
|
return currencies.filter { (code, config) ->
|
||||||
val areAllMethodsAvailable = config.supportedPaymentMethods.containsAll(userPaymentMethods)
|
val areAllMethodsAvailable = config.supportedPaymentMethods.any { it in userPaymentMethods }
|
||||||
availableCurrencyCodes.contains(code.uppercase()) && areAllMethodsAvailable
|
availableCurrencyCodes.contains(code.uppercase()) && areAllMethodsAvailable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -241,6 +241,7 @@ class DonateToSignalViewModel(
|
||||||
state.copy(
|
state.copy(
|
||||||
oneTimeDonationState = state.oneTimeDonationState.copy(
|
oneTimeDonationState = state.oneTimeDonationState.copy(
|
||||||
boosts = boostList,
|
boosts = boostList,
|
||||||
|
selectedBoost = null,
|
||||||
selectedCurrency = currency,
|
selectedCurrency = currency,
|
||||||
donationStage = DonateToSignalState.DonationStage.READY,
|
donationStage = DonateToSignalState.DonationStage.READY,
|
||||||
selectableCurrencyCodes = availableCurrencies.map(Currency::getCurrencyCode),
|
selectableCurrencyCodes = availableCurrencies.map(Currency::getCurrencyCode),
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.thoughtcrime.securesms.components.settings.app.subscription.donate.Do
|
||||||
import org.thoughtcrime.securesms.components.settings.app.subscription.models.GooglePayButton
|
import org.thoughtcrime.securesms.components.settings.app.subscription.models.GooglePayButton
|
||||||
import org.thoughtcrime.securesms.components.settings.app.subscription.models.PayPalButton
|
import org.thoughtcrime.securesms.components.settings.app.subscription.models.PayPalButton
|
||||||
import org.thoughtcrime.securesms.components.settings.configure
|
import org.thoughtcrime.securesms.components.settings.configure
|
||||||
|
import org.thoughtcrime.securesms.components.settings.models.IndeterminateLoadingCircle
|
||||||
import org.thoughtcrime.securesms.payments.FiatMoneyUtil
|
import org.thoughtcrime.securesms.payments.FiatMoneyUtil
|
||||||
import org.thoughtcrime.securesms.util.LifecycleDisposable
|
import org.thoughtcrime.securesms.util.LifecycleDisposable
|
||||||
import org.thoughtcrime.securesms.util.fragments.requireListener
|
import org.thoughtcrime.securesms.util.fragments.requireListener
|
||||||
|
@ -42,6 +43,7 @@ class GatewaySelectorBottomSheet : DSLSettingsBottomSheetFragment() {
|
||||||
BadgeDisplay112.register(adapter)
|
BadgeDisplay112.register(adapter)
|
||||||
GooglePayButton.register(adapter)
|
GooglePayButton.register(adapter)
|
||||||
PayPalButton.register(adapter)
|
PayPalButton.register(adapter)
|
||||||
|
IndeterminateLoadingCircle.register(adapter)
|
||||||
|
|
||||||
lifecycleDisposable.bindTo(viewLifecycleOwner)
|
lifecycleDisposable.bindTo(viewLifecycleOwner)
|
||||||
|
|
||||||
|
@ -65,6 +67,12 @@ class GatewaySelectorBottomSheet : DSLSettingsBottomSheetFragment() {
|
||||||
|
|
||||||
space(66.dp)
|
space(66.dp)
|
||||||
|
|
||||||
|
if (state.loading) {
|
||||||
|
customPref(IndeterminateLoadingCircle)
|
||||||
|
space(16.dp)
|
||||||
|
return@configure
|
||||||
|
}
|
||||||
|
|
||||||
if (state.isGooglePayAvailable) {
|
if (state.isGooglePayAvailable) {
|
||||||
customPref(
|
customPref(
|
||||||
GooglePayButton.Model(
|
GooglePayButton.Model(
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
package org.thoughtcrime.securesms.components.settings.app.subscription.donate.gateway
|
||||||
|
|
||||||
|
import io.reactivex.rxjava3.core.Single
|
||||||
|
import org.thoughtcrime.securesms.components.settings.app.subscription.getAvailablePaymentMethods
|
||||||
|
import org.whispersystems.signalservice.api.services.DonationsService
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
class GatewaySelectorRepository(
|
||||||
|
private val donationsService: DonationsService
|
||||||
|
) {
|
||||||
|
fun getAvailableGateways(currencyCode: String): Single<Set<GatewayResponse.Gateway>> {
|
||||||
|
return Single.fromCallable {
|
||||||
|
donationsService.getDonationsConfiguration(Locale.getDefault())
|
||||||
|
}.flatMap { it.flattenResult() }
|
||||||
|
.map { configuration ->
|
||||||
|
configuration.getAvailablePaymentMethods(currencyCode).map {
|
||||||
|
when (it) {
|
||||||
|
"PAYPAL" -> listOf(GatewayResponse.Gateway.PAYPAL)
|
||||||
|
"CARD" -> listOf(GatewayResponse.Gateway.CREDIT_CARD, GatewayResponse.Gateway.GOOGLE_PAY)
|
||||||
|
else -> listOf()
|
||||||
|
}
|
||||||
|
}.flatten().toSet()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.components.settings.app.subscription.donate.g
|
||||||
import org.thoughtcrime.securesms.badges.models.Badge
|
import org.thoughtcrime.securesms.badges.models.Badge
|
||||||
|
|
||||||
data class GatewaySelectorState(
|
data class GatewaySelectorState(
|
||||||
|
val loading: Boolean = true,
|
||||||
val badge: Badge,
|
val badge: Badge,
|
||||||
val isGooglePayAvailable: Boolean = false,
|
val isGooglePayAvailable: Boolean = false,
|
||||||
val isPayPalAvailable: Boolean = false,
|
val isPayPalAvailable: Boolean = false,
|
||||||
|
|
|
@ -2,18 +2,21 @@ package org.thoughtcrime.securesms.components.settings.app.subscription.donate.g
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import io.reactivex.rxjava3.core.Single
|
||||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||||
import io.reactivex.rxjava3.kotlin.plusAssign
|
import io.reactivex.rxjava3.kotlin.plusAssign
|
||||||
import io.reactivex.rxjava3.kotlin.subscribeBy
|
import io.reactivex.rxjava3.kotlin.subscribeBy
|
||||||
import org.signal.donations.PaymentSourceType
|
import org.signal.donations.PaymentSourceType
|
||||||
import org.thoughtcrime.securesms.components.settings.app.subscription.InAppDonations
|
import org.thoughtcrime.securesms.components.settings.app.subscription.InAppDonations
|
||||||
import org.thoughtcrime.securesms.components.settings.app.subscription.StripeRepository
|
import org.thoughtcrime.securesms.components.settings.app.subscription.StripeRepository
|
||||||
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
import org.thoughtcrime.securesms.util.rx.RxStore
|
import org.thoughtcrime.securesms.util.rx.RxStore
|
||||||
|
|
||||||
class GatewaySelectorViewModel(
|
class GatewaySelectorViewModel(
|
||||||
args: GatewaySelectorBottomSheetArgs,
|
args: GatewaySelectorBottomSheetArgs,
|
||||||
private val repository: StripeRepository
|
repository: StripeRepository,
|
||||||
|
gatewaySelectorRepository: GatewaySelectorRepository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val store = RxStore(
|
private val store = RxStore(
|
||||||
|
@ -29,7 +32,19 @@ class GatewaySelectorViewModel(
|
||||||
val state = store.stateFlowable
|
val state = store.stateFlowable
|
||||||
|
|
||||||
init {
|
init {
|
||||||
checkIfGooglePayIsAvailable()
|
val isGooglePayAvailable = repository.isGooglePayAvailable().toSingleDefault(true).onErrorReturnItem(false)
|
||||||
|
val availabilitySet = gatewaySelectorRepository.getAvailableGateways(currencyCode = args.request.currencyCode)
|
||||||
|
disposables += Single.zip(isGooglePayAvailable, availabilitySet, ::Pair).subscribeBy { (googlePayAvailable, gatewaysAvailable) ->
|
||||||
|
SignalStore.donationsValues().isGooglePayReady = googlePayAvailable
|
||||||
|
store.update {
|
||||||
|
it.copy(
|
||||||
|
loading = false,
|
||||||
|
isCreditCardAvailable = it.isCreditCardAvailable && gatewaysAvailable.contains(GatewayResponse.Gateway.CREDIT_CARD),
|
||||||
|
isGooglePayAvailable = it.isGooglePayAvailable && googlePayAvailable && gatewaysAvailable.contains(GatewayResponse.Gateway.GOOGLE_PAY),
|
||||||
|
isPayPalAvailable = it.isPayPalAvailable && gatewaysAvailable.contains(GatewayResponse.Gateway.PAYPAL)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
|
@ -37,25 +52,13 @@ class GatewaySelectorViewModel(
|
||||||
disposables.clear()
|
disposables.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkIfGooglePayIsAvailable() {
|
|
||||||
disposables += repository.isGooglePayAvailable().subscribeBy(
|
|
||||||
onComplete = {
|
|
||||||
SignalStore.donationsValues().isGooglePayReady = true
|
|
||||||
store.update { it.copy(isGooglePayAvailable = true) }
|
|
||||||
},
|
|
||||||
onError = {
|
|
||||||
SignalStore.donationsValues().isGooglePayReady = false
|
|
||||||
store.update { it.copy(isGooglePayAvailable = false) }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
class Factory(
|
class Factory(
|
||||||
private val args: GatewaySelectorBottomSheetArgs,
|
private val args: GatewaySelectorBottomSheetArgs,
|
||||||
private val repository: StripeRepository
|
private val repository: StripeRepository,
|
||||||
|
private val gatewaySelectorRepository: GatewaySelectorRepository = GatewaySelectorRepository(ApplicationDependencies.getDonationsService())
|
||||||
) : ViewModelProvider.Factory {
|
) : ViewModelProvider.Factory {
|
||||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||||
return modelClass.cast(GatewaySelectorViewModel(args, repository)) as T
|
return modelClass.cast(GatewaySelectorViewModel(args, repository, gatewaySelectorRepository)) as T
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,11 +24,11 @@ class DonationsConfigurationExtensionsKtTest {
|
||||||
private val testSubject = JsonUtil.fromJson(testData, DonationsConfiguration::class.java)
|
private val testSubject = JsonUtil.fromJson(testData, DonationsConfiguration::class.java)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Given all methods are available, when I getSubscriptionAmounts, then I expect BIF`() {
|
fun `Given all methods are available, when I getSubscriptionAmounts, then I expect all currencies`() {
|
||||||
val subscriptionPrices = testSubject.getSubscriptionAmounts(DonationsConfiguration.SUBSCRIPTION_LEVELS.first(), AllPaymentMethodsAvailability)
|
val subscriptionPrices = testSubject.getSubscriptionAmounts(DonationsConfiguration.SUBSCRIPTION_LEVELS.first(), AllPaymentMethodsAvailability)
|
||||||
|
|
||||||
assertEquals(1, subscriptionPrices.size)
|
assertEquals(3, subscriptionPrices.size)
|
||||||
assertEquals("BIF", subscriptionPrices.first().currency.currencyCode)
|
assertTrue(subscriptionPrices.map { it.currency.currencyCode }.containsAll(setOf("JPY", "BIF", "USD")))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -84,11 +84,13 @@ class DonationsConfigurationExtensionsKtTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Given all methods are available, when I getGiftAmounts, then I expect BIF`() {
|
fun `Given all methods are available, when I getGiftAmounts, then I expect BIF and JPY and USD`() {
|
||||||
val giftAmounts = testSubject.getGiftBadgeAmounts(AllPaymentMethodsAvailability)
|
val giftAmounts = testSubject.getGiftBadgeAmounts(AllPaymentMethodsAvailability)
|
||||||
|
|
||||||
assertEquals(1, giftAmounts.size)
|
assertEquals(3, giftAmounts.size)
|
||||||
assertNotNull(giftAmounts[Currency.getInstance("BIF")])
|
assertNotNull(giftAmounts[Currency.getInstance("BIF")])
|
||||||
|
assertNotNull(giftAmounts[Currency.getInstance("JPY")])
|
||||||
|
assertNotNull(giftAmounts[Currency.getInstance("USD")])
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -108,11 +110,13 @@ class DonationsConfigurationExtensionsKtTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Given all methods are available, when I getBoostAmounts, then I expect BIF`() {
|
fun `Given all methods are available, when I getBoostAmounts, then I expect BIF and JPY and USD`() {
|
||||||
val boostAmounts = testSubject.getBoostAmounts(AllPaymentMethodsAvailability)
|
val boostAmounts = testSubject.getBoostAmounts(AllPaymentMethodsAvailability)
|
||||||
|
|
||||||
assertEquals(1, boostAmounts.size)
|
assertEquals(3, boostAmounts.size)
|
||||||
assertNotNull(boostAmounts[Currency.getInstance("BIF")])
|
assertNotNull(boostAmounts[Currency.getInstance("BIF")])
|
||||||
|
assertNotNull(boostAmounts[Currency.getInstance("JPY")])
|
||||||
|
assertNotNull(boostAmounts[Currency.getInstance("USD")])
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -132,11 +136,13 @@ class DonationsConfigurationExtensionsKtTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Given all methods are available, when I getMinimumDonationAmounts, then I expect BIF`() {
|
fun `Given all methods are available, when I getMinimumDonationAmounts, then I expect BIF and JPY and USD`() {
|
||||||
val minimumDonationAmounts = testSubject.getMinimumDonationAmounts(AllPaymentMethodsAvailability)
|
val minimumDonationAmounts = testSubject.getMinimumDonationAmounts(AllPaymentMethodsAvailability)
|
||||||
|
|
||||||
assertEquals(1, minimumDonationAmounts.size)
|
assertEquals(3, minimumDonationAmounts.size)
|
||||||
assertNotNull(minimumDonationAmounts[Currency.getInstance("BIF")])
|
assertNotNull(minimumDonationAmounts[Currency.getInstance("BIF")])
|
||||||
|
assertNotNull(minimumDonationAmounts[Currency.getInstance("JPY")])
|
||||||
|
assertNotNull(minimumDonationAmounts[Currency.getInstance("USD")])
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -185,6 +191,24 @@ class DonationsConfigurationExtensionsKtTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Given I want to pay in USD, when I getAvailablePaymentMethods, then I expect CARD`() {
|
||||||
|
val availablePaymentMethods = testSubject.getAvailablePaymentMethods("UsD")
|
||||||
|
|
||||||
|
assertEquals(1, availablePaymentMethods.size)
|
||||||
|
assertTrue("CARD" in availablePaymentMethods)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Given I want to pay in BIF, when I getAvailablePaymentMethods, then I expect CARD and PAYPAL`() {
|
||||||
|
val availablePaymentMethods = testSubject.getAvailablePaymentMethods("bIF")
|
||||||
|
|
||||||
|
println(testSubject.currencies)
|
||||||
|
assertEquals(2, availablePaymentMethods.size)
|
||||||
|
assertTrue("CARD" in availablePaymentMethods)
|
||||||
|
assertTrue("PAYPAL" in availablePaymentMethods)
|
||||||
|
}
|
||||||
|
|
||||||
private object AllPaymentMethodsAvailability : PaymentMethodAvailability {
|
private object AllPaymentMethodsAvailability : PaymentMethodAvailability {
|
||||||
override fun isPayPalAvailable(): Boolean = true
|
override fun isPayPalAvailable(): Boolean = true
|
||||||
override fun isGooglePayOrCreditCardAvailable(): Boolean = true
|
override fun isGooglePayOrCreditCardAvailable(): Boolean = true
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"currencies": {
|
"currencies": {
|
||||||
"JPY": {
|
"jpy": {
|
||||||
"minimum": 300,
|
"minimum": 300,
|
||||||
"oneTime": {
|
"oneTime": {
|
||||||
"1": [
|
"1": [
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
"PAYPAL"
|
"PAYPAL"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"USD": {
|
"usd": {
|
||||||
"minimum": 2.5,
|
"minimum": 2.5,
|
||||||
"oneTime": {
|
"oneTime": {
|
||||||
"1": [
|
"1": [
|
||||||
|
@ -48,7 +48,7 @@
|
||||||
"CARD"
|
"CARD"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"BIF": {
|
"bif": {
|
||||||
"minimum": 3000,
|
"minimum": 3000,
|
||||||
"oneTime": {
|
"oneTime": {
|
||||||
"1": [
|
"1": [
|
||||||
|
|
Loading…
Add table
Reference in a new issue