mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-24 13:08:46 +00:00
Show dialog when attempting to donate again while still processing previous donation.
This commit is contained in:
committed by
Greyson Parrelli
parent
b96b99c1c4
commit
117bbdbcdf
@@ -25,6 +25,7 @@ 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.configure
|
||||
import org.thoughtcrime.securesms.database.JobDatabase
|
||||
import org.thoughtcrime.securesms.database.LocalMetricsDatabase
|
||||
import org.thoughtcrime.securesms.database.LogDatabase
|
||||
import org.thoughtcrime.securesms.database.MegaphoneDatabase
|
||||
@@ -218,6 +219,18 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter
|
||||
}
|
||||
)
|
||||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from("Retry all jobs now"),
|
||||
summary = DSLSettingsText.from("Clear backoff intervals, app will restart"),
|
||||
onClick = {
|
||||
SimpleTask.run({
|
||||
JobDatabase.getInstance(ApplicationDependencies.getApplication()).debugResetBackoffInterval()
|
||||
}) {
|
||||
AppUtil.restart(requireContext())
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
dividerPref()
|
||||
|
||||
sectionHeaderPref(DSLSettingsText.from("Payments"))
|
||||
|
||||
@@ -17,24 +17,8 @@ import java.math.BigDecimal
|
||||
import java.math.BigInteger
|
||||
import java.math.MathContext
|
||||
import java.util.Currency
|
||||
import kotlin.time.Duration.Companion.days
|
||||
|
||||
object DonationSerializationHelper {
|
||||
|
||||
private val PENDING_ONE_TIME_BANK_TRANSFER_TIMEOUT = 14.days
|
||||
private val PENDING_ONE_TIME_NORMAL_TIMEOUT = 1.days
|
||||
|
||||
val PendingOneTimeDonation.isExpired: Boolean
|
||||
get() {
|
||||
val timeout = if (paymentMethodType == PendingOneTimeDonation.PaymentMethodType.SEPA_DEBIT) {
|
||||
PENDING_ONE_TIME_BANK_TRANSFER_TIMEOUT
|
||||
} else {
|
||||
PENDING_ONE_TIME_NORMAL_TIMEOUT
|
||||
}
|
||||
|
||||
return (timestamp + timeout.inWholeMilliseconds) < System.currentTimeMillis()
|
||||
}
|
||||
|
||||
fun createPendingOneTimeDonationProto(
|
||||
badge: Badge,
|
||||
paymentSourceType: PaymentSourceType,
|
||||
|
||||
@@ -41,6 +41,7 @@ import org.thoughtcrime.securesms.util.Projection
|
||||
import org.thoughtcrime.securesms.util.SpanUtil
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
|
||||
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
||||
import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription
|
||||
import java.util.Currency
|
||||
|
||||
/**
|
||||
@@ -233,7 +234,6 @@ class DonateToSignalFragment :
|
||||
|
||||
customPref(
|
||||
DonationPillToggle.Model(
|
||||
isEnabled = state.areFieldsEnabled,
|
||||
selected = state.donateToSignalType,
|
||||
onClick = {
|
||||
viewModel.toggleDonationType()
|
||||
@@ -256,23 +256,27 @@ class DonateToSignalFragment :
|
||||
text = DSLSettingsText.from(R.string.SubscribeFragment__update_subscription),
|
||||
isEnabled = state.canUpdate,
|
||||
onClick = {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.SubscribeFragment__update_subscription_question)
|
||||
.setMessage(
|
||||
getString(
|
||||
R.string.SubscribeFragment__you_will_be_charged_the_full_amount_s_of,
|
||||
FiatMoneyUtil.format(
|
||||
requireContext().resources,
|
||||
viewModel.getSelectedSubscriptionCost(),
|
||||
FiatMoneyUtil.formatOptions().trimZerosAfterDecimal()
|
||||
if (state.monthlyDonationState.transactionState.isTransactionJobPending) {
|
||||
showDonationPendingDialog(state)
|
||||
} else {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.SubscribeFragment__update_subscription_question)
|
||||
.setMessage(
|
||||
getString(
|
||||
R.string.SubscribeFragment__you_will_be_charged_the_full_amount_s_of,
|
||||
FiatMoneyUtil.format(
|
||||
requireContext().resources,
|
||||
viewModel.getSelectedSubscriptionCost(),
|
||||
FiatMoneyUtil.formatOptions().trimZerosAfterDecimal()
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
.setPositiveButton(R.string.SubscribeFragment__update) { _, _ ->
|
||||
viewModel.updateSubscription()
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||
.show()
|
||||
.setPositiveButton(R.string.SubscribeFragment__update) { _, _ ->
|
||||
viewModel.updateSubscription()
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||
.show()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -282,28 +286,58 @@ class DonateToSignalFragment :
|
||||
text = DSLSettingsText.from(R.string.SubscribeFragment__cancel_subscription),
|
||||
isEnabled = state.areFieldsEnabled,
|
||||
onClick = {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.SubscribeFragment__confirm_cancellation)
|
||||
.setMessage(R.string.SubscribeFragment__you_wont_be_charged_again)
|
||||
.setPositiveButton(R.string.SubscribeFragment__confirm) { _, _ ->
|
||||
viewModel.cancelSubscription()
|
||||
}
|
||||
.setNegativeButton(R.string.SubscribeFragment__not_now) { _, _ -> }
|
||||
.show()
|
||||
if (state.monthlyDonationState.transactionState.isTransactionJobPending) {
|
||||
showDonationPendingDialog(state)
|
||||
} else {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.SubscribeFragment__confirm_cancellation)
|
||||
.setMessage(R.string.SubscribeFragment__you_wont_be_charged_again)
|
||||
.setPositiveButton(R.string.SubscribeFragment__confirm) { _, _ ->
|
||||
viewModel.cancelSubscription()
|
||||
}
|
||||
.setNegativeButton(R.string.SubscribeFragment__not_now) { _, _ -> }
|
||||
.show()
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
primaryButton(
|
||||
text = DSLSettingsText.from(R.string.DonateToSignalFragment__continue),
|
||||
isEnabled = state.canContinue,
|
||||
isEnabled = state.continueEnabled,
|
||||
onClick = {
|
||||
viewModel.requestSelectGateway()
|
||||
if (state.canContinue) {
|
||||
viewModel.requestSelectGateway()
|
||||
} else {
|
||||
showDonationPendingDialog(state)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showDonationPendingDialog(state: DonateToSignalState) {
|
||||
val message = if (state.donateToSignalType == DonateToSignalType.ONE_TIME) {
|
||||
if (state.oneTimeDonationState.isOneTimeDonationLongRunning) {
|
||||
R.string.DonateToSignalFragment__bank_transfers_usually_take_1_business_day_to_process_onetime
|
||||
} else {
|
||||
R.string.DonateToSignalFragment__your_payment_is_still_being_processed_onetime
|
||||
}
|
||||
} else {
|
||||
if (state.monthlyDonationState.activeSubscription?.paymentMethod == ActiveSubscription.PAYMENT_METHOD_SEPA_DEBIT) {
|
||||
R.string.DonateToSignalFragment__bank_transfers_usually_take_1_business_day_to_process_monthly
|
||||
} else {
|
||||
R.string.DonateToSignalFragment__your_payment_is_still_being_processed_monthly
|
||||
}
|
||||
}
|
||||
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.DonateToSignalFragment__you_have_a_donation_pending)
|
||||
.setMessage(message)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun DSLConfiguration.displayOneTimeSelection(areFieldsEnabled: Boolean, state: DonateToSignalState.OneTimeDonationState) {
|
||||
when (state.donationStage) {
|
||||
DonateToSignalState.DonationStage.INIT -> customPref(Boost.LoadingModel())
|
||||
@@ -337,11 +371,6 @@ class DonateToSignalFragment :
|
||||
}
|
||||
|
||||
private fun DSLConfiguration.displayMonthlySelection(areFieldsEnabled: Boolean, state: DonateToSignalState.MonthlyDonationState) {
|
||||
if (state.transactionState.isTransactionJobPending) {
|
||||
customPref(Subscription.LoaderModel())
|
||||
return
|
||||
}
|
||||
|
||||
when (state.donationStage) {
|
||||
DonateToSignalState.DonationStage.INIT -> customPref(Subscription.LoaderModel())
|
||||
DonateToSignalState.DonationStage.FAILURE -> customPref(NetworkFailure.Model { viewModel.retryMonthlyDonationState() })
|
||||
|
||||
@@ -4,6 +4,8 @@ import org.signal.core.util.money.FiatMoney
|
||||
import org.thoughtcrime.securesms.badges.models.Badge
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.InAppDonations
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.boost.Boost
|
||||
import org.thoughtcrime.securesms.database.model.isLongRunning
|
||||
import org.thoughtcrime.securesms.database.model.isPending
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.subscription.Subscription
|
||||
import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription
|
||||
@@ -19,8 +21,8 @@ data class DonateToSignalState(
|
||||
|
||||
val areFieldsEnabled: Boolean
|
||||
get() = when (donateToSignalType) {
|
||||
DonateToSignalType.ONE_TIME -> oneTimeDonationState.donationStage == DonationStage.READY && !oneTimeDonationState.isOneTimeDonationPending
|
||||
DonateToSignalType.MONTHLY -> monthlyDonationState.donationStage == DonationStage.READY && !monthlyDonationState.transactionState.isInProgress
|
||||
DonateToSignalType.ONE_TIME -> oneTimeDonationState.donationStage == DonationStage.READY
|
||||
DonateToSignalType.MONTHLY -> monthlyDonationState.donationStage == DonationStage.READY
|
||||
DonateToSignalType.GIFT -> error("This flow does not support gifts")
|
||||
}
|
||||
|
||||
@@ -59,13 +61,20 @@ data class DonateToSignalState(
|
||||
DonateToSignalType.GIFT -> error("This flow does not support gifts")
|
||||
}
|
||||
|
||||
val canContinue: Boolean
|
||||
val continueEnabled: Boolean
|
||||
get() = when (donateToSignalType) {
|
||||
DonateToSignalType.ONE_TIME -> areFieldsEnabled && oneTimeDonationState.isSelectionValid && InAppDonations.hasAtLeastOnePaymentMethodAvailable()
|
||||
DonateToSignalType.MONTHLY -> areFieldsEnabled && monthlyDonationState.isSelectionValid && InAppDonations.hasAtLeastOnePaymentMethodAvailable()
|
||||
DonateToSignalType.GIFT -> error("This flow does not support gifts")
|
||||
}
|
||||
|
||||
val canContinue: Boolean
|
||||
get() = when (donateToSignalType) {
|
||||
DonateToSignalType.ONE_TIME -> continueEnabled && !oneTimeDonationState.isOneTimeDonationPending
|
||||
DonateToSignalType.MONTHLY -> continueEnabled && !monthlyDonationState.isSubscriptionActive
|
||||
DonateToSignalType.GIFT -> error("This flow does not support gifts")
|
||||
}
|
||||
|
||||
val canUpdate: Boolean
|
||||
get() = when (donateToSignalType) {
|
||||
DonateToSignalType.ONE_TIME -> false
|
||||
@@ -85,7 +94,8 @@ data class DonateToSignalState(
|
||||
val isCustomAmountFocused: Boolean = false,
|
||||
val donationStage: DonationStage = DonationStage.INIT,
|
||||
val selectableCurrencyCodes: List<String> = emptyList(),
|
||||
val isOneTimeDonationPending: Boolean = SignalStore.donationsValues().getPendingOneTimeDonation() != null,
|
||||
val isOneTimeDonationPending: Boolean = SignalStore.donationsValues().getPendingOneTimeDonation().isPending(),
|
||||
val isOneTimeDonationLongRunning: Boolean = SignalStore.donationsValues().getPendingOneTimeDonation().isLongRunning(),
|
||||
private val minimumDonationAmounts: Map<Currency, FiatMoney> = emptyMap()
|
||||
) {
|
||||
val minimumDonationAmountOfSelectedCurrency: FiatMoney = minimumDonationAmounts[selectedCurrency] ?: FiatMoney(BigDecimal.ZERO, selectedCurrency)
|
||||
|
||||
@@ -13,12 +13,12 @@ import org.signal.core.util.StringUtil
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.core.util.money.FiatMoney
|
||||
import org.signal.core.util.money.PlatformCurrencyUtil
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationSerializationHelper.isExpired
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.MonthlyDonationRepository
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.OneTimeDonationRepository
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.boost.Boost
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.donate.gateway.GatewayRequest
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.manage.DonationRedemptionJobWatcher
|
||||
import org.thoughtcrime.securesms.database.model.isExpired
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.jobmanager.JobTracker
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
|
||||
@@ -15,14 +15,13 @@ object DonationPillToggle {
|
||||
}
|
||||
|
||||
class Model(
|
||||
val isEnabled: Boolean,
|
||||
val selected: DonateToSignalType,
|
||||
val onClick: () -> Unit
|
||||
) : MappingModel<Model> {
|
||||
override fun areItemsTheSame(newItem: Model): Boolean = true
|
||||
|
||||
override fun areContentsTheSame(newItem: Model): Boolean {
|
||||
return isEnabled == newItem.isEnabled && selected == newItem.selected
|
||||
return selected == newItem.selected
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.content.ContentValues
|
||||
import android.database.Cursor
|
||||
import androidx.core.content.contentValuesOf
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase
|
||||
import net.zetetic.database.sqlcipher.SQLiteOpenHelper
|
||||
import org.signal.core.util.CursorUtil
|
||||
@@ -408,6 +409,11 @@ class JobDatabase(
|
||||
}
|
||||
}
|
||||
|
||||
/** Should only be used for debugging! */
|
||||
fun debugResetBackoffInterval() {
|
||||
writableDatabase.update(Jobs.TABLE_NAME, contentValuesOf(Jobs.NEXT_BACKOFF_INTERVAL to 0), null, null)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = Log.tag(JobDatabase::class.java)
|
||||
private const val DATABASE_VERSION = 2
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
package org.thoughtcrime.securesms.database.model
|
||||
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.PendingOneTimeDonation
|
||||
import org.whispersystems.signalservice.internal.push.BodyRange
|
||||
import kotlin.time.Duration.Companion.days
|
||||
|
||||
/**
|
||||
* Collection of extensions to make working with database protos cleaner.
|
||||
@@ -52,3 +54,25 @@ fun List<BodyRange>?.toBodyRangeList(): BodyRangeList? {
|
||||
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
fun PendingOneTimeDonation?.isPending(): Boolean {
|
||||
return this != null && this.error == null && !this.isExpired
|
||||
}
|
||||
|
||||
fun PendingOneTimeDonation?.isLongRunning(): Boolean {
|
||||
return isPending() && this!!.paymentMethodType == PendingOneTimeDonation.PaymentMethodType.SEPA_DEBIT
|
||||
}
|
||||
|
||||
val PendingOneTimeDonation.isExpired: Boolean
|
||||
get() {
|
||||
val pendingOneTimeBankTransferTimeout = 14.days
|
||||
val pendingOneTimeNormalTimeout = 1.days
|
||||
|
||||
val timeout = if (paymentMethodType == PendingOneTimeDonation.PaymentMethodType.SEPA_DEBIT) {
|
||||
pendingOneTimeBankTransferTimeout
|
||||
} else {
|
||||
pendingOneTimeNormalTimeout
|
||||
}
|
||||
|
||||
return (timestamp + timeout.inWholeMilliseconds) < System.currentTimeMillis()
|
||||
}
|
||||
|
||||
@@ -14,12 +14,12 @@ import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialRequestContext
|
||||
import org.signal.libsignal.zkgroup.receipts.ReceiptSerial
|
||||
import org.thoughtcrime.securesms.badges.Badges
|
||||
import org.thoughtcrime.securesms.badges.models.Badge
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationSerializationHelper.isExpired
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.donate.stripe.Stripe3DSData
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.BadgeList
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.DonationErrorValue
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.PendingOneTimeDonation
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.TerminalDonationQueue
|
||||
import org.thoughtcrime.securesms.database.model.isExpired
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.jobs.SubscriptionReceiptRequestResponseJob
|
||||
import org.thoughtcrime.securesms.payments.currency.CurrencyUtil
|
||||
|
||||
@@ -5823,6 +5823,17 @@
|
||||
<string name="DonateToSignalFragment__continue">Continue</string>
|
||||
<!-- Description below title -->
|
||||
<string name="DonateToSignalFragment__private_messaging">Private messaging, funded by you. No ads, no tracking, no compromise. Donate now to support Signal.</string>
|
||||
<!-- Dialog title when a user tries to donate while they already have a pending donation. -->
|
||||
<string name="DonateToSignalFragment__you_have_a_donation_pending">You have a donation pending</string>
|
||||
<!-- Dialog body when a user tries to donate while they already have a pending monthly donation. -->
|
||||
<string name="DonateToSignalFragment__bank_transfers_usually_take_1_business_day_to_process_monthly">Bank transfers usually take 1 business day to process. Please wait until this payment completes before updating your subscription.</string>
|
||||
<!-- Dialog body when a user tries to donate while they already have a pending one time donation. -->
|
||||
<string name="DonateToSignalFragment__bank_transfers_usually_take_1_business_day_to_process_onetime">Bank transfers usually take 1 business day to process. Please wait until this payment completes before making another donation.</string>
|
||||
<!-- Dialog body when a user tries to donate while they already have a pending monthly donation. -->
|
||||
<string name="DonateToSignalFragment__your_payment_is_still_being_processed_monthly">Your payment is still being processed. This can take a few minutes depending on your connection. Please wait until this payment completes before updating your subscription.</string>
|
||||
<!-- Dialog body when a user tries to donate while they already have a pending one time donation. -->
|
||||
<string name="DonateToSignalFragment__your_payment_is_still_being_processed_onetime">Your payment is still being processed. This can take a few minutes depending on your connection. Please wait until this payment completes before making another donation.</string>
|
||||
|
||||
<!-- Donation pill toggle monthly text -->
|
||||
<string name="DonationPillToggle__monthly">Monthly</string>
|
||||
<!-- Donation pill toggle one-time text -->
|
||||
|
||||
Reference in New Issue
Block a user