Respect server currency lists for subscriptions and badges.
This commit is contained in:
parent
c06fb81490
commit
2a9eb1bae0
18 changed files with 162 additions and 78 deletions
|
@ -13,7 +13,6 @@ import io.reactivex.rxjava3.subjects.PublishSubject
|
|||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.badges.BadgeRepository
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.SubscriptionsRepository
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.util.livedata.Store
|
||||
import org.whispersystems.libsignal.util.guava.Optional
|
||||
|
@ -44,7 +43,7 @@ class BadgesOverviewViewModel(
|
|||
|
||||
disposables += Single.zip(
|
||||
subscriptionsRepository.getActiveSubscription(),
|
||||
subscriptionsRepository.getSubscriptions(SignalStore.donationsValues().getSubscriptionCurrency())
|
||||
subscriptionsRepository.getSubscriptions()
|
||||
) { active, all ->
|
||||
if (!active.isActive && active.activeSubscription?.willCancelAtPeriodEnd() == true) {
|
||||
Optional.fromNullable<String>(all.firstOrNull { it.level == active.activeSubscription?.level }?.badge?.id)
|
||||
|
|
|
@ -5,6 +5,7 @@ import org.signal.core.util.money.FiatMoney
|
|||
import org.thoughtcrime.securesms.badges.Badges
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.subscription.Subscription
|
||||
import org.thoughtcrime.securesms.util.PlatformCurrencyUtil
|
||||
import org.whispersystems.signalservice.api.services.DonationsService
|
||||
import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription
|
||||
import org.whispersystems.signalservice.api.subscriptions.SubscriptionLevels
|
||||
|
@ -28,7 +29,7 @@ class SubscriptionsRepository(private val donationsService: DonationsService) {
|
|||
}
|
||||
}
|
||||
|
||||
fun getSubscriptions(currency: Currency): Single<List<Subscription>> = donationsService.getSubscriptionLevels(Locale.getDefault())
|
||||
fun getSubscriptions(): Single<List<Subscription>> = donationsService.getSubscriptionLevels(Locale.getDefault())
|
||||
.flatMap(ServiceResponse<SubscriptionLevels>::flattenResult)
|
||||
.map { subscriptionLevels ->
|
||||
subscriptionLevels.levels.map { (code, level) ->
|
||||
|
@ -36,7 +37,13 @@ class SubscriptionsRepository(private val donationsService: DonationsService) {
|
|||
id = code,
|
||||
name = level.name,
|
||||
badge = Badges.fromServiceBadge(level.badge),
|
||||
price = FiatMoney(level.currencies[currency.currencyCode]!!, currency),
|
||||
prices = level.currencies.filter {
|
||||
PlatformCurrencyUtil
|
||||
.getAvailableCurrencyCodes()
|
||||
.contains(it.key)
|
||||
}.map { (currencyCode, price) ->
|
||||
FiatMoney(price, Currency.getInstance(currencyCode))
|
||||
}.toSet(),
|
||||
level = code.toInt()
|
||||
)
|
||||
}.sortedBy {
|
||||
|
|
|
@ -141,10 +141,10 @@ class BoostFragment : DSLSettingsBottomSheetFragment(
|
|||
|
||||
customPref(
|
||||
CurrencySelection.Model(
|
||||
currencySelection = state.currencySelection,
|
||||
selectedCurrency = state.currencySelection,
|
||||
isEnabled = state.stage == BoostState.Stage.READY,
|
||||
onClick = {
|
||||
findNavController().navigate(BoostFragmentDirections.actionBoostFragmentToSetDonationCurrencyFragment(true))
|
||||
findNavController().navigate(BoostFragmentDirections.actionBoostFragmentToSetDonationCurrencyFragment(true, viewModel.getSupportedCurrencyCodes().toTypedArray()))
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
|
@ -4,6 +4,7 @@ import io.reactivex.rxjava3.core.Single
|
|||
import org.signal.core.util.money.FiatMoney
|
||||
import org.thoughtcrime.securesms.badges.Badges
|
||||
import org.thoughtcrime.securesms.badges.models.Badge
|
||||
import org.thoughtcrime.securesms.util.PlatformCurrencyUtil
|
||||
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile
|
||||
import org.whispersystems.signalservice.api.services.DonationsService
|
||||
import org.whispersystems.signalservice.internal.ServiceResponse
|
||||
|
@ -13,12 +14,14 @@ import java.util.Locale
|
|||
|
||||
class BoostRepository(private val donationsService: DonationsService) {
|
||||
|
||||
fun getBoosts(currency: Currency): Single<List<Boost>> {
|
||||
fun getBoosts(): Single<Map<Currency, List<Boost>>> {
|
||||
return donationsService.boostAmounts
|
||||
.flatMap(ServiceResponse<Map<String, List<BigDecimal>>>::flattenResult)
|
||||
.map { result ->
|
||||
val boosts = result[currency.currencyCode] ?: throw Exception("Unsupported currency! ${currency.currencyCode}")
|
||||
boosts.map { Boost(FiatMoney(it, currency)) }
|
||||
result
|
||||
.filter { PlatformCurrencyUtil.getAvailableCurrencyCodes().contains(it.key) }
|
||||
.mapKeys { (code, _) -> Currency.getInstance(code) }
|
||||
.mapValues { (currency, prices) -> prices.map { Boost(FiatMoney(it, currency)) } }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,19 +2,19 @@ package org.thoughtcrime.securesms.components.settings.app.subscription.boost
|
|||
|
||||
import org.signal.core.util.money.FiatMoney
|
||||
import org.thoughtcrime.securesms.badges.models.Badge
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.models.CurrencySelection
|
||||
import java.math.BigDecimal
|
||||
import java.util.Currency
|
||||
|
||||
data class BoostState(
|
||||
val boostBadge: Badge? = null,
|
||||
val currencySelection: CurrencySelection = CurrencySelection("USD"),
|
||||
val currencySelection: Currency,
|
||||
val isGooglePayAvailable: Boolean = false,
|
||||
val boosts: List<Boost> = listOf(),
|
||||
val selectedBoost: Boost? = null,
|
||||
val customAmount: FiatMoney = FiatMoney(BigDecimal.ZERO, Currency.getInstance(currencySelection.selectedCurrencyCode)),
|
||||
val customAmount: FiatMoney = FiatMoney(BigDecimal.ZERO, currencySelection),
|
||||
val isCustomAmountFocused: Boolean = false,
|
||||
val stage: Stage = Stage.INIT,
|
||||
val supportedCurrencyCodes: List<String> = emptyList()
|
||||
) {
|
||||
enum class Stage {
|
||||
INIT,
|
||||
|
|
|
@ -17,10 +17,11 @@ import org.signal.donations.GooglePayApi
|
|||
import org.thoughtcrime.securesms.badges.models.Badge
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationEvent
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationPaymentRepository
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.models.CurrencySelection
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.util.PlatformCurrencyUtil
|
||||
import org.thoughtcrime.securesms.util.livedata.Store
|
||||
import java.math.BigDecimal
|
||||
import java.util.Currency
|
||||
|
||||
class BoostViewModel(
|
||||
private val boostRepository: BoostRepository,
|
||||
|
@ -28,7 +29,7 @@ class BoostViewModel(
|
|||
private val fetchTokenRequestCode: Int
|
||||
) : ViewModel() {
|
||||
|
||||
private val store = Store(BoostState())
|
||||
private val store = Store(BoostState(currencySelection = SignalStore.donationsValues().getBoostCurrency()))
|
||||
private val eventPublisher: PublishSubject<DonationEvent> = PublishSubject.create()
|
||||
private val disposables = CompositeDisposable()
|
||||
|
||||
|
@ -41,15 +42,26 @@ class BoostViewModel(
|
|||
disposables.clear()
|
||||
}
|
||||
|
||||
fun getSupportedCurrencyCodes(): List<String> {
|
||||
return store.state.supportedCurrencyCodes
|
||||
}
|
||||
|
||||
fun refresh() {
|
||||
disposables.clear()
|
||||
|
||||
val currencyObservable = SignalStore.donationsValues().observableBoostCurrency
|
||||
val boosts = currencyObservable.flatMapSingle { boostRepository.getBoosts(it) }
|
||||
val allBoosts = boostRepository.getBoosts()
|
||||
val boostBadge = boostRepository.getBoostBadge()
|
||||
|
||||
disposables += Observable.combineLatest(boosts, boostBadge.toObservable()) { boostList, badge ->
|
||||
BoostInfo(boostList, boostList[2], badge)
|
||||
disposables += Observable.combineLatest(currencyObservable, allBoosts.toObservable(), boostBadge.toObservable()) { currency, boostMap, badge ->
|
||||
val boostList = if (currency in boostMap) {
|
||||
boostMap[currency]!!
|
||||
} else {
|
||||
SignalStore.donationsValues().setBoostCurrency(PlatformCurrencyUtil.USD)
|
||||
listOf()
|
||||
}
|
||||
|
||||
BoostInfo(boostList, boostList[2], badge, boostMap.keys)
|
||||
}.subscribeBy(
|
||||
onNext = { info ->
|
||||
store.update {
|
||||
|
@ -57,7 +69,8 @@ class BoostViewModel(
|
|||
boosts = info.boosts,
|
||||
selectedBoost = if (it.selectedBoost in info.boosts) it.selectedBoost else info.defaultBoost,
|
||||
boostBadge = it.boostBadge ?: info.boostBadge,
|
||||
stage = if (it.stage == BoostState.Stage.INIT || it.stage == BoostState.Stage.FAILURE) BoostState.Stage.READY else it.stage
|
||||
stage = if (it.stage == BoostState.Stage.INIT || it.stage == BoostState.Stage.FAILURE) BoostState.Stage.READY else it.stage,
|
||||
supportedCurrencyCodes = info.supportedCurrencies.map(Currency::getCurrencyCode)
|
||||
)
|
||||
}
|
||||
},
|
||||
|
@ -77,7 +90,7 @@ class BoostViewModel(
|
|||
disposables += currencyObservable.subscribeBy { currency ->
|
||||
store.update {
|
||||
it.copy(
|
||||
currencySelection = CurrencySelection(currency.currencyCode),
|
||||
currencySelection = currency,
|
||||
isCustomAmountFocused = false,
|
||||
customAmount = FiatMoney(
|
||||
BigDecimal.ZERO, currency
|
||||
|
@ -169,7 +182,7 @@ class BoostViewModel(
|
|||
store.update { it.copy(isCustomAmountFocused = isFocused) }
|
||||
}
|
||||
|
||||
private data class BoostInfo(val boosts: List<Boost>, val defaultBoost: Boost?, val boostBadge: Badge)
|
||||
private data class BoostInfo(val boosts: List<Boost>, val defaultBoost: Boost?, val boostBadge: Badge, val supportedCurrencies: Set<Currency>)
|
||||
|
||||
class Factory(
|
||||
private val boostRepository: BoostRepository,
|
||||
|
|
|
@ -15,7 +15,8 @@ class SetCurrencyFragment : DSLSettingsBottomSheetFragment() {
|
|||
|
||||
private val viewModel: SetCurrencyViewModel by viewModels(
|
||||
factoryProducer = {
|
||||
SetCurrencyViewModel.Factory(SetCurrencyFragmentArgs.fromBundle(requireArguments()).isBoost)
|
||||
val args = SetCurrencyFragmentArgs.fromBundle(requireArguments())
|
||||
SetCurrencyViewModel.Factory(args.isBoost, args.supportedCurrencyCodes.toList())
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ import androidx.annotation.VisibleForTesting
|
|||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import org.signal.donations.StripeApi
|
||||
import org.thoughtcrime.securesms.BuildConfig
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.storage.StorageSyncHelper
|
||||
|
@ -14,32 +13,26 @@ import org.whispersystems.signalservice.api.subscriptions.SubscriberId
|
|||
import java.util.Currency
|
||||
import java.util.Locale
|
||||
|
||||
class SetCurrencyViewModel(private val isBoost: Boolean) : ViewModel() {
|
||||
class SetCurrencyViewModel(
|
||||
private val isBoost: Boolean,
|
||||
supportedCurrencyCodes: List<String>
|
||||
) : ViewModel() {
|
||||
|
||||
private val store = Store(SetCurrencyState())
|
||||
private val store = Store(
|
||||
SetCurrencyState(
|
||||
selectedCurrencyCode = if (isBoost) {
|
||||
SignalStore.donationsValues().getBoostCurrency().currencyCode
|
||||
} else {
|
||||
SignalStore.donationsValues().getSubscriptionCurrency().currencyCode
|
||||
},
|
||||
currencies = supportedCurrencyCodes
|
||||
.map(Currency::getInstance)
|
||||
.sortedWith(CurrencyComparator(BuildConfig.DEFAULT_CURRENCIES.split(",")))
|
||||
)
|
||||
)
|
||||
|
||||
val state: LiveData<SetCurrencyState> = store.stateLiveData
|
||||
|
||||
init {
|
||||
val defaultCurrency = if (isBoost) {
|
||||
SignalStore.donationsValues().getBoostCurrency()
|
||||
} else {
|
||||
SignalStore.donationsValues().getSubscriptionCurrency()
|
||||
}
|
||||
|
||||
store.update { state ->
|
||||
val platformCurrencies = Currency.getAvailableCurrencies()
|
||||
val stripeCurrencies = platformCurrencies
|
||||
.filter { StripeApi.Validation.supportedCurrencyCodes.contains(it.currencyCode) }
|
||||
.sortedWith(CurrencyComparator(BuildConfig.DEFAULT_CURRENCIES.split(",")))
|
||||
|
||||
state.copy(
|
||||
selectedCurrencyCode = defaultCurrency.currencyCode,
|
||||
currencies = stripeCurrencies
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun setSelectedCurrency(selectedCurrencyCode: String) {
|
||||
store.update { it.copy(selectedCurrencyCode = selectedCurrencyCode) }
|
||||
|
||||
|
@ -93,9 +86,9 @@ class SetCurrencyViewModel(private val isBoost: Boolean) : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
class Factory(private val isBoost: Boolean) : ViewModelProvider.Factory {
|
||||
class Factory(private val isBoost: Boolean, private val supportedCurrencyCodes: List<String>) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
return modelClass.cast(SetCurrencyViewModel(isBoost))!!
|
||||
return modelClass.cast(SetCurrencyViewModel(isBoost, supportedCurrencyCodes))!!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import com.google.android.material.button.MaterialButton
|
|||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.badges.BadgeImageView
|
||||
import org.thoughtcrime.securesms.components.settings.PreferenceModel
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.payments.FiatMoneyUtil
|
||||
import org.thoughtcrime.securesms.subscription.Subscription
|
||||
import org.thoughtcrime.securesms.util.DateUtils
|
||||
|
@ -51,7 +52,7 @@ object ActiveSubscriptionPreference {
|
|||
R.string.MySupportPreference__s_per_month,
|
||||
FiatMoneyUtil.format(
|
||||
context.resources,
|
||||
model.subscription.price,
|
||||
model.subscription.prices.first { it.currency == SignalStore.donationsValues().getSubscriptionCurrency() },
|
||||
FiatMoneyUtil.formatOptions()
|
||||
)
|
||||
)
|
||||
|
|
|
@ -12,7 +12,6 @@ import io.reactivex.rxjava3.kotlin.subscribeBy
|
|||
import io.reactivex.rxjava3.subjects.PublishSubject
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.SubscriptionsRepository
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.subscription.LevelUpdate
|
||||
import org.thoughtcrime.securesms.util.livedata.Store
|
||||
|
@ -66,7 +65,7 @@ class ManageDonationsViewModel(
|
|||
}
|
||||
)
|
||||
|
||||
disposables += subscriptionsRepository.getSubscriptions(SignalStore.donationsValues().getSubscriptionCurrency()).subscribeBy(
|
||||
disposables += subscriptionsRepository.getSubscriptions().subscribeBy(
|
||||
onSuccess = { subs ->
|
||||
store.update { it.copy(availableSubscriptions = subs) }
|
||||
},
|
||||
|
|
|
@ -6,19 +6,16 @@ import org.thoughtcrime.securesms.R
|
|||
import org.thoughtcrime.securesms.components.settings.PreferenceModel
|
||||
import org.thoughtcrime.securesms.util.MappingAdapter
|
||||
import org.thoughtcrime.securesms.util.MappingViewHolder
|
||||
import java.util.Currency
|
||||
|
||||
data class CurrencySelection(
|
||||
val selectedCurrencyCode: String,
|
||||
) {
|
||||
object CurrencySelection {
|
||||
|
||||
companion object {
|
||||
fun register(adapter: MappingAdapter) {
|
||||
adapter.registerFactory(Model::class.java, MappingAdapter.LayoutFactory({ ViewHolder(it) }, R.layout.subscription_currency_selection))
|
||||
}
|
||||
fun register(adapter: MappingAdapter) {
|
||||
adapter.registerFactory(Model::class.java, MappingAdapter.LayoutFactory({ ViewHolder(it) }, R.layout.subscription_currency_selection))
|
||||
}
|
||||
|
||||
class Model(
|
||||
val currencySelection: CurrencySelection,
|
||||
val selectedCurrency: Currency,
|
||||
override val isEnabled: Boolean,
|
||||
val onClick: () -> Unit
|
||||
) : PreferenceModel<Model>(isEnabled = isEnabled) {
|
||||
|
@ -28,7 +25,7 @@ data class CurrencySelection(
|
|||
|
||||
override fun areContentsTheSame(newItem: Model): Boolean {
|
||||
return super.areContentsTheSame(newItem) &&
|
||||
newItem.currencySelection.selectedCurrencyCode == currencySelection.selectedCurrencyCode
|
||||
newItem.selectedCurrency == selectedCurrency
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,7 +34,7 @@ data class CurrencySelection(
|
|||
private val spinner: TextView = itemView.findViewById(R.id.subscription_currency_selection_spinner)
|
||||
|
||||
override fun bind(model: Model) {
|
||||
spinner.text = model.currencySelection.selectedCurrencyCode
|
||||
spinner.text = model.selectedCurrency.currencyCode
|
||||
|
||||
itemView.setOnClickListener { model.onClick() }
|
||||
|
||||
|
|
|
@ -118,10 +118,13 @@ class SubscribeFragment : DSLSettingsFragment(
|
|||
|
||||
customPref(
|
||||
CurrencySelection.Model(
|
||||
currencySelection = state.currencySelection,
|
||||
selectedCurrency = state.currencySelection,
|
||||
isEnabled = areFieldsEnabled && state.activeSubscription?.isActive != true,
|
||||
onClick = {
|
||||
findNavController().navigate(SubscribeFragmentDirections.actionSubscribeFragmentToSetDonationCurrencyFragment(false))
|
||||
val selectableCurrencies = viewModel.getSelectableCurrencyCodes()
|
||||
if (selectableCurrencies != null) {
|
||||
findNavController().navigate(SubscribeFragmentDirections.actionSubscribeFragmentToSetDonationCurrencyFragment(false, selectableCurrencies.toTypedArray()))
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
|
@ -138,7 +141,8 @@ class SubscribeFragment : DSLSettingsFragment(
|
|||
isActive = isActive,
|
||||
willRenew = isActive && state.activeSubscription?.activeSubscription?.willCancelAtPeriodEnd() ?: false,
|
||||
onClick = { viewModel.setSelectedSubscription(it) },
|
||||
renewalTimestamp = TimeUnit.SECONDS.toMillis(state.activeSubscription?.activeSubscription?.endOfCurrentPeriod ?: 0L)
|
||||
renewalTimestamp = TimeUnit.SECONDS.toMillis(state.activeSubscription?.activeSubscription?.endOfCurrentPeriod ?: 0L),
|
||||
selectedCurrency = state.currencySelection
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -154,7 +158,7 @@ class SubscribeFragment : DSLSettingsFragment(
|
|||
text = DSLSettingsText.from(R.string.SubscribeFragment__update_subscription),
|
||||
isEnabled = areFieldsEnabled && (!activeAndSameLevel || isExpiring),
|
||||
onClick = {
|
||||
val price = viewModel.state.value?.selectedSubscription?.price ?: return@primaryButton
|
||||
val price = viewModel.getPriceOfSelectedSubscription() ?: return@primaryButton
|
||||
val calendar = Calendar.getInstance()
|
||||
|
||||
calendar.add(Calendar.MONTH, 1)
|
||||
|
@ -222,7 +226,7 @@ class SubscribeFragment : DSLSettingsFragment(
|
|||
}
|
||||
|
||||
private fun onGooglePayButtonClicked() {
|
||||
viewModel.requestTokenFromGooglePay(requireContext())
|
||||
viewModel.requestTokenFromGooglePay()
|
||||
}
|
||||
|
||||
private fun onPaymentConfirmed(badge: Badge) {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
package org.thoughtcrime.securesms.components.settings.app.subscription.subscribe
|
||||
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.models.CurrencySelection
|
||||
import org.thoughtcrime.securesms.subscription.Subscription
|
||||
import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription
|
||||
import java.util.Currency
|
||||
|
||||
data class SubscribeState(
|
||||
val currencySelection: CurrencySelection = CurrencySelection("USD"),
|
||||
val currencySelection: Currency,
|
||||
val subscriptions: List<Subscription> = listOf(),
|
||||
val selectedSubscription: Subscription? = null,
|
||||
val activeSubscription: ActiveSubscription? = null,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package org.thoughtcrime.securesms.components.settings.app.subscription.subscribe
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
@ -8,21 +7,26 @@ import androidx.lifecycle.ViewModelProvider
|
|||
import com.google.android.gms.wallet.PaymentData
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import io.reactivex.rxjava3.kotlin.plusAssign
|
||||
import io.reactivex.rxjava3.kotlin.subscribeBy
|
||||
import io.reactivex.rxjava3.subjects.PublishSubject
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.core.util.money.FiatMoney
|
||||
import org.signal.donations.GooglePayApi
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationEvent
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationPaymentRepository
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.SubscriptionsRepository
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.models.CurrencySelection
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.storage.StorageSyncHelper
|
||||
import org.thoughtcrime.securesms.subscription.LevelUpdate
|
||||
import org.thoughtcrime.securesms.subscription.Subscriber
|
||||
import org.thoughtcrime.securesms.subscription.Subscription
|
||||
import org.thoughtcrime.securesms.util.PlatformCurrencyUtil
|
||||
import org.thoughtcrime.securesms.util.livedata.Store
|
||||
import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription
|
||||
import org.whispersystems.signalservice.api.subscriptions.SubscriberId
|
||||
import java.util.Currency
|
||||
|
||||
class SubscribeViewModel(
|
||||
|
@ -31,7 +35,7 @@ class SubscribeViewModel(
|
|||
private val fetchTokenRequestCode: Int
|
||||
) : ViewModel() {
|
||||
|
||||
private val store = Store(SubscribeState())
|
||||
private val store = Store(SubscribeState(currencySelection = SignalStore.donationsValues().getSubscriptionCurrency()))
|
||||
private val eventPublisher: PublishSubject<DonationEvent> = PublishSubject.create()
|
||||
private val disposables = CompositeDisposable()
|
||||
|
||||
|
@ -45,11 +49,20 @@ class SubscribeViewModel(
|
|||
disposables.clear()
|
||||
}
|
||||
|
||||
fun getPriceOfSelectedSubscription(): FiatMoney? {
|
||||
return store.state.selectedSubscription?.prices?.first { it.currency == store.state.currencySelection }
|
||||
}
|
||||
|
||||
fun getSelectableCurrencyCodes(): List<String>? {
|
||||
return store.state.subscriptions.firstOrNull()?.prices?.map { it.currency.currencyCode }
|
||||
}
|
||||
|
||||
fun refresh() {
|
||||
disposables.clear()
|
||||
|
||||
val currency: Observable<Currency> = SignalStore.donationsValues().observableSubscriptionCurrency
|
||||
val allSubscriptions: Observable<List<Subscription>> = currency.switchMapSingle { subscriptionsRepository.getSubscriptions(it) }
|
||||
val allSubscriptions: Single<List<Subscription>> = subscriptionsRepository.getSubscriptions()
|
||||
|
||||
refreshActiveSubscription()
|
||||
|
||||
disposables += LevelUpdate.isProcessing.subscribeBy {
|
||||
|
@ -60,7 +73,25 @@ class SubscribeViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
disposables += Observable.combineLatest(allSubscriptions, activeSubscriptionSubject, ::Pair).subscribeBy(
|
||||
disposables += allSubscriptions.subscribeBy(
|
||||
onSuccess = { subscriptions ->
|
||||
if (subscriptions.isNotEmpty()) {
|
||||
val priceCurrencies = subscriptions[0].prices.map { it.currency }
|
||||
val selectedCurrency = SignalStore.donationsValues().getSubscriptionCurrency()
|
||||
|
||||
if (selectedCurrency !in priceCurrencies) {
|
||||
Log.w(TAG, "Unsupported currency selection. Defaulting to USD. $currency isn't supported.")
|
||||
val usd = PlatformCurrencyUtil.USD
|
||||
val newSubscriber = SignalStore.donationsValues().getSubscriber(usd) ?: Subscriber(SubscriberId.generate(), usd.currencyCode)
|
||||
SignalStore.donationsValues().setSubscriber(newSubscriber)
|
||||
StorageSyncHelper.scheduleSyncForDataChange()
|
||||
}
|
||||
}
|
||||
},
|
||||
onError = {}
|
||||
)
|
||||
|
||||
disposables += Observable.combineLatest(allSubscriptions.toObservable(), activeSubscriptionSubject, ::Pair).subscribeBy(
|
||||
onNext = { (subs, active) ->
|
||||
store.update {
|
||||
it.copy(
|
||||
|
@ -79,7 +110,7 @@ class SubscribeViewModel(
|
|||
onError = { eventPublisher.onNext(DonationEvent.GooglePayUnavailableError(it)) }
|
||||
)
|
||||
|
||||
disposables += currency.map { CurrencySelection(it.currencyCode) }.subscribe { selection ->
|
||||
disposables += currency.subscribe { selection ->
|
||||
store.update { it.copy(currencySelection = selection) }
|
||||
}
|
||||
}
|
||||
|
@ -190,7 +221,7 @@ class SubscribeViewModel(
|
|||
)
|
||||
}
|
||||
|
||||
fun requestTokenFromGooglePay(context: Context) {
|
||||
fun requestTokenFromGooglePay() {
|
||||
val snapshot = store.state
|
||||
if (snapshot.selectedSubscription == null) {
|
||||
return
|
||||
|
@ -198,8 +229,10 @@ class SubscribeViewModel(
|
|||
|
||||
store.update { it.copy(stage = SubscribeState.Stage.TOKEN_REQUEST) }
|
||||
|
||||
val selectedCurrency = snapshot.currencySelection
|
||||
|
||||
subscriptionToPurchase = snapshot.selectedSubscription
|
||||
donationPaymentRepository.requestTokenFromGooglePay(snapshot.selectedSubscription.price, snapshot.selectedSubscription.name, fetchTokenRequestCode)
|
||||
donationPaymentRepository.requestTokenFromGooglePay(snapshot.selectedSubscription.prices.first { it.currency == selectedCurrency }, snapshot.selectedSubscription.name, fetchTokenRequestCode)
|
||||
}
|
||||
|
||||
fun setSelectedSubscription(subscription: Subscription) {
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.util.DateUtils
|
|||
import org.thoughtcrime.securesms.util.MappingAdapter
|
||||
import org.thoughtcrime.securesms.util.MappingViewHolder
|
||||
import org.thoughtcrime.securesms.util.visible
|
||||
import java.util.Currency
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
|
@ -22,7 +23,7 @@ data class Subscription(
|
|||
val id: String,
|
||||
val name: String,
|
||||
val badge: Badge,
|
||||
val price: FiatMoney,
|
||||
val prices: Set<FiatMoney>,
|
||||
val level: Int,
|
||||
) {
|
||||
|
||||
|
@ -39,7 +40,8 @@ data class Subscription(
|
|||
val willRenew: Boolean,
|
||||
override val isEnabled: Boolean,
|
||||
val onClick: () -> Unit,
|
||||
val renewalTimestamp: Long
|
||||
val renewalTimestamp: Long,
|
||||
val selectedCurrency: Currency
|
||||
) : PreferenceModel<Model>(isEnabled = isEnabled) {
|
||||
|
||||
override fun areItemsTheSame(newItem: Model): Boolean {
|
||||
|
@ -52,7 +54,8 @@ data class Subscription(
|
|||
newItem.isSelected == isSelected &&
|
||||
newItem.isActive == isActive &&
|
||||
newItem.renewalTimestamp == renewalTimestamp &&
|
||||
newItem.willRenew == willRenew
|
||||
newItem.willRenew == willRenew &&
|
||||
newItem.selectedCurrency == selectedCurrency
|
||||
}
|
||||
|
||||
override fun getChangePayload(newItem: Model): Any? {
|
||||
|
@ -86,7 +89,7 @@ data class Subscription(
|
|||
|
||||
val formattedPrice = FiatMoneyUtil.format(
|
||||
context.resources,
|
||||
model.subscription.price,
|
||||
model.subscription.prices.first { it.currency == model.selectedCurrency },
|
||||
FiatMoneyUtil.formatOptions()
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package org.thoughtcrime.securesms.util
|
||||
|
||||
import java.util.Currency
|
||||
|
||||
/**
|
||||
* Utility methods for java.util.Currency
|
||||
*
|
||||
* This is prefixed with "Platform" as there are several different Currency classes
|
||||
* available in the app, and this utility class is specifically for dealing with
|
||||
* java.util.Currency
|
||||
*/
|
||||
object PlatformCurrencyUtil {
|
||||
|
||||
val USD: Currency = Currency.getInstance("USD")
|
||||
|
||||
/**
|
||||
* Note: Adding this as an extension method of Currency causes some confusion in
|
||||
* AndroidStudio due to a separate Currency class from the AndroidSDK having
|
||||
* an extension method of the same signature.
|
||||
*/
|
||||
fun getAvailableCurrencyCodes(): Set<String> {
|
||||
return Currency.getAvailableCurrencies().map { it.currencyCode }.toSet()
|
||||
}
|
||||
}
|
|
@ -31,6 +31,10 @@
|
|||
<argument
|
||||
android:name="isBoost"
|
||||
app:argType="boolean" />
|
||||
|
||||
<argument
|
||||
android:name="supportedCurrencyCodes"
|
||||
app:argType="string[]" />
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
|
|
|
@ -73,6 +73,9 @@
|
|||
android:name="isBoost"
|
||||
app:argType="boolean" />
|
||||
|
||||
<argument
|
||||
android:name="supportedCurrencyCodes"
|
||||
app:argType="string[]" />
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
|
|
Loading…
Add table
Reference in a new issue