mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-25 11:20:47 +01:00
Display thanks sheet if we resume activity before iDEAL is redeemed.
This commit is contained in:
@@ -75,11 +75,7 @@ class InAppPaymentsBottomSheetDelegate(
|
||||
TerminalDonationBottomSheet.show(fragmentManager, donation)
|
||||
} else if (donation.error != null) {
|
||||
lifecycleDisposable += badgeRepository.getBadge(donation).observeOn(AndroidSchedulers.mainThread()).subscribe { badge ->
|
||||
val args = ThanksForYourSupportBottomSheetDialogFragmentArgs.Builder(badge).build().toBundle()
|
||||
val sheet = ThanksForYourSupportBottomSheetDialogFragment()
|
||||
|
||||
sheet.arguments = args
|
||||
sheet.show(fragmentManager, null)
|
||||
ThanksForYourSupportBottomSheetDialogFragment.create(badge).show(fragmentManager, ThanksForYourSupportBottomSheetDialogFragment.SHEET_TAG)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@ object ExternalNavigationHelper {
|
||||
|
||||
private val TAG = Log.tag(ExternalNavigationHelper::class)
|
||||
|
||||
fun maybeLaunchExternalNavigationIntent(context: Context, webRequestUri: Uri?, launchIntent: (Intent) -> Unit): Boolean {
|
||||
fun maybeLaunchExternalNavigationIntent(context: Context, webRequestUri: Uri?, force: Boolean = false, launchIntent: (Intent) -> Unit): Boolean {
|
||||
val url = webRequestUri ?: return false
|
||||
if (url.scheme?.startsWith("http") == true || url.scheme == StripeApi.RETURN_URL_SCHEME) {
|
||||
if (!force && (url.scheme?.startsWith("http") == true || url.scheme == StripeApi.RETURN_URL_SCHEME)) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,8 @@ class Stripe3DSDialogFragment : DialogFragment(R.layout.donation_webview_fragmen
|
||||
setOnClickListener {
|
||||
ExternalNavigationHelper.maybeLaunchExternalNavigationIntent(
|
||||
context,
|
||||
args.uri
|
||||
args.uri,
|
||||
force = true
|
||||
) {
|
||||
handleLaunchExternal(it)
|
||||
}
|
||||
@@ -125,7 +126,11 @@ class Stripe3DSDialogFragment : DialogFragment(R.layout.donation_webview_fragmen
|
||||
private inner class Stripe3DSWebClient : WebViewClient() {
|
||||
|
||||
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
|
||||
return ExternalNavigationHelper.maybeLaunchExternalNavigationIntent(requireContext(), request?.url, this@Stripe3DSDialogFragment::handleLaunchExternal)
|
||||
return ExternalNavigationHelper.maybeLaunchExternalNavigationIntent(
|
||||
context = requireContext(),
|
||||
webRequestUri = request?.url,
|
||||
launchIntent = this@Stripe3DSDialogFragment::handleLaunchExternal
|
||||
)
|
||||
}
|
||||
|
||||
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
|
||||
|
||||
@@ -7,9 +7,14 @@ import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import org.signal.core.util.dp
|
||||
import org.signal.core.util.money.FiatMoney
|
||||
import org.signal.donations.InAppPaymentType
|
||||
@@ -25,6 +30,7 @@ import org.thoughtcrime.securesms.components.settings.app.subscription.DonationS
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.completed.InAppPaymentsBottomSheetDelegate
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.donate.CheckoutFlowActivity
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.models.NetworkFailure
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.thanks.ThanksForYourSupportBottomSheetDialogFragment
|
||||
import org.thoughtcrime.securesms.components.settings.configure
|
||||
import org.thoughtcrime.securesms.components.settings.models.IndeterminateLoadingCircle
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.DonationErrorValue
|
||||
@@ -82,6 +88,14 @@ class ManageDonationsFragment :
|
||||
if (savedInstanceState == null && args.directToCheckoutType != InAppPaymentType.UNKNOWN) {
|
||||
launcher.launch(args.directToCheckoutType)
|
||||
}
|
||||
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||
viewModel.displayThanksBottomSheetPulse.collectLatest {
|
||||
ThanksForYourSupportBottomSheetDialogFragment.create(it).show(parentFragmentManager, ThanksForYourSupportBottomSheetDialogFragment.SHEET_TAG)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.components.settings.app.subscription.manage
|
||||
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.channels.trySendBlocking
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import org.thoughtcrime.securesms.database.DatabaseObserver.InAppPaymentObserver
|
||||
import org.thoughtcrime.securesms.database.InAppPaymentTable
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.InAppPaymentData
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
|
||||
object ManageDonationsRepository {
|
||||
/**
|
||||
* Emits any time we see a successfully completed IDEAL payment that we've not notified the user about.
|
||||
*/
|
||||
fun consumeSuccessfulIdealPayments(): Flow<InAppPaymentTable.InAppPayment> {
|
||||
return callbackFlow {
|
||||
val observer = InAppPaymentObserver {
|
||||
if (it.state == InAppPaymentTable.State.END &&
|
||||
it.data.error == null &&
|
||||
it.data.paymentMethodType == InAppPaymentData.PaymentMethodType.IDEAL &&
|
||||
!it.notified
|
||||
) {
|
||||
trySendBlocking(it)
|
||||
|
||||
SignalDatabase.inAppPayments.update(
|
||||
it.copy(notified = true)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
AppDependencies.databaseObserver.registerInAppPaymentObserver(observer)
|
||||
awaitClose { AppDependencies.databaseObserver.unregisterObserver(observer) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.components.settings.app.subscription.manage
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
@@ -9,9 +10,15 @@ import io.reactivex.rxjava3.disposables.Disposable
|
||||
import io.reactivex.rxjava3.kotlin.plusAssign
|
||||
import io.reactivex.rxjava3.kotlin.subscribeBy
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.core.util.orNull
|
||||
import org.signal.donations.InAppPaymentType
|
||||
import org.thoughtcrime.securesms.badges.Badges
|
||||
import org.thoughtcrime.securesms.badges.models.Badge
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaymentsRepository
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.RecurringInAppPaymentRepository
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
@@ -31,6 +38,9 @@ class ManageDonationsViewModel : ViewModel() {
|
||||
private val networkDisposable: Disposable
|
||||
|
||||
val state: LiveData<ManageDonationsState> = store.stateLiveData
|
||||
private val internalDisplayThanksBottomSheetPulse = MutableSharedFlow<Badge>()
|
||||
|
||||
val displayThanksBottomSheetPulse: SharedFlow<Badge> = internalDisplayThanksBottomSheetPulse
|
||||
|
||||
init {
|
||||
store.update(Recipient.self().live().liveDataResolved) { self, state ->
|
||||
@@ -45,6 +55,13 @@ class ManageDonationsViewModel : ViewModel() {
|
||||
retry()
|
||||
}
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
ManageDonationsRepository.consumeSuccessfulIdealPayments()
|
||||
.collectLatest {
|
||||
internalDisplayThanksBottomSheetPulse.emit(Badges.fromDatabaseBadge(it.data.badge!!))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.animation.AnimationCompleteListener
|
||||
import org.thoughtcrime.securesms.badges.BadgeImageView
|
||||
import org.thoughtcrime.securesms.badges.BadgeRepository
|
||||
import org.thoughtcrime.securesms.badges.models.Badge
|
||||
import org.thoughtcrime.securesms.components.FixedRoundedCornerBottomSheetDialogFragment
|
||||
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
@@ -30,6 +31,19 @@ import org.thoughtcrime.securesms.util.visible
|
||||
|
||||
class ThanksForYourSupportBottomSheetDialogFragment : FixedRoundedCornerBottomSheetDialogFragment() {
|
||||
|
||||
companion object {
|
||||
private val TAG = Log.tag(ThanksForYourSupportBottomSheetDialogFragment::class.java)
|
||||
const val SHEET_TAG = "ThanksForYourSupportBottomSheet"
|
||||
|
||||
fun create(badge: Badge): ThanksForYourSupportBottomSheetDialogFragment {
|
||||
val args = ThanksForYourSupportBottomSheetDialogFragmentArgs.Builder(badge).build().toBundle()
|
||||
val sheet = ThanksForYourSupportBottomSheetDialogFragment()
|
||||
|
||||
sheet.arguments = args
|
||||
return sheet
|
||||
}
|
||||
}
|
||||
|
||||
override val peekHeightPercentage: Float = 1f
|
||||
|
||||
private lateinit var switch: MaterialSwitch
|
||||
@@ -156,10 +170,6 @@ class ThanksForYourSupportBottomSheetDialogFragment : FixedRoundedCornerBottomSh
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = Log.tag(ThanksForYourSupportBottomSheetDialogFragment::class.java)
|
||||
}
|
||||
|
||||
interface Callback {
|
||||
fun onBoostThanksSheetDismissed()
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import org.thoughtcrime.securesms.components.settings.app.subscription.donate.st
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord
|
||||
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
|
||||
@@ -588,17 +587,6 @@ class InAppPaymentValues internal constructor(store: KeyValueStore) : SignalStor
|
||||
}
|
||||
}
|
||||
|
||||
fun setPendingOneTimeDonationError(error: DonationErrorValue) {
|
||||
synchronized(this) {
|
||||
val pendingOneTimeDonation = getPendingOneTimeDonation()
|
||||
if (pendingOneTimeDonation != null) {
|
||||
setPendingOneTimeDonation(pendingOneTimeDonation.newBuilder().error(error).build())
|
||||
} else {
|
||||
Log.w(TAG, "PendingOneTimeDonation was null, ignoring error.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun consumePending3DSData(): Stripe3DSData? {
|
||||
synchronized(this) {
|
||||
val data = getBlob(PENDING_3DS_DATA, null)?.let {
|
||||
|
||||
Reference in New Issue
Block a user