Remove deprecated LevelConfiguration#name.

Co-authored-by: Alex Hart <alex@signal.org>
This commit is contained in:
Chris Eager 2024-10-17 07:25:24 -05:00 committed by Greyson Parrelli
parent 200132e623
commit ba34a74e2d
21 changed files with 56 additions and 105 deletions

View file

@ -20,7 +20,6 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import kotlinx.coroutines.rx3.asFlowable
import org.signal.core.util.getSerializableCompat
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
import org.thoughtcrime.securesms.components.settings.app.subscription.donate.InAppPaymentCheckoutDelegate
import org.thoughtcrime.securesms.compose.ComposeFragment
@ -81,17 +80,6 @@ class MessageBackupsFlowFragment : ComposeFragment(), InAppPaymentCheckoutDelega
)
}
LaunchedEffect(
state.selectedMessageBackupTier,
state.selectedMessageBackupTierLabel,
state.availableBackupTypes
) {
if (state.selectedMessageBackupTierLabel == null && state.selectedMessageBackupTier != null && state.availableBackupTypes.isNotEmpty()) {
val type = state.availableBackupTypes.firstOrNull { it.tier == state.selectedMessageBackupTier } ?: return@LaunchedEffect
viewModel.onMessageBackupTierUpdated(type.tier, getTypeLabel(type))
}
}
Nav.Host(
navController = navController,
startDestination = state.startScreen.name
@ -133,7 +121,7 @@ class MessageBackupsFlowFragment : ComposeFragment(), InAppPaymentCheckoutDelega
onMessageBackupsTierSelected = { tier ->
val type = state.availableBackupTypes.first { it.tier == tier }
viewModel.onMessageBackupTierUpdated(tier, getTypeLabel(type))
viewModel.onMessageBackupTierUpdated(tier)
},
onNavigationClick = viewModel::goToPreviousStage,
onReadMoreClicked = {},
@ -164,13 +152,6 @@ class MessageBackupsFlowFragment : ComposeFragment(), InAppPaymentCheckoutDelega
}
}
private fun getTypeLabel(type: MessageBackupsType): String {
return when (type) {
is MessageBackupsType.Free -> requireContext().resources.getQuantityString(R.plurals.MessageBackupsTypeSelectionScreen__text_plus_d_days_of_media, type.mediaRetentionDays, type.mediaRetentionDays)
is MessageBackupsType.Paid -> requireContext().getString(R.string.MessageBackupsTypeSelectionScreen__text_plus_all_your_media)
}
}
override fun onUserLaunchedAnExternalApplication() = error("Not supported by this fragment.")
override fun navigateToDonationPending(inAppPayment: InAppPaymentTable.InAppPayment) = error("Not supported by this fragment.")

View file

@ -12,7 +12,6 @@ import org.whispersystems.signalservice.api.backup.BackupKey
data class MessageBackupsFlowState(
val hasBackupSubscriberAvailable: Boolean = false,
val selectedMessageBackupTierLabel: String? = null,
val selectedMessageBackupTier: MessageBackupTier? = SignalStore.backup.backupTier,
val currentMessageBackupTier: MessageBackupTier? = SignalStore.backup.backupTier,
val availableBackupTypes: List<MessageBackupsType> = emptyList(),

View file

@ -171,11 +171,10 @@ class MessageBackupsFlowViewModel(
}
}
fun onMessageBackupTierUpdated(messageBackupTier: MessageBackupTier, messageBackupTierLabel: String) {
fun onMessageBackupTierUpdated(messageBackupTier: MessageBackupTier) {
internalStateFlow.update {
it.copy(
selectedMessageBackupTier = messageBackupTier,
selectedMessageBackupTierLabel = messageBackupTierLabel
selectedMessageBackupTier = messageBackupTier
)
}
}
@ -207,7 +206,6 @@ class MessageBackupsFlowViewModel(
endOfPeriod = null,
inAppPaymentData = InAppPaymentData(
badge = null,
label = state.selectedMessageBackupTierLabel!!,
amount = paidFiat.toFiatValue(),
level = SubscriptionsConfiguration.BACKUPS_LEVEL.toLong(),
recipientId = Recipient.self().id.serialize(),

View file

@ -115,7 +115,7 @@ class GiftFlowConfirmationFragment :
val continueButton = requireView().findViewById<MaterialButton>(R.id.continue_button)
continueButton.setOnClickListener {
lifecycleDisposable += viewModel.insertInAppPayment(requireContext()).subscribe { inAppPayment ->
lifecycleDisposable += viewModel.insertInAppPayment().subscribe { inAppPayment ->
findNavController().safeNavigate(
GiftFlowConfirmationFragmentDirections.actionGiftFlowConfirmationFragmentToGatewaySelectorBottomSheet(
inAppPayment

View file

@ -1,11 +1,9 @@
package org.thoughtcrime.securesms.badges.gifts.flow
import android.content.Context
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.schedulers.Schedulers
import org.signal.core.util.money.FiatMoney
import org.signal.donations.InAppPaymentType
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.badges.Badges
import org.thoughtcrime.securesms.badges.models.Badge
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationSerializationHelper.toFiatValue
@ -25,7 +23,7 @@ import java.util.Locale
*/
class GiftFlowRepository {
fun insertInAppPayment(context: Context, giftSnapshot: GiftFlowState): Single<InAppPaymentTable.InAppPayment> {
fun insertInAppPayment(giftSnapshot: GiftFlowState): Single<InAppPaymentTable.InAppPayment> {
return Single.fromCallable {
SignalDatabase.inAppPayments.insert(
type = InAppPaymentType.ONE_TIME_GIFT,
@ -34,7 +32,6 @@ class GiftFlowRepository {
endOfPeriod = null,
inAppPaymentData = InAppPaymentData(
badge = Badges.toDatabaseBadge(giftSnapshot.giftBadge!!),
label = context.getString(R.string.preferences__one_time),
amount = giftSnapshot.giftPrices[giftSnapshot.currency]!!.toFiatValue(),
level = giftSnapshot.giftLevel!!,
recipientId = giftSnapshot.recipient!!.id.serialize(),

View file

@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.badges.gifts.flow
import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
@ -104,9 +103,9 @@ class GiftFlowViewModel(
)
}
fun insertInAppPayment(context: Context): Single<InAppPaymentTable.InAppPayment> {
fun insertInAppPayment(): Single<InAppPaymentTable.InAppPayment> {
val giftSnapshot = snapshot
return giftFlowRepository.insertInAppPayment(context, giftSnapshot)
return giftFlowRepository.insertInAppPayment(giftSnapshot)
.doOnSuccess { inAppPayment ->
store.update { it.copy(inAppPaymentId = inAppPayment.id) }
}

View file

@ -24,7 +24,7 @@ private const val SEPA_DEBIT = "SEPA_DEBIT"
* PAYPAL - PayPal
*
* @param level The subscription level to get amounts for
* @param paymentMethodAvailability Predicate object which checks whether different payment methods are availble.
* @param paymentMethodAvailability Predicate object which checks whether different payment methods are available.
*/
fun SubscriptionsConfiguration.getSubscriptionAmounts(
level: Int,

View file

@ -1,7 +1,10 @@
package org.thoughtcrime.securesms.components.settings.app.subscription
import android.content.Context
import org.signal.donations.InAppPaymentType
import org.signal.donations.PaymentSourceType
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.database.model.InAppPaymentReceiptRecord
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.Environment
@ -100,4 +103,32 @@ object InAppDonations {
fun isIDEALAvailbleForDonateToSignalType(inAppPaymentType: InAppPaymentType): Boolean {
return inAppPaymentType != InAppPaymentType.ONE_TIME_GIFT && isIDEALAvailable()
}
/**
* Labels are utilized when displaying Google Play sheet and when displaying receipts.
*/
fun resolveLabel(context: Context, inAppPaymentType: InAppPaymentType, level: Long): String {
return when (inAppPaymentType) {
InAppPaymentType.UNKNOWN -> error("Unsupported type.")
InAppPaymentType.ONE_TIME_GIFT -> context.getString(R.string.DonationReceiptListFragment__donation_for_a_friend)
InAppPaymentType.ONE_TIME_DONATION -> context.getString(R.string.DonationReceiptListFragment__one_time)
InAppPaymentType.RECURRING_DONATION -> context.getString(R.string.InAppDonations__recurring_d, level)
InAppPaymentType.RECURRING_BACKUP -> error("Unsupported type.")
}
}
/**
* Labels are utilized when displaying Google Play sheet and when displaying receipts.
*/
fun resolveLabel(context: Context, inAppPaymentReceiptRecord: InAppPaymentReceiptRecord): String {
val level = inAppPaymentReceiptRecord.subscriptionLevel
val type: InAppPaymentType = when (inAppPaymentReceiptRecord.type) {
InAppPaymentReceiptRecord.Type.RECURRING_BACKUP -> InAppPaymentType.RECURRING_BACKUP
InAppPaymentReceiptRecord.Type.RECURRING_DONATION -> InAppPaymentType.RECURRING_DONATION
InAppPaymentReceiptRecord.Type.ONE_TIME_DONATION -> InAppPaymentType.ONE_TIME_DONATION
InAppPaymentReceiptRecord.Type.ONE_TIME_GIFT -> InAppPaymentType.ONE_TIME_GIFT
}
return resolveLabel(context, type, level.toLong())
}
}

View file

@ -80,7 +80,6 @@ object RecurringInAppPaymentRepository {
Subscription(
id = level.toString(),
level = level,
name = levelConfig.name,
badge = Badges.fromServiceBadge(levelConfig.badge),
prices = config.getSubscriptionAmounts(level)
)

View file

@ -206,7 +206,6 @@ class DonateToSignalViewModel(
endOfPeriod = null,
inAppPaymentData = InAppPaymentData(
badge = snapshot.badge?.let { Badges.toDatabaseBadge(it) },
label = snapshot.badge?.description ?: "",
amount = amount.toFiatValue(),
level = snapshot.level.toLong(),
recipientId = Recipient.self().id.serialize(),

View file

@ -160,7 +160,7 @@ class InAppPaymentCheckoutDelegate(
viewModel.provideGatewayRequestForGooglePay(inAppPayment)
inAppPaymentComponent.stripeRepository.requestTokenFromGooglePay(
price = inAppPayment.data.amount!!.toFiatMoney(),
label = inAppPayment.data.label,
label = InAppDonations.resolveLabel(fragment.requireContext(), inAppPayment.type, inAppPayment.data.level),
requestCode = InAppPaymentsRepository.getGooglePayRequestCode(inAppPayment.type)
)
}

View file

@ -52,7 +52,6 @@ data class Stripe3DSData(
InAppPaymentType.RECURRING_BACKUP -> ExternalLaunchTransactionState.GatewayRequest.InAppPaymentType.RECURRING_BACKUPS
},
badge = inAppPayment.data.badge,
label = inAppPayment.data.label,
price = inAppPayment.data.amount!!.amount,
currencyCode = inAppPayment.data.amount.currencyCode,
level = inAppPayment.data.level,
@ -92,7 +91,6 @@ data class Stripe3DSData(
data = InAppPaymentData(
paymentMethodType = PaymentSourceType.fromCode(proto.paymentSourceType).toPaymentMethodType(),
badge = proto.gatewayRequest.badge,
label = proto.gatewayRequest.label,
amount = FiatValue(amount = proto.gatewayRequest.price, currencyCode = proto.gatewayRequest.currencyCode),
level = proto.gatewayRequest.level,
recipientId = null,

View file

@ -8,6 +8,7 @@ import org.thoughtcrime.securesms.components.SignalProgressDialog
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
import org.thoughtcrime.securesms.components.settings.DSLSettingsText
import org.thoughtcrime.securesms.components.settings.app.subscription.InAppDonations
import org.thoughtcrime.securesms.components.settings.app.subscription.receipts.ReceiptImageRenderer
import org.thoughtcrime.securesms.components.settings.configure
import org.thoughtcrime.securesms.components.settings.models.SplashImage
@ -38,10 +39,9 @@ class DonationReceiptDetailFragment : DSLSettingsFragment(layoutId = R.layout.do
viewModel.state.observe(viewLifecycleOwner) { state ->
if (state.inAppPaymentReceiptRecord != null) {
adapter.submitList(getConfiguration(state.inAppPaymentReceiptRecord, state.subscriptionName).toMappingModelList())
}
val subscriptionName = InAppDonations.resolveLabel(requireContext(), state.inAppPaymentReceiptRecord)
adapter.submitList(getConfiguration(state.inAppPaymentReceiptRecord, subscriptionName).toMappingModelList())
if (state.inAppPaymentReceiptRecord != null && state.subscriptionName != null) {
sharePngButton.isEnabled = true
sharePngButton.setOnClickListener {
progressDialog = SignalProgressDialog.show(requireContext())
@ -49,7 +49,7 @@ class DonationReceiptDetailFragment : DSLSettingsFragment(layoutId = R.layout.do
context = requireContext(),
lifecycleOwner = viewLifecycleOwner,
record = state.inAppPaymentReceiptRecord,
subscriptionName = state.subscriptionName,
subscriptionName = InAppDonations.resolveLabel(requireContext(), state.inAppPaymentReceiptRecord),
callback = object : ReceiptImageRenderer.Callback {
override fun onBitmapRendered() {
progressDialog.dismiss()

View file

@ -2,26 +2,10 @@ package org.thoughtcrime.securesms.components.settings.app.subscription.receipts
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.schedulers.Schedulers
import org.thoughtcrime.securesms.components.settings.app.subscription.getSubscriptionLevels
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.InAppPaymentReceiptRecord
import org.thoughtcrime.securesms.dependencies.AppDependencies
import java.util.Locale
class DonationReceiptDetailRepository {
fun getSubscriptionLevelName(subscriptionLevel: Int): Single<String> {
return Single
.fromCallable {
AppDependencies
.donationsService
.getDonationsConfiguration(Locale.getDefault())
}
.flatMap { it.flattenResult() }
.map { it.getSubscriptionLevels()[subscriptionLevel] ?: throw Exception("Subscription level $subscriptionLevel not found") }
.map { it.name }
.subscribeOn(Schedulers.io())
}
fun getDonationReceiptRecord(id: Long): Single<InAppPaymentReceiptRecord> {
return Single.fromCallable<InAppPaymentReceiptRecord> {
SignalDatabase.donationReceipts.getReceipt(id)!!

View file

@ -3,6 +3,5 @@ package org.thoughtcrime.securesms.components.settings.app.subscription.receipts
import org.thoughtcrime.securesms.database.model.InAppPaymentReceiptRecord
data class DonationReceiptDetailState(
val inAppPaymentReceiptRecord: InAppPaymentReceiptRecord? = null,
val subscriptionName: String? = null
val inAppPaymentReceiptRecord: InAppPaymentReceiptRecord? = null
)

View file

@ -5,61 +5,32 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.kotlin.plusAssign
import org.thoughtcrime.securesms.database.model.InAppPaymentReceiptRecord
import org.thoughtcrime.securesms.util.InternetConnectionObserver
import org.thoughtcrime.securesms.util.livedata.Store
class DonationReceiptDetailViewModel(id: Long, private val repository: DonationReceiptDetailRepository) : ViewModel() {
private val store = Store(DonationReceiptDetailState())
private val disposables = CompositeDisposable()
private var networkDisposable: Disposable
private val cachedRecord: Single<InAppPaymentReceiptRecord> = repository.getDonationReceiptRecord(id).cache()
val state: LiveData<DonationReceiptDetailState> = store.stateLiveData
init {
networkDisposable = InternetConnectionObserver
.observe()
.distinctUntilChanged()
.subscribe { isConnected ->
if (isConnected) {
retry()
}
}
refresh()
}
private fun retry() {
if (store.state.subscriptionName == null) {
refresh()
}
}
private fun refresh() {
disposables.clear()
disposables += cachedRecord.subscribe { record ->
store.update { it.copy(inAppPaymentReceiptRecord = record) }
}
disposables += cachedRecord.flatMap {
if (it.subscriptionLevel > 0) {
repository.getSubscriptionLevelName(it.subscriptionLevel)
} else {
Single.just("")
}
}.subscribe { name ->
store.update { it.copy(subscriptionName = name) }
}
}
override fun onCleared() {
disposables.clear()
networkDisposable.dispose()
}
class Factory(private val id: Long, private val repository: DonationReceiptDetailRepository) : ViewModelProvider.Factory {

View file

@ -221,23 +221,23 @@ class InAppPaymentKeepAliveJob private constructor(
return null
}
val (badge, label) = if (oldInAppPayment == null) {
val badge = if (oldInAppPayment == null) {
info(type, "Old payment not found in database. Loading badge / label information from donations configuration.")
val configuration = AppDependencies.donationsService.getDonationsConfiguration(Locale.getDefault())
if (configuration.result.isPresent) {
val subscriptionConfig = configuration.result.get().levels[subscription.level]
if (subscriptionConfig == null) {
info(type, "Failed to load subscription configuration for level ${subscription.level} for type $type")
null to ""
null
} else {
Badges.toDatabaseBadge(Badges.fromServiceBadge(subscriptionConfig.badge)) to subscriptionConfig.name
Badges.toDatabaseBadge(Badges.fromServiceBadge(subscriptionConfig.badge))
}
} else {
warn(TAG, "Failed to load configuration while processing $type")
null to ""
null
}
} else {
oldInAppPayment.data.badge to oldInAppPayment.data.label
oldInAppPayment.data.badge
}
info(type, "End of period has changed. Requesting receipt refresh. (old: $oldEndOfPeriod, new: $endOfCurrentPeriod)")
@ -260,7 +260,6 @@ class InAppPaymentKeepAliveJob private constructor(
error = null,
level = subscription.level.toLong(),
cancellation = null,
label = label,
recipientId = null,
additionalMessage = null,
redemption = InAppPaymentData.RedemptionState(

View file

@ -27,7 +27,6 @@ import java.util.Locale
*/
data class Subscription(
val id: String,
val name: String,
val badge: Badge,
val prices: Set<FiatMoney>,
val level: Int

View file

@ -407,7 +407,7 @@ message InAppPaymentData {
optional Error error = 3; // An error, if present.
int64 level = 4; // The transaction "level" given to us by the server
optional Cancellation cancellation = 5; // The transaction was cancelled. This notes why.
string label = 6; // Descriptive text about the token
reserved 6; // removed: Descriptive text about the token
optional string recipientId = 7; // The target recipient the token is to be sent to (only used for gifts)
optional string additionalMessage = 8; // The additional message to send the target recipient (only used for gifts)
PaymentMethodType paymentMethodType = 9; // The method through which this in app payment was made
@ -484,7 +484,7 @@ message ExternalLaunchTransactionState {
InAppPaymentType inAppPaymentType = 1;
BadgeList.Badge badge = 2;
string label = 3;
reserved 3; // removed field label
DecimalValue price = 4;
string currencyCode = 5;
int64 level = 6;

View file

@ -5766,6 +5766,10 @@
<string name="InAppPaymentInProgressFragment__processing_donation">Processing donation…</string>
<string name="InAppPaymentInProgressFragment__processing_payment">Processing payment…</string>
<!--InAppDonations -->
<!-- Label displayed in google play and on receipts for recurring subscriptions. Placeholder is level -->
<string name="InAppDonations__recurring_d">Recurring %1$d</string>
<!-- InAppPaymentErrors -->
<!-- Displayed as a title in a dialog or notification when a payment failure happens. -->
<string name="InAppPaymentErrors__error_processing_payment">Error processing payment</string>

View file

@ -76,16 +76,10 @@ public class SubscriptionsConfiguration {
}
public static class LevelConfiguration {
@JsonProperty("name")
private String name;
@JsonProperty("badge")
private SignalServiceProfile.Badge badge;
public String getName() {
return name;
}
public SignalServiceProfile.Badge getBadge() {
return badge;
}