mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-25 04:06:14 +00:00
Always display paid tier but stick a dialog in front of it for non-GPS devices.
This commit is contained in:
@@ -1820,7 +1820,7 @@ object BackupRepository {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getAvailableBackupsTypes(availableBackupTiers: List<MessageBackupTier>): List<MessageBackupsType> {
|
||||
suspend fun getBackupTypes(availableBackupTiers: List<MessageBackupTier>): List<MessageBackupsType> {
|
||||
return availableBackupTiers.mapNotNull {
|
||||
val type = getBackupsType(it)
|
||||
|
||||
@@ -1868,9 +1868,20 @@ object BackupRepository {
|
||||
RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP).getOrNull()?.activeSubscription?.let {
|
||||
FiatMoney.fromSignalNetworkAmount(it.amount, Currency.getInstance(it.currency))
|
||||
}
|
||||
} else {
|
||||
} else if (AppDependencies.billingApi.isApiAvailable()) {
|
||||
Log.d(TAG, "Accessing price via billing api.")
|
||||
AppDependencies.billingApi.queryProduct()?.price
|
||||
} else {
|
||||
Log.d(TAG, "Billing API is not available on this device. Accessing price via subscription configuration.")
|
||||
val configurationResult = AppDependencies.donationsService.getDonationsConfiguration(Locale.getDefault()).toNetworkResult()
|
||||
val currency = Currency.getInstance(Locale.getDefault())
|
||||
|
||||
when (configurationResult) {
|
||||
is NetworkResult.Success -> configurationResult.result.currencies[currency.currencyCode.lowercase()]?.backupSubscription[SubscriptionsConfiguration.BACKUPS_LEVEL]?.let {
|
||||
FiatMoney(it, currency)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
if (productPrice == null) {
|
||||
|
||||
@@ -170,7 +170,7 @@ class MessageBackupsFlowFragment : ComposeFragment(), InAppPaymentCheckoutDelega
|
||||
stage = state.stage,
|
||||
currentBackupTier = state.currentMessageBackupTier,
|
||||
selectedBackupTier = state.selectedMessageBackupTier,
|
||||
availableBackupTypes = state.availableBackupTypes,
|
||||
allBackupTypes = state.allBackupTypes,
|
||||
isNextEnabled = state.isCheckoutButtonEnabled(),
|
||||
onMessageBackupsTierSelected = viewModel::onMessageBackupTierUpdated,
|
||||
onNavigationClick = viewModel::goToPreviousStage,
|
||||
@@ -180,7 +180,14 @@ class MessageBackupsFlowFragment : ComposeFragment(), InAppPaymentCheckoutDelega
|
||||
getString(R.string.backup_support_url)
|
||||
)
|
||||
},
|
||||
onNextClicked = viewModel::goToNextStage
|
||||
onNextClicked = viewModel::goToNextStage,
|
||||
isBillingApiAvailable = state.isBillingApiAvailable,
|
||||
onLearnMoreAboutWhyUserCanNotUpgrade = {
|
||||
CommunicationActions.openBrowserLink(
|
||||
requireContext(),
|
||||
getString(R.string.backup_support_url)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,8 @@ import org.whispersystems.signalservice.api.AccountEntropyPool
|
||||
data class MessageBackupsFlowState(
|
||||
val selectedMessageBackupTier: MessageBackupTier? = SignalStore.backup.backupTier,
|
||||
val currentMessageBackupTier: MessageBackupTier? = null,
|
||||
val availableBackupTypes: List<MessageBackupsType> = emptyList(),
|
||||
val allBackupTypes: List<MessageBackupsType> = emptyList(),
|
||||
val isBillingApiAvailable: Boolean = false,
|
||||
val inAppPayment: InAppPaymentTable.InAppPayment? = null,
|
||||
val startScreen: MessageBackupsStage,
|
||||
val stage: MessageBackupsStage = startScreen,
|
||||
@@ -35,7 +36,7 @@ data class MessageBackupsFlowState(
|
||||
* Whether or not the 'next' button on the type selection screen is enabled.
|
||||
*/
|
||||
fun isCheckoutButtonEnabled(): Boolean {
|
||||
return selectedMessageBackupTier in availableBackupTypes.map { it.tier } &&
|
||||
return selectedMessageBackupTier in allBackupTypes.map { it.tier } &&
|
||||
selectedMessageBackupTier != currentMessageBackupTier &&
|
||||
paymentReadyState == PaymentReadyState.READY
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ class MessageBackupsFlowViewModel(
|
||||
|
||||
private val internalStateFlow = MutableStateFlow(
|
||||
MessageBackupsFlowState(
|
||||
availableBackupTypes = emptyList(),
|
||||
allBackupTypes = emptyList(),
|
||||
currentMessageBackupTier = SignalStore.backup.backupTier,
|
||||
selectedMessageBackupTier = resolveSelectedTier(initialTierSelection, SignalStore.backup.backupTier),
|
||||
startScreen = startScreen
|
||||
@@ -91,9 +91,9 @@ class MessageBackupsFlowViewModel(
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
val availableBackupTypes: List<MessageBackupsType> = try {
|
||||
val allBackupTypes: List<MessageBackupsType> = try {
|
||||
withContext(SignalDispatchers.IO) {
|
||||
BackupRepository.getAvailableBackupsTypes(
|
||||
BackupRepository.getBackupTypes(
|
||||
if (!RemoteConfig.messageBackups) emptyList() else listOf(MessageBackupTier.FREE, MessageBackupTier.PAID)
|
||||
)
|
||||
}
|
||||
@@ -104,8 +104,9 @@ class MessageBackupsFlowViewModel(
|
||||
|
||||
internalStateFlow.update { state ->
|
||||
state.copy(
|
||||
availableBackupTypes = availableBackupTypes,
|
||||
selectedMessageBackupTier = if (state.selectedMessageBackupTier in availableBackupTypes.map { it.tier }) state.selectedMessageBackupTier else availableBackupTypes.firstOrNull()?.tier
|
||||
allBackupTypes = allBackupTypes,
|
||||
isBillingApiAvailable = AppDependencies.billingApi.isApiAvailable(),
|
||||
selectedMessageBackupTier = if (state.selectedMessageBackupTier in allBackupTypes.map { it.tier }) state.selectedMessageBackupTier else allBackupTypes.firstOrNull()?.tier
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -285,7 +286,7 @@ class MessageBackupsFlowViewModel(
|
||||
|
||||
MessageBackupTier.PAID -> {
|
||||
check(state.selectedMessageBackupTier == MessageBackupTier.PAID)
|
||||
check(state.availableBackupTypes.any { it.tier == state.selectedMessageBackupTier })
|
||||
check(state.allBackupTypes.any { it.tier == state.selectedMessageBackupTier })
|
||||
|
||||
viewModelScope.launch(SignalDispatchers.IO) {
|
||||
internalStateFlow.update { it.copy(inAppPayment = null) }
|
||||
|
||||
@@ -74,12 +74,14 @@ fun MessageBackupsTypeSelectionScreen(
|
||||
stage: MessageBackupsStage,
|
||||
currentBackupTier: MessageBackupTier?,
|
||||
selectedBackupTier: MessageBackupTier?,
|
||||
availableBackupTypes: List<MessageBackupsType>,
|
||||
allBackupTypes: List<MessageBackupsType>,
|
||||
isBillingApiAvailable: Boolean,
|
||||
isNextEnabled: Boolean,
|
||||
onMessageBackupsTierSelected: (MessageBackupTier) -> Unit,
|
||||
onNavigationClick: () -> Unit,
|
||||
onReadMoreClicked: () -> Unit,
|
||||
onNextClicked: () -> Unit
|
||||
onNextClicked: () -> Unit,
|
||||
onLearnMoreAboutWhyUserCanNotUpgrade: () -> Unit
|
||||
) {
|
||||
Scaffolds.Settings(
|
||||
title = "",
|
||||
@@ -144,7 +146,7 @@ fun MessageBackupsTypeSelectionScreen(
|
||||
}
|
||||
|
||||
itemsIndexed(
|
||||
availableBackupTypes,
|
||||
allBackupTypes,
|
||||
{ _, item -> item.tier }
|
||||
) { index, item ->
|
||||
MessageBackupsTypeBlock(
|
||||
@@ -159,9 +161,31 @@ fun MessageBackupsTypeSelectionScreen(
|
||||
}
|
||||
|
||||
val hasCurrentBackupTier = currentBackupTier != null
|
||||
var displayNotAvailableDialog by remember { mutableStateOf(false) }
|
||||
val onSubscribeButtonClick = remember(isBillingApiAvailable, selectedBackupTier) {
|
||||
{
|
||||
if (selectedBackupTier == MessageBackupTier.PAID && !isBillingApiAvailable) {
|
||||
displayNotAvailableDialog = true
|
||||
} else {
|
||||
onNextClicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (displayNotAvailableDialog) {
|
||||
UpgradeNotAvailableDialog(
|
||||
onConfirm = {
|
||||
displayNotAvailableDialog = false
|
||||
},
|
||||
onDismiss = onLearnMoreAboutWhyUserCanNotUpgrade,
|
||||
onDismissRequest = {
|
||||
displayNotAvailableDialog = false
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Buttons.LargeTonal(
|
||||
onClick = onNextClicked,
|
||||
onClick = onSubscribeButtonClick,
|
||||
enabled = isNextEnabled,
|
||||
modifier = Modifier
|
||||
.testTag("subscribe-button")
|
||||
@@ -169,8 +193,8 @@ fun MessageBackupsTypeSelectionScreen(
|
||||
.padding(vertical = if (hasCurrentBackupTier) 10.dp else 16.dp)
|
||||
) {
|
||||
val text: String = if (currentBackupTier == null) {
|
||||
if (selectedBackupTier == MessageBackupTier.PAID && availableBackupTypes.map { it.tier }.contains(selectedBackupTier)) {
|
||||
val paidTier = availableBackupTypes.first { it.tier == MessageBackupTier.PAID } as MessageBackupsType.Paid
|
||||
if (selectedBackupTier == MessageBackupTier.PAID && allBackupTypes.map { it.tier }.contains(selectedBackupTier)) {
|
||||
val paidTier = allBackupTypes.first { it.tier == MessageBackupTier.PAID } as MessageBackupsType.Paid
|
||||
val context = LocalContext.current
|
||||
|
||||
val price = remember(paidTier) {
|
||||
@@ -200,6 +224,23 @@ fun MessageBackupsTypeSelectionScreen(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UpgradeNotAvailableDialog(
|
||||
onConfirm: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
onDismissRequest: () -> Unit
|
||||
) {
|
||||
Dialogs.SimpleAlertDialog(
|
||||
title = stringResource(R.string.MessageBackupsTypeSelectionScreen__cant_upgrade_plan),
|
||||
body = stringResource(R.string.MessageBackupsTypeSelectionScreen__to_subscribe_to_signal_secure_backups),
|
||||
confirm = stringResource(android.R.string.ok),
|
||||
dismiss = stringResource(R.string.MessageBackupsTypeSelectionScreen__learn_more),
|
||||
onConfirm = onConfirm,
|
||||
onDismiss = onDismiss,
|
||||
onDismissRequest = onDismissRequest
|
||||
)
|
||||
}
|
||||
|
||||
@SignalPreview
|
||||
@Composable
|
||||
private fun MessageBackupsTypeSelectionScreenPreview() {
|
||||
@@ -209,12 +250,14 @@ private fun MessageBackupsTypeSelectionScreenPreview() {
|
||||
MessageBackupsTypeSelectionScreen(
|
||||
stage = MessageBackupsStage.TYPE_SELECTION,
|
||||
selectedBackupTier = selectedBackupsType,
|
||||
availableBackupTypes = testBackupTypes(),
|
||||
allBackupTypes = testBackupTypes(),
|
||||
onMessageBackupsTierSelected = { selectedBackupsType = it },
|
||||
onNavigationClick = {},
|
||||
onReadMoreClicked = {},
|
||||
onNextClicked = {},
|
||||
onLearnMoreAboutWhyUserCanNotUpgrade = {},
|
||||
currentBackupTier = null,
|
||||
isBillingApiAvailable = true,
|
||||
isNextEnabled = true
|
||||
)
|
||||
}
|
||||
@@ -229,17 +272,31 @@ private fun MessageBackupsTypeSelectionScreenWithCurrentTierPreview() {
|
||||
MessageBackupsTypeSelectionScreen(
|
||||
stage = MessageBackupsStage.TYPE_SELECTION,
|
||||
selectedBackupTier = selectedBackupsType,
|
||||
availableBackupTypes = testBackupTypes(),
|
||||
allBackupTypes = testBackupTypes(),
|
||||
onMessageBackupsTierSelected = { selectedBackupsType = it },
|
||||
onNavigationClick = {},
|
||||
onReadMoreClicked = {},
|
||||
onNextClicked = {},
|
||||
onLearnMoreAboutWhyUserCanNotUpgrade = {},
|
||||
currentBackupTier = MessageBackupTier.PAID,
|
||||
isBillingApiAvailable = true,
|
||||
isNextEnabled = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@SignalPreview
|
||||
@Composable
|
||||
private fun UpgradeNotAvailableDialogPreview() {
|
||||
Previews.Preview {
|
||||
UpgradeNotAvailableDialog(
|
||||
onConfirm = {},
|
||||
onDismiss = {},
|
||||
onDismissRequest = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MessageBackupsTypeBlock(
|
||||
messageBackupsType: MessageBackupsType,
|
||||
|
||||
@@ -97,8 +97,8 @@ abstract class UpgradeToPaidTierBottomSheet : ComposeBottomSheetDialogFragment()
|
||||
override fun SheetContent() {
|
||||
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
|
||||
|
||||
val paidBackupType = state.availableBackupTypes.firstOrNull { it.tier == MessageBackupTier.PAID } as? MessageBackupsType.Paid
|
||||
val freeBackupType = state.availableBackupTypes.firstOrNull { it.tier == MessageBackupTier.FREE } as? MessageBackupsType.Free
|
||||
val paidBackupType = state.allBackupTypes.firstOrNull { it.tier == MessageBackupTier.PAID } as? MessageBackupsType.Paid
|
||||
val freeBackupType = state.allBackupTypes.firstOrNull { it.tier == MessageBackupTier.FREE } as? MessageBackupsType.Free
|
||||
|
||||
if (paidBackupType != null && freeBackupType != null) {
|
||||
UpgradeSheetContent(
|
||||
|
||||
@@ -495,6 +495,7 @@ private fun RemoteBackupsSettingsContent(
|
||||
BackupCard(
|
||||
backupState = state.backupState,
|
||||
onBackupTypeActionButtonClicked = contentCallbacks::onBackupTypeActionClick,
|
||||
isPaidTierPricingAvailable = state.isPaidTierPricingAvailable,
|
||||
buttonsEnabled = backupDeleteState.isIdle()
|
||||
)
|
||||
}
|
||||
@@ -983,6 +984,7 @@ private fun LazyListScope.appendBackupDetailsItems(
|
||||
@Composable
|
||||
private fun BackupCard(
|
||||
backupState: BackupState.WithTypeAndRenewalTime,
|
||||
isPaidTierPricingAvailable: Boolean,
|
||||
buttonsEnabled: Boolean,
|
||||
onBackupTypeActionButtonClicked: (MessageBackupTier) -> Unit = {}
|
||||
) {
|
||||
@@ -1074,7 +1076,7 @@ private fun BackupCard(
|
||||
)
|
||||
}
|
||||
|
||||
if (backupState.isActive()) {
|
||||
if (backupState.isActive() && isPaidTierPricingAvailable) {
|
||||
val buttonText = when (messageBackupsType) {
|
||||
is MessageBackupsType.Paid -> stringResource(R.string.RemoteBackupsSettingsFragment__manage_or_cancel)
|
||||
is MessageBackupsType.Free -> stringResource(R.string.RemoteBackupsSettingsFragment__upgrade)
|
||||
@@ -1464,6 +1466,7 @@ private fun getBackupExportPhaseProgressString(state: ArchiveUploadProgressState
|
||||
stringResource(R.string.RemoteBackupsSettingsFragment__Waiting_for_Wifi)
|
||||
}
|
||||
}
|
||||
|
||||
ArchiveUploadProgressState.BackupPhase.Message -> {
|
||||
pluralStringResource(
|
||||
R.plurals.RemoteBackupsSettingsFragment__processing_messages_progress_text,
|
||||
@@ -1835,65 +1838,92 @@ private fun SubscriptionMismatchMissingGooglePlayCardPreview() {
|
||||
@Composable
|
||||
private fun BackupCardPreview() {
|
||||
Previews.Preview {
|
||||
Column {
|
||||
BackupCard(
|
||||
backupState = BackupState.ActivePaid(
|
||||
messageBackupsType = MessageBackupsType.Paid(
|
||||
pricePerMonth = FiatMoney(BigDecimal.valueOf(3), Currency.getInstance("CAD")),
|
||||
storageAllowanceBytes = 100_000_000,
|
||||
mediaTtl = 30.days
|
||||
LazyColumn {
|
||||
item {
|
||||
BackupCard(
|
||||
backupState = BackupState.ActivePaid(
|
||||
messageBackupsType = MessageBackupsType.Paid(
|
||||
pricePerMonth = FiatMoney(BigDecimal.valueOf(3), Currency.getInstance("CAD")),
|
||||
storageAllowanceBytes = 100_000_000,
|
||||
mediaTtl = 30.days
|
||||
),
|
||||
renewalTime = 1727193018.seconds,
|
||||
price = FiatMoney(BigDecimal.valueOf(3), Currency.getInstance("CAD"))
|
||||
),
|
||||
renewalTime = 1727193018.seconds,
|
||||
price = FiatMoney(BigDecimal.valueOf(3), Currency.getInstance("CAD"))
|
||||
),
|
||||
buttonsEnabled = true
|
||||
)
|
||||
isPaidTierPricingAvailable = true,
|
||||
buttonsEnabled = true
|
||||
)
|
||||
}
|
||||
|
||||
BackupCard(
|
||||
backupState = BackupState.Canceled(
|
||||
messageBackupsType = MessageBackupsType.Paid(
|
||||
pricePerMonth = FiatMoney(BigDecimal.valueOf(3), Currency.getInstance("CAD")),
|
||||
storageAllowanceBytes = 100_000_000,
|
||||
mediaTtl = 30.days
|
||||
item {
|
||||
BackupCard(
|
||||
backupState = BackupState.Canceled(
|
||||
messageBackupsType = MessageBackupsType.Paid(
|
||||
pricePerMonth = FiatMoney(BigDecimal.valueOf(3), Currency.getInstance("CAD")),
|
||||
storageAllowanceBytes = 100_000_000,
|
||||
mediaTtl = 30.days
|
||||
),
|
||||
renewalTime = 1727193018.seconds
|
||||
),
|
||||
renewalTime = 1727193018.seconds
|
||||
),
|
||||
buttonsEnabled = true
|
||||
)
|
||||
isPaidTierPricingAvailable = true,
|
||||
buttonsEnabled = true
|
||||
)
|
||||
}
|
||||
|
||||
BackupCard(
|
||||
backupState = BackupState.Inactive(
|
||||
messageBackupsType = MessageBackupsType.Paid(
|
||||
pricePerMonth = FiatMoney(BigDecimal.valueOf(3), Currency.getInstance("CAD")),
|
||||
storageAllowanceBytes = 100_000_000,
|
||||
mediaTtl = 30.days
|
||||
item {
|
||||
BackupCard(
|
||||
backupState = BackupState.Inactive(
|
||||
messageBackupsType = MessageBackupsType.Paid(
|
||||
pricePerMonth = FiatMoney(BigDecimal.valueOf(3), Currency.getInstance("CAD")),
|
||||
storageAllowanceBytes = 100_000_000,
|
||||
mediaTtl = 30.days
|
||||
),
|
||||
renewalTime = 1727193018.seconds
|
||||
),
|
||||
renewalTime = 1727193018.seconds
|
||||
),
|
||||
buttonsEnabled = true
|
||||
)
|
||||
isPaidTierPricingAvailable = true,
|
||||
buttonsEnabled = true
|
||||
)
|
||||
}
|
||||
|
||||
BackupCard(
|
||||
backupState = BackupState.ActivePaid(
|
||||
messageBackupsType = MessageBackupsType.Paid(
|
||||
pricePerMonth = FiatMoney(BigDecimal.valueOf(3), Currency.getInstance("CAD")),
|
||||
storageAllowanceBytes = 100_000_000,
|
||||
mediaTtl = 30.days
|
||||
item {
|
||||
BackupCard(
|
||||
backupState = BackupState.ActivePaid(
|
||||
messageBackupsType = MessageBackupsType.Paid(
|
||||
pricePerMonth = FiatMoney(BigDecimal.valueOf(3), Currency.getInstance("CAD")),
|
||||
storageAllowanceBytes = 100_000_000,
|
||||
mediaTtl = 30.days
|
||||
),
|
||||
renewalTime = 1727193018.seconds,
|
||||
price = FiatMoney(BigDecimal.valueOf(3), Currency.getInstance("CAD"))
|
||||
),
|
||||
renewalTime = 1727193018.seconds,
|
||||
price = FiatMoney(BigDecimal.valueOf(3), Currency.getInstance("CAD"))
|
||||
),
|
||||
buttonsEnabled = true
|
||||
)
|
||||
isPaidTierPricingAvailable = true,
|
||||
buttonsEnabled = true
|
||||
)
|
||||
}
|
||||
|
||||
BackupCard(
|
||||
backupState = BackupState.ActiveFree(
|
||||
messageBackupsType = MessageBackupsType.Free(
|
||||
mediaRetentionDays = 30
|
||||
)
|
||||
),
|
||||
buttonsEnabled = true
|
||||
)
|
||||
item {
|
||||
BackupCard(
|
||||
backupState = BackupState.ActiveFree(
|
||||
messageBackupsType = MessageBackupsType.Free(
|
||||
mediaRetentionDays = 30
|
||||
)
|
||||
),
|
||||
isPaidTierPricingAvailable = true,
|
||||
buttonsEnabled = true
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
BackupCard(
|
||||
backupState = BackupState.ActiveFree(
|
||||
messageBackupsType = MessageBackupsType.Free(
|
||||
mediaRetentionDays = 30
|
||||
)
|
||||
),
|
||||
isPaidTierPricingAvailable = false,
|
||||
buttonsEnabled = true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ data class RemoteBackupsSettingsState(
|
||||
val canRestoreUsingCellular: Boolean = false,
|
||||
val hasRedemptionError: Boolean = false,
|
||||
val isOutOfStorageSpace: Boolean = false,
|
||||
val isPaidTierPricingAvailable: Boolean = false,
|
||||
val totalAllowedStorageSpace: String = "",
|
||||
val backupState: BackupState,
|
||||
val backupMediaSize: Long = -1L,
|
||||
|
||||
@@ -83,6 +83,20 @@ class RemoteBackupsSettingsViewModel : ViewModel() {
|
||||
val restoreState: StateFlow<BackupRestoreState> = _restoreState
|
||||
|
||||
init {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val isBillingApiAvailable = AppDependencies.billingApi.isApiAvailable()
|
||||
if (isBillingApiAvailable) {
|
||||
_state.update {
|
||||
it.copy(isPaidTierPricingAvailable = true)
|
||||
}
|
||||
} else {
|
||||
val paidType = BackupRepository.getPaidType()
|
||||
_state.update {
|
||||
it.copy(isPaidTierPricingAvailable = paidType is NetworkResult.Success)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
refreshBackupMediaSizeState()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user