diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/DonationPaymentRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/DonationPaymentRepository.kt index ea71ce8418..b19ac4bdee 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/DonationPaymentRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/DonationPaymentRepository.kt @@ -6,6 +6,7 @@ import com.google.android.gms.wallet.PaymentData import io.reactivex.rxjava3.core.Completable import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.schedulers.Schedulers +import org.signal.core.util.logging.Log import org.signal.core.util.money.FiatMoney import org.signal.donations.GooglePayApi import org.signal.donations.GooglePayPaymentSource @@ -15,6 +16,7 @@ import org.thoughtcrime.securesms.jobs.BoostReceiptRequestResponseJob import org.thoughtcrime.securesms.jobs.SubscriptionReceiptRequestResponseJob import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.storage.StorageSyncHelper +import org.thoughtcrime.securesms.subscription.LevelUpdate import org.thoughtcrime.securesms.subscription.LevelUpdateOperation import org.thoughtcrime.securesms.subscription.Subscriber import org.thoughtcrime.securesms.util.Environment @@ -142,7 +144,8 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet levelUpdateOperation.idempotencyKey.serialize() ).flatMap(ServiceResponse::flattenResult).ignoreElement().andThen { SignalStore.donationsValues().clearUserManuallyCancelled() - SignalStore.donationsValues().clearLevelOperation(levelUpdateOperation) + SignalStore.donationsValues().clearLevelOperation() + LevelUpdate.updateProcessingState(false) it.onComplete() }.andThen { val jobId = SubscriptionReceiptRequestResponseJob.enqueueSubscriptionContinuation() @@ -164,6 +167,8 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet it.onError(DonationExceptions.TimedOutWaitingForTokenRedemption) } } + }.doOnError { + LevelUpdate.updateProcessingState(false) }.subscribeOn(Schedulers.io()) } @@ -176,8 +181,10 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet ) SignalStore.donationsValues().setLevelOperation(newOperation) + LevelUpdate.updateProcessingState(true) newOperation } else { + LevelUpdate.updateProcessingState(true) levelUpdateOperation } } @@ -206,4 +213,8 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet ApplicationDependencies.getDonationsService().setDefaultPaymentMethodId(it.subscriberId, paymentMethodId) }.flatMap(ServiceResponse::flattenResult).ignoreElement() } + + companion object { + private val TAG = Log.tag(DonationPaymentRepository::class.java) + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/boost/BoostFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/boost/BoostFragment.kt index fa3929d06f..1a97e2655a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/boost/BoostFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/boost/BoostFragment.kt @@ -193,6 +193,7 @@ class BoostFragment : DSLSettingsBottomSheetFragment( dialog.dismiss() findNavController().popBackStack() } + .show() } else { Log.w(TAG, "Error occurred while processing payment", throwable) MaterialAlertDialogBuilder(requireContext()) @@ -202,6 +203,7 @@ class BoostFragment : DSLSettingsBottomSheetFragment( dialog.dismiss() findNavController().popBackStack() } + .show() } } @@ -214,6 +216,7 @@ class BoostFragment : DSLSettingsBottomSheetFragment( dialog.dismiss() findNavController().popBackStack() } + .show() } private fun startAnimationAboveSelectedBoost(view: View) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/manage/ManageDonationsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/manage/ManageDonationsViewModel.kt index 66146c4fa1..08a48ec085 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/manage/ManageDonationsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/manage/ManageDonationsViewModel.kt @@ -14,9 +14,8 @@ 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.LevelUpdateOperation +import org.thoughtcrime.securesms.subscription.LevelUpdate import org.thoughtcrime.securesms.util.livedata.Store -import org.whispersystems.libsignal.util.guava.Optional import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription class ManageDonationsViewModel( @@ -43,11 +42,11 @@ class ManageDonationsViewModel( fun refresh() { disposables.clear() - val levelUpdateOperationEdges: Observable> = SignalStore.donationsValues().levelUpdateOperationObservable.distinctUntilChanged() + val levelUpdateOperationEdges: Observable = LevelUpdate.isProcessing.distinctUntilChanged() val activeSubscription: Single = subscriptionsRepository.getActiveSubscription() - disposables += levelUpdateOperationEdges.flatMapSingle { optionalKey -> - if (optionalKey.isPresent) { + disposables += levelUpdateOperationEdges.flatMapSingle { isProcessing -> + if (isProcessing) { Single.just(ManageDonationsState.TransactionState.InTransaction) } else { activeSubscription.map { ManageDonationsState.TransactionState.NotInTransaction(it) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/subscribe/SubscribeFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/subscribe/SubscribeFragment.kt index 74196d6179..1ef69820c6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/subscribe/SubscribeFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/subscribe/SubscribeFragment.kt @@ -241,6 +241,7 @@ class SubscribeFragment : DSLSettingsFragment( requireActivity().finish() requireActivity().startActivity(AppSettingsActivity.subscriptions(requireContext())) } + .show() } else { Log.w(TAG, "Error occurred while processing payment", throwable) MaterialAlertDialogBuilder(requireContext()) @@ -249,6 +250,7 @@ class SubscribeFragment : DSLSettingsFragment( .setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() } + .show() } } @@ -281,6 +283,7 @@ class SubscribeFragment : DSLSettingsFragment( dialog.dismiss() findNavController().popBackStack() } + .show() } companion object { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/subscribe/SubscribeViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/subscribe/SubscribeViewModel.kt index bc794b2992..a858366111 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/subscribe/SubscribeViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/subscribe/SubscribeViewModel.kt @@ -19,6 +19,7 @@ import org.thoughtcrime.securesms.components.settings.app.subscription.DonationP 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.subscription.LevelUpdate import org.thoughtcrime.securesms.subscription.Subscription import org.thoughtcrime.securesms.util.livedata.Store import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription @@ -51,10 +52,10 @@ class SubscribeViewModel( val allSubscriptions: Observable> = currency.switchMapSingle { subscriptionsRepository.getSubscriptions(it) } refreshActiveSubscription() - disposables += SignalStore.donationsValues().levelUpdateOperationObservable.subscribeBy { + disposables += LevelUpdate.isProcessing.subscribeBy { store.update { state -> state.copy( - hasInProgressSubscriptionTransaction = it.isPresent + hasInProgressSubscriptionTransaction = it ) } } @@ -113,6 +114,7 @@ class SubscribeViewModel( onComplete = { eventPublisher.onNext(DonationEvent.SubscriptionCancelled) SignalStore.donationsValues().setLastEndOfPeriod(0L) + SignalStore.donationsValues().clearLevelOperation() SignalStore.donationsValues().markUserManuallyCancelled() refreshActiveSubscription() store.update { it.copy(stage = SubscribeState.Stage.READY) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/DonationsValues.kt b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/DonationsValues.kt index c3fd93f2eb..b091e66ea7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/DonationsValues.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/DonationsValues.kt @@ -12,7 +12,6 @@ import org.thoughtcrime.securesms.payments.currency.CurrencyUtil import org.thoughtcrime.securesms.subscription.LevelUpdateOperation import org.thoughtcrime.securesms.subscription.Subscriber import org.thoughtcrime.securesms.util.TextSecurePreferences -import org.whispersystems.libsignal.util.guava.Optional import org.whispersystems.signalservice.api.subscriptions.IdempotencyKey import org.whispersystems.signalservice.api.subscriptions.SubscriberId import java.util.Currency @@ -45,9 +44,6 @@ internal class DonationsValues internal constructor(store: KeyValueStore) : Sign private val boostCurrencyPublisher: Subject by lazy { BehaviorSubject.createDefault(getBoostCurrency()) } val observableBoostCurrency: Observable by lazy { boostCurrencyPublisher } - private val levelUpdateOperationPublisher: Subject> by lazy { BehaviorSubject.createDefault(Optional.fromNullable(getLevelOperation())) } - val levelUpdateOperationObservable: Observable> by lazy { levelUpdateOperationPublisher } - fun getSubscriptionCurrency(): Currency { val currencyCode = getString(KEY_SUBSCRIPTION_CURRENCY_CODE, null) val currency: Currency? = if (currencyCode == null) { @@ -120,7 +116,7 @@ internal class DonationsValues internal constructor(store: KeyValueStore) : Sign fun getLevelOperation(): LevelUpdateOperation? { val level = getString(KEY_LEVEL, null) - val idempotencyKey = getIdempotencyKey() + val idempotencyKey = getBlob(KEY_IDEMPOTENCY, null)?.let { IdempotencyKey.fromBytes(it) } return if (level == null || idempotencyKey == null) { null @@ -130,19 +126,17 @@ internal class DonationsValues internal constructor(store: KeyValueStore) : Sign } fun setLevelOperation(levelUpdateOperation: LevelUpdateOperation) { - putString(KEY_LEVEL, levelUpdateOperation.level) - setIdempotencyKey(levelUpdateOperation.idempotencyKey) - dispatchLevelOperation() + store.beginWrite() + .putString(KEY_LEVEL, levelUpdateOperation.level) + .putBlob(KEY_IDEMPOTENCY, levelUpdateOperation.idempotencyKey.bytes) + .apply() } - fun clearLevelOperation(levelUpdateOperation: LevelUpdateOperation): Boolean { - val currentKey = getIdempotencyKey() - return if (currentKey == levelUpdateOperation.idempotencyKey) { - clearLevelOperation() - true - } else { - false - } + fun clearLevelOperation() { + store.beginWrite() + .remove(KEY_LEVEL) + .remove(KEY_IDEMPOTENCY) + .apply() } fun setExpiredBadge(badge: Badge?) { @@ -159,20 +153,6 @@ internal class DonationsValues internal constructor(store: KeyValueStore) : Sign return Badges.fromDatabaseBadge(BadgeList.Badge.parseFrom(badgeBytes)) } - private fun clearLevelOperation() { - remove(KEY_IDEMPOTENCY) - remove(KEY_LEVEL) - dispatchLevelOperation() - } - - private fun getIdempotencyKey(): IdempotencyKey? { - return getBlob(KEY_IDEMPOTENCY, null)?.let { IdempotencyKey.fromBytes(it) } - } - - private fun setIdempotencyKey(key: IdempotencyKey) { - putBlob(KEY_IDEMPOTENCY, key.bytes) - } - fun getLastKeepAliveLaunchTime(): Long { return getLong(KEY_LAST_KEEP_ALIVE_LAUNCH, 0L) } @@ -200,8 +180,4 @@ internal class DonationsValues internal constructor(store: KeyValueStore) : Sign fun clearUserManuallyCancelled() { remove(USER_MANUALLY_CANCELLED) } - - private fun dispatchLevelOperation() { - levelUpdateOperationPublisher.onNext(Optional.fromNullable(getLevelOperation())) - } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/subscription/LevelUpdate.kt b/app/src/main/java/org/thoughtcrime/securesms/subscription/LevelUpdate.kt new file mode 100644 index 0000000000..44ce1e6c93 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/subscription/LevelUpdate.kt @@ -0,0 +1,15 @@ +package org.thoughtcrime.securesms.subscription + +import io.reactivex.rxjava3.core.Observable +import io.reactivex.rxjava3.subjects.BehaviorSubject + +object LevelUpdate { + + private var isProcessingSubject = BehaviorSubject.createDefault(false) + + var isProcessing: Observable = isProcessingSubject + + fun updateProcessingState(isProcessing: Boolean) { + isProcessingSubject.onNext(isProcessing) + } +}