Add whoami check for receipt_credentials.

This commit is contained in:
Alex Hart
2025-01-09 10:04:15 -04:00
committed by Greyson Parrelli
parent 0dbab7ede0
commit 23f90e070e
16 changed files with 300 additions and 69 deletions

View File

@@ -231,6 +231,22 @@ private fun AppSettingsContent(
}
}
BackupFailureState.ALREADY_REDEEMED -> {
item {
Dividers.Default()
BackupsWarningRow(
text = stringResource(R.string.AppSettingsFragment__couldnt_redeem_your_backups_subscription),
onClick = {
BackupRepository.markBackupAlreadyRedeemedIndicatorClicked()
callbacks.navigate(R.id.action_appSettingsFragment_to_remoteBackupsSettingsFragment)
}
)
Dividers.Default()
}
}
BackupFailureState.NONE -> Unit
}

View File

@@ -77,6 +77,8 @@ class AppSettingsViewModel : ViewModel() {
BackupFailureState.COULD_NOT_COMPLETE_BACKUP
} else if (SignalStore.backup.subscriptionStateMismatchDetected) {
BackupFailureState.SUBSCRIPTION_STATE_MISMATCH
} else if (SignalStore.backup.hasBackupAlreadyRedeemedError) {
BackupFailureState.ALREADY_REDEEMED
} else {
BackupFailureState.NONE
}

View File

@@ -12,5 +12,6 @@ enum class BackupFailureState {
NONE,
BACKUP_FAILED,
COULD_NOT_COMPLETE_BACKUP,
SUBSCRIPTION_STATE_MISMATCH
SUBSCRIPTION_STATE_MISMATCH,
ALREADY_REDEEMED
}

View File

@@ -36,6 +36,7 @@ import androidx.compose.material3.BasicAlertDialog
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SnackbarHostState
@@ -153,7 +154,8 @@ class RemoteBackupsSettingsFragment : ComposeFragment() {
backupProgress = backupProgress,
backupSize = state.backupSize,
backupState = state.backupState,
backupRestoreState = restoreState
backupRestoreState = restoreState,
hasRedemptionError = state.hasRedemptionError
)
}
@@ -248,6 +250,10 @@ class RemoteBackupsSettingsFragment : ComposeFragment() {
override fun onRestoreUsingCellularClick(canUseCellular: Boolean) {
viewModel.setCanRestoreUsingCellular(canUseCellular)
}
override fun onRedemptionErrorDetailsClick() {
BackupAlertBottomSheet.create(BackupAlert.CouldNotRedeemBackup).show(parentFragmentManager, null)
}
}
private fun displayBackupKey() {
@@ -331,6 +337,7 @@ private interface ContentCallbacks {
fun onContactSupport() = Unit
fun onLearnMoreAboutBackupFailure() = Unit
fun onRestoreUsingCellularClick(canUseCellular: Boolean) = Unit
fun onRedemptionErrorDetailsClick() = Unit
}
@Composable
@@ -346,7 +353,8 @@ private fun RemoteBackupsSettingsContent(
requestedSnackbar: RemoteBackupsSettingsState.Snackbar,
contentCallbacks: ContentCallbacks,
backupProgress: ArchiveUploadProgressState?,
backupSize: Long
backupSize: Long,
hasRedemptionError: Boolean
) {
val snackbarHostState = remember {
SnackbarHostState()
@@ -364,6 +372,12 @@ private fun RemoteBackupsSettingsContent(
modifier = Modifier
.padding(it)
) {
if (hasRedemptionError) {
item {
RedemptionErrorAlert(onDetailsClick = contentCallbacks::onRedemptionErrorDetailsClick)
}
}
item {
AnimatedContent(backupState, label = "backup-state-block") { state ->
when (state) {
@@ -771,6 +785,42 @@ private fun BackupCard(
}
}
@Composable
private fun RedemptionErrorAlert(
onDetailsClick: () -> Unit
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(horizontal = 16.dp)
.padding(top = 8.dp, bottom = 4.dp)
.border(
width = 1.dp,
color = colorResource(R.color.signal_colorOutline_38),
shape = RoundedCornerShape(12.dp)
)
.padding(vertical = 16.dp)
.padding(start = 16.dp, end = 12.dp)
) {
Icon(
painter = painterResource(R.drawable.symbol_backup_error_24),
tint = Color(0xFFFF9500),
contentDescription = null
)
Text(
text = stringResource(R.string.AppSettingsFragment__couldnt_redeem_your_backups_subscription),
modifier = Modifier.padding(start = 16.dp, end = 4.dp).weight(1f)
)
Buttons.Small(onClick = onDetailsClick) {
Text(
text = stringResource(R.string.RemoteBackupsSettingsFragment__details)
)
}
}
}
@Composable
private fun BoxCard(content: @Composable () -> Unit) {
Box(
@@ -988,6 +1038,7 @@ private fun getBackupPhaseMessage(state: ArchiveUploadProgressState): String {
(progress.progress * 100).toInt()
)
}
else -> stringResource(R.string.RemoteBackupsSettingsFragment__preparing_backup)
}
}
@@ -1272,11 +1323,20 @@ private fun RemoteBackupsSettingsContentPreview() {
backupState = RemoteBackupsSettingsState.BackupState.ActiveFree(
messageBackupsType = MessageBackupsType.Free(mediaRetentionDays = 30)
),
backupRestoreState = BackupRestoreState.FromBackupStatusData(BackupStatusData.CouldNotCompleteBackup)
backupRestoreState = BackupRestoreState.FromBackupStatusData(BackupStatusData.CouldNotCompleteBackup),
hasRedemptionError = true
)
}
}
@SignalPreview
@Composable
private fun RedemptionErrorAlertPreview() {
Previews.Preview {
RedemptionErrorAlert { }
}
}
@SignalPreview
@Composable
private fun LoadingCardPreview() {

View File

@@ -15,6 +15,7 @@ data class RemoteBackupsSettingsState(
val backupsEnabled: Boolean,
val canBackUpUsingCellular: Boolean = false,
val canRestoreUsingCellular: Boolean = false,
val hasRedemptionError: Boolean = false,
val backupState: BackupState = BackupState.Loading,
val backupSize: Long = 0,
val backupsFrequency: BackupFrequency = BackupFrequency.DAILY,

View File

@@ -240,6 +240,7 @@ class RemoteBackupsSettingsViewModel : ViewModel() {
Log.d(TAG, "Subscription found. Updating UI state with subscription details.")
_state.update {
it.copy(
hasRedemptionError = lastPurchase?.data?.error?.data_ == "409",
backupState = when {
subscription.isActive -> RemoteBackupsSettingsState.BackupState.ActivePaid(
messageBackupsType = type,

View File

@@ -23,18 +23,17 @@ import org.thoughtcrime.securesms.components.settings.app.subscription.thanks.Th
import org.thoughtcrime.securesms.components.settings.app.subscription.thanks.ThanksForYourSupportBottomSheetDialogFragmentArgs
import org.thoughtcrime.securesms.database.InAppPaymentTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord
import org.thoughtcrime.securesms.database.model.databaseprotos.DonationErrorValue
import org.thoughtcrime.securesms.database.model.databaseprotos.InAppPaymentData
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.RemoteConfig
/**
* Handles displaying bottom sheets for in-app payments. The current policy is to "fire and forget".
*/
class InAppPaymentsBottomSheetDelegate(
private val fragmentManager: FragmentManager,
private val lifecycleOwner: LifecycleOwner,
private vararg val supportedTypes: InAppPaymentSubscriberRecord.Type = arrayOf(InAppPaymentSubscriberRecord.Type.DONATION)
private val lifecycleOwner: LifecycleOwner
) : DefaultLifecycleObserver {
companion object {
@@ -56,13 +55,11 @@ class InAppPaymentsBottomSheetDelegate(
private val badgeRepository = TerminalDonationRepository()
override fun onResume(owner: LifecycleOwner) {
if (InAppPaymentSubscriberRecord.Type.DONATION in supportedTypes) {
handleLegacyTerminalDonationSheets()
handleLegacyVerifiedMonthlyDonationSheets()
handleInAppPaymentDonationSheets()
}
handleLegacyTerminalDonationSheets()
handleLegacyVerifiedMonthlyDonationSheets()
handleInAppPaymentDonationSheets()
if (InAppPaymentSubscriberRecord.Type.BACKUP in supportedTypes) {
if (RemoteConfig.messageBackups) {
handleInAppPaymentBackupsSheets()
}
}