Respect server currency lists for subscriptions and badges.

This commit is contained in:
Alex Hart 2021-11-03 11:30:07 -03:00 committed by Greyson Parrelli
parent c06fb81490
commit 2a9eb1bae0
18 changed files with 162 additions and 78 deletions

View file

@ -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)

View file

@ -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 {

View file

@ -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()))
}
)
)

View file

@ -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)) } }
}
}

View file

@ -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,

View file

@ -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,

View file

@ -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())
}
)

View file

@ -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))!!
}
}
}

View file

@ -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()
)
)

View file

@ -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) }
},

View file

@ -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() }

View file

@ -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) {

View file

@ -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,

View file

@ -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) {

View file

@ -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()
)

View file

@ -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()
}
}

View file

@ -31,6 +31,10 @@
<argument
android:name="isBoost"
app:argType="boolean" />
<argument
android:name="supportedCurrencyCodes"
app:argType="string[]" />
</dialog>
<dialog

View file

@ -73,6 +73,9 @@
android:name="isBoost"
app:argType="boolean" />
<argument
android:name="supportedCurrencyCodes"
app:argType="string[]" />
</dialog>
<dialog