From 0b678b61833a8422fef8cacae5e7b813826b9d63 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Tue, 25 Nov 2025 15:32:32 +1100 Subject: [PATCH] Added a new field to detect if a refund is in progress --- .../securesms/debugmenu/DebugMenuViewModel.kt | 3 +- .../prosettings/ProSettingsHomeScreen.kt | 33 ++++++++++++++++--- .../securesms/pro/ProDataMapper.kt | 9 +++-- .../thoughtcrime/securesms/pro/ProStatus.kt | 6 +++- .../securesms/pro/ProStatusManager.kt | 24 ++++++++++---- .../securesms/pro/api/GetProDetails.kt | 9 +++-- .../thoughtcrime/securesms/ui/Components.kt | 5 ++- 7 files changed, 70 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenuViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenuViewModel.kt index 82b991b2c4..9f2bf9091f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenuViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenuViewModel.kt @@ -53,7 +53,6 @@ import org.thoughtcrime.securesms.ui.UINavigator import org.thoughtcrime.securesms.util.ClearDataUtils import org.thoughtcrime.securesms.util.DateUtils import java.time.ZonedDateTime -import kotlin.time.Instant @HiltViewModel(assistedFactory = DebugMenuViewModel.Factory::class) @@ -115,6 +114,7 @@ class DebugMenuViewModel @AssistedInject constructor( DebugSubscriptionStatus.EXPIRED, DebugSubscriptionStatus.EXPIRED_EARLIER, DebugSubscriptionStatus.EXPIRED_APPLE, + DebugSubscriptionStatus.AUTO_APPLE_REFUNDING, ), selectedDebugSubscriptionStatus = textSecurePreferences.getDebugSubscriptionType() ?: DebugSubscriptionStatus.AUTO_GOOGLE, debugProPlanStatus = setOf( @@ -656,6 +656,7 @@ class DebugMenuViewModel @AssistedInject constructor( enum class DebugSubscriptionStatus(val label: String) { AUTO_GOOGLE("Auto Renewing (Google, 3 months)"), + AUTO_APPLE_REFUNDING("Refunding (Apple, 3 months)"), EXPIRING_GOOGLE("Expiring/Cancelled (Expires in 14 days, Google, 12 months)"), EXPIRING_GOOGLE_LATER("Expiring/Cancelled (Expires in 40 days, Google, 12 months)"), AUTO_APPLE("Auto Renewing (Apple, 1 months)"), diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/prosettings/ProSettingsHomeScreen.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/prosettings/ProSettingsHomeScreen.kt index 0a5e6dbf62..bdea2b9621 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/prosettings/ProSettingsHomeScreen.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/prosettings/ProSettingsHomeScreen.kt @@ -56,6 +56,7 @@ import org.session.libsession.utilities.NonTranslatableStringConstants import org.session.libsession.utilities.StringSubstitutionConstants.APP_NAME_KEY import org.session.libsession.utilities.StringSubstitutionConstants.APP_PRO_KEY import org.session.libsession.utilities.StringSubstitutionConstants.ICON_KEY +import org.session.libsession.utilities.StringSubstitutionConstants.PLATFORM_KEY import org.session.libsession.utilities.StringSubstitutionConstants.PRO_KEY import org.thoughtcrime.securesms.preferences.prosettings.ProSettingsViewModel.Commands.* import org.thoughtcrime.securesms.pro.ProDataState @@ -272,6 +273,7 @@ fun ProSettingsHome( Spacer(Modifier.height(LocalDimensions.current.smallSpacing)) ProSettings( showProBadge = data.proDataState.showProBadge, + proStatus = data.proDataState.type, subscriptionRefreshState = data.proDataState.refreshState, inSheet = inSheet, expiry = data.subscriptionExpiryLabel, @@ -518,6 +520,7 @@ fun ProStatItem( fun ProSettings( modifier: Modifier = Modifier, showProBadge: Boolean, + proStatus: ProStatus.Active, subscriptionRefreshState: State, inSheet: Boolean, expiry: CharSequence, @@ -529,6 +532,8 @@ fun ProSettings( .put(PRO_KEY, NonTranslatableStringConstants.PRO) .format().toString(), ) { + val refunding = proStatus.refundInProgress + // Cell content Column( modifier = Modifier.fillMaxWidth(), @@ -544,6 +549,7 @@ fun ProSettings( ) } + val (subtitle, subColor, icon) = when(subscriptionRefreshState){ is State.Loading -> Triple Unit>( Phrase.from(LocalContext.current, R.string.proAccessLoadingEllipsis) @@ -560,20 +566,37 @@ fun ProSettings( LocalColors.current.warning, chevronIcon ) - is State.Success<*> -> Triple Unit>( - expiry, - LocalColors.current.text, chevronIcon - ) + is State.Success<*> -> { + Triple Unit>( + if(refunding) Phrase.from(LocalContext.current, R.string.processingRefundRequest) + .put(PLATFORM_KEY, proStatus.providerData.platform) + .format().toString() + else expiry, + LocalColors.current.text, + if(refunding){{ + Icon( + modifier = Modifier.align(Alignment.Center) + .size(LocalDimensions.current.iconMedium) + .qaTag(R.string.qa_action_item_icon), + painter = painterResource(id = R.drawable.ic_circle_warning_custom), + contentDescription = null, + tint = LocalColors.current.text + ) + }} else chevronIcon + ) + } } ActionRowItem( - title = annotatedStringResource( + title = if(refunding) annotatedStringResource(R.string.proRequestedRefund) + else annotatedStringResource( Phrase.from(LocalContext.current, R.string.updateAccess) .put(PRO_KEY, NonTranslatableStringConstants.PRO) .format().toString() ), subtitle = annotatedStringResource(subtitle), subtitleColor = subColor, + enabled = !refunding, endContent = { Box( modifier = Modifier.size(LocalDimensions.current.itemButtonIconSpacing) diff --git a/app/src/main/java/org/thoughtcrime/securesms/pro/ProDataMapper.kt b/app/src/main/java/org/thoughtcrime/securesms/pro/ProDataMapper.kt index a802045349..873d0cd6a0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/pro/ProDataMapper.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/pro/ProDataMapper.kt @@ -23,14 +23,16 @@ fun ProDetails.toProStatus(): ProStatus { validUntil = expiry!!, duration = paymentItem.planDuration.toSubscriptionDuration(), providerData = paymentItem.paymentProvider.getMetadata(), - quickRefundExpiry = paymentItem.platformExpiry + quickRefundExpiry = paymentItem.platformExpiry, + refundInProgress = refundRequestedAtMs > 0 ) } else { ProStatus.Active.Expiring( validUntil = expiry!!, duration = paymentItem.planDuration.toSubscriptionDuration(), providerData = paymentItem.paymentProvider.getMetadata(), - quickRefundExpiry = paymentItem.platformExpiry + quickRefundExpiry = paymentItem.platformExpiry, + refundInProgress = refundRequestedAtMs > 0 ) } } @@ -94,7 +96,8 @@ val previewAutoRenewingApple = ProStatus.Active.AutoRenewing( validUntil = Instant.now() + Duration.ofDays(14), duration = ProSubscriptionDuration.THREE_MONTHS, providerData = previewAppleMetaData, - quickRefundExpiry = Instant.now() + Duration.ofDays(14) + quickRefundExpiry = Instant.now() + Duration.ofDays(14), + refundInProgress = false ) val previewExpiredApple = ProStatus.Expired( diff --git a/app/src/main/java/org/thoughtcrime/securesms/pro/ProStatus.kt b/app/src/main/java/org/thoughtcrime/securesms/pro/ProStatus.kt index 5060e5e96a..3f0d4aee49 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/pro/ProStatus.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/pro/ProStatus.kt @@ -13,12 +13,14 @@ sealed interface ProStatus{ val duration: ProSubscriptionDuration val providerData: PaymentProviderMetadata val quickRefundExpiry: Instant? + val refundInProgress: Boolean data class AutoRenewing( override val validUntil: Instant, override val duration: ProSubscriptionDuration, override val providerData: PaymentProviderMetadata, - override val quickRefundExpiry: Instant? + override val quickRefundExpiry: Instant?, + override val refundInProgress: Boolean ): Active data class Expiring( @@ -26,6 +28,8 @@ sealed interface ProStatus{ override val duration: ProSubscriptionDuration, override val providerData: PaymentProviderMetadata, override val quickRefundExpiry: Instant? + , + override val refundInProgress: Boolean ): Active fun isWithinQuickRefundWindow(): Boolean { diff --git a/app/src/main/java/org/thoughtcrime/securesms/pro/ProStatusManager.kt b/app/src/main/java/org/thoughtcrime/securesms/pro/ProStatusManager.kt index 0763e988d3..a918c8b086 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/pro/ProStatusManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/pro/ProStatusManager.kt @@ -1,7 +1,6 @@ package org.thoughtcrime.securesms.pro import android.app.Application -import androidx.core.content.contentValuesOf import dagger.Lazy import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope @@ -125,35 +124,48 @@ class ProStatusManager @Inject constructor( validUntil = Instant.now() + Duration.ofDays(14), duration = ProSubscriptionDuration.THREE_MONTHS, providerData = BackendRequests.getPaymentProviderMetadata(PAYMENT_PROVIDER_GOOGLE_PLAY)!!, - quickRefundExpiry = Instant.now() + Duration.ofDays(7) + quickRefundExpiry = Instant.now() + Duration.ofDays(7), + refundInProgress = false + ) + + DebugMenuViewModel.DebugSubscriptionStatus.AUTO_APPLE_REFUNDING -> ProStatus.Active.AutoRenewing( + validUntil = Instant.now() + Duration.ofDays(14), + duration = ProSubscriptionDuration.THREE_MONTHS, + providerData = BackendRequests.getPaymentProviderMetadata(PAYMENT_PROVIDER_APP_STORE)!!, + quickRefundExpiry = Instant.now() + Duration.ofDays(7), + refundInProgress = true ) DebugMenuViewModel.DebugSubscriptionStatus.EXPIRING_GOOGLE -> ProStatus.Active.Expiring( validUntil = Instant.now() + Duration.ofDays(2), duration = ProSubscriptionDuration.TWELVE_MONTHS, providerData = BackendRequests.getPaymentProviderMetadata(PAYMENT_PROVIDER_GOOGLE_PLAY)!!, - quickRefundExpiry = Instant.now() + Duration.ofDays(7) + quickRefundExpiry = Instant.now() + Duration.ofDays(7), + refundInProgress = false ) DebugMenuViewModel.DebugSubscriptionStatus.EXPIRING_GOOGLE_LATER -> ProStatus.Active.Expiring( validUntil = Instant.now() + Duration.ofDays(40), duration = ProSubscriptionDuration.TWELVE_MONTHS, providerData = BackendRequests.getPaymentProviderMetadata(PAYMENT_PROVIDER_GOOGLE_PLAY)!!, - quickRefundExpiry = Instant.now() + Duration.ofDays(7) + quickRefundExpiry = Instant.now() + Duration.ofDays(7), + refundInProgress = false ) DebugMenuViewModel.DebugSubscriptionStatus.AUTO_APPLE -> ProStatus.Active.AutoRenewing( validUntil = Instant.now() + Duration.ofDays(14), duration = ProSubscriptionDuration.ONE_MONTH, providerData = BackendRequests.getPaymentProviderMetadata(PAYMENT_PROVIDER_APP_STORE)!!, - quickRefundExpiry = Instant.now() + Duration.ofDays(7) + quickRefundExpiry = Instant.now() + Duration.ofDays(7), + refundInProgress = false ) DebugMenuViewModel.DebugSubscriptionStatus.EXPIRING_APPLE -> ProStatus.Active.Expiring( validUntil = Instant.now() + Duration.ofDays(2), duration = ProSubscriptionDuration.ONE_MONTH, providerData = BackendRequests.getPaymentProviderMetadata(PAYMENT_PROVIDER_APP_STORE)!!, - quickRefundExpiry = Instant.now() + Duration.ofDays(7) + quickRefundExpiry = Instant.now() + Duration.ofDays(7), + refundInProgress = false ) DebugMenuViewModel.DebugSubscriptionStatus.EXPIRED -> ProStatus.Expired( diff --git a/app/src/main/java/org/thoughtcrime/securesms/pro/api/GetProDetails.kt b/app/src/main/java/org/thoughtcrime/securesms/pro/api/GetProDetails.kt index df386213dc..d8a117399c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/pro/api/GetProDetails.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/pro/api/GetProDetails.kt @@ -53,7 +53,7 @@ class ProDetails( @Serializable(with = InstantAsMillisSerializer::class) val expiry: Instant? = null, - @SerialName("grace_duration_ms") + @SerialName("grace_period_duration_ms") val graceDurationMs: Long? = null, @SerialName("error_report") @@ -65,6 +65,11 @@ class ProDetails( @SerialName("items") val paymentItems: List = emptyList(), + @SerialName("refund_requested_unix_ts_ms") + val refundRequestedAtMs: Int = 0, + + + val version: Int, ) { init { @@ -86,7 +91,7 @@ class ProDetails( @Serializable(with = InstantAsMillisSerializer::class) val expiry: Instant? = null, - @SerialName("grace_duration_ms") + @SerialName("grace_period_duration_ms") val graceDurationMs: Long? = null, @SerialName("platform_refund_expiry_unix_ts_ms") diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/Components.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/Components.kt index ebab217eef..31775fd2d3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/Components.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/Components.kt @@ -1335,6 +1335,7 @@ fun ActionRowItem( onClick: () -> Unit, @StringRes qaTag: Int, modifier: Modifier = Modifier, + enabled: Boolean = true, subtitle: AnnotatedString? = null, titleColor: Color = LocalColors.current.text, subtitleColor: Color = LocalColors.current.text, @@ -1347,7 +1348,9 @@ fun ActionRowItem( Row( modifier = modifier .heightIn(min = minHeight) - .clickable { onClick() } + .then( + if (enabled) Modifier.clickable { onClick() } else Modifier + ) .padding(paddingValues) .qaTag(qaTag), verticalAlignment = Alignment.CenterVertically