Fix backups deletion pipeline.

This commit is contained in:
Alex Hart
2025-07-25 10:30:47 -03:00
committed by Michelle Tang
parent 3bb2ab3a0c
commit 53ee0648c0
11 changed files with 160 additions and 67 deletions

View File

@@ -22,7 +22,6 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
@@ -69,7 +68,6 @@ import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.DialogProperties
import androidx.fragment.app.setFragmentResultListener
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.fragment.findNavController
@@ -487,7 +485,7 @@ private fun RemoteBackupsSettingsContent(
state = state.backupState,
onLearnMoreClick = contentCallbacks::onLearnMoreAboutLostSubscription,
onRenewClick = contentCallbacks::onRenewLostSubscription,
isRenewEnabled = backupDeleteState != DeletionState.DELETE_BACKUPS
isRenewEnabled = backupDeleteState.isIdle()
)
}
@@ -497,7 +495,7 @@ private fun RemoteBackupsSettingsContent(
BackupCard(
backupState = state.backupState,
onBackupTypeActionButtonClicked = contentCallbacks::onBackupTypeActionClick,
buttonsEnabled = backupDeleteState != DeletionState.DELETE_BACKUPS
buttonsEnabled = backupDeleteState.isIdle()
)
}
@@ -506,7 +504,7 @@ private fun RemoteBackupsSettingsContent(
title = stringResource(R.string.RemoteBackupsSettingsFragment__your_subscription_was_not_found),
onRenewClick = contentCallbacks::onRenewLostSubscription,
onLearnMoreClick = contentCallbacks::onLearnMoreAboutLostSubscription,
isRenewEnabled = backupDeleteState != DeletionState.DELETE_BACKUPS
isRenewEnabled = backupDeleteState.isIdle()
)
}
@@ -535,6 +533,7 @@ private fun RemoteBackupsSettingsContent(
canRestoreUsingCellular = state.canRestoreUsingCellular,
canBackUpNow = !state.isOutOfStorageSpace,
includeDebuglog = state.includeDebuglog,
backupMediaDetails = state.backupMediaDetails,
contentCallbacks = contentCallbacks
)
} else {
@@ -591,7 +590,7 @@ private fun RemoteBackupsSettingsContent(
}
RemoteBackupsSettingsState.Dialog.PROGRESS_SPINNER -> {
CircularProgressDialog(onDismiss = contentCallbacks::onDialogDismissed)
Dialogs.IndeterminateProgressDialog(onDismissRequest = { contentCallbacks.onDialogDismissed() })
}
RemoteBackupsSettingsState.Dialog.DOWNLOADING_YOUR_BACKUP -> {
@@ -764,13 +763,13 @@ private fun LazyListScope.appendBackupDeletionItems(
} else {
item {
LinearProgressIndicator(
modifier = Modifier.fillMaxWidth()
modifier = Modifier.horizontalGutters().fillMaxWidth()
)
}
}
}
DeletionState.DELETE_BACKUPS -> {
DeletionState.MEDIA_DOWNLOAD_FINISHED, DeletionState.DELETE_BACKUPS -> {
item {
DescriptionText(text = stringResource(R.string.RemoteBackupsSettingsFragment__backups_have_been_turned_off_and_your_data))
}
@@ -841,6 +840,7 @@ private fun LazyListScope.appendBackupDetailsItems(
canRestoreUsingCellular: Boolean,
canBackUpNow: Boolean,
includeDebuglog: Boolean?,
backupMediaDetails: RemoteBackupsSettingsState.BackupMediaDetails?,
contentCallbacks: ContentCallbacks
) {
item {
@@ -851,6 +851,16 @@ private fun LazyListScope.appendBackupDetailsItems(
Texts.SectionHeader(text = stringResource(id = R.string.RemoteBackupsSettingsFragment__backup_details))
}
if (backupMediaDetails != null) {
item {
Column(modifier = Modifier.horizontalGutters()) {
Text("[Internal Only] Backup Media Details")
Text("Awaiting Restore: ${backupMediaDetails.awaitingRestore.toUnitString()}")
Text("Offloaded: ${backupMediaDetails.offloaded.toUnitString()}")
}
}
}
if (backupRestoreState !is BackupRestoreState.None) {
if (backupRestoreState is BackupRestoreState.FromBackupStatusData) {
appendRestoreFromBackupStatusData(
@@ -1643,35 +1653,6 @@ private fun ResumeRestoreOverCellularDialog(
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun CircularProgressDialog(
onDismiss: () -> Unit
) {
BasicAlertDialog(
onDismissRequest = onDismiss,
properties = DialogProperties(
dismissOnBackPress = false,
dismissOnClickOutside = false
)
) {
Surface(
shape = Dialogs.Defaults.shape,
color = Dialogs.Defaults.containerColor
) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.aspectRatio(1f)
) {
CircularProgressIndicator(
modifier = Modifier
.size(48.dp)
)
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun BackupFrequencyDialog(
@@ -2122,16 +2103,6 @@ private fun SkipDownloadDialogPreview() {
}
}
@SignalPreview
@Composable
private fun CircularProgressDialogPreview() {
Previews.Preview {
CircularProgressDialog(
onDismiss = {}
)
}
}
@SignalPreview
@Composable
private fun BackupFrequencyDialogPreview() {

View File

@@ -5,6 +5,7 @@
package org.thoughtcrime.securesms.components.settings.app.backups.remote
import org.signal.core.util.ByteSize
import org.thoughtcrime.securesms.backup.v2.BackupFrequency
import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
import org.thoughtcrime.securesms.components.settings.app.backups.BackupState
@@ -28,9 +29,15 @@ data class RemoteBackupsSettingsState(
val dialog: Dialog = Dialog.NONE,
val snackbar: Snackbar = Snackbar.NONE,
val includeDebuglog: Boolean? = null,
val canBackupMessagesJobRun: Boolean = false
val canBackupMessagesJobRun: Boolean = false,
val backupMediaDetails: BackupMediaDetails? = null
) {
data class BackupMediaDetails(
val awaitingRestore: ByteSize,
val offloaded: ByteSize
)
enum class Dialog {
NONE,
TURN_OFF_AND_DELETE_BACKUPS,

View File

@@ -22,6 +22,7 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.reactive.asFlow
import kotlinx.coroutines.withContext
import org.signal.core.util.bytes
import org.signal.core.util.logging.Log
import org.signal.core.util.mebiBytes
@@ -44,6 +45,7 @@ import org.thoughtcrime.securesms.jobs.BackupMessagesJob
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.keyvalue.protos.ArchiveUploadProgressState
import org.thoughtcrime.securesms.service.MessageBackupListener
import org.thoughtcrime.securesms.util.Environment
import org.thoughtcrime.securesms.util.RemoteConfig
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.api.NetworkResult
@@ -81,7 +83,7 @@ class RemoteBackupsSettingsViewModel : ViewModel() {
init {
viewModelScope.launch(Dispatchers.IO) {
_state.update { it.copy(backupMediaSize = SignalDatabase.attachments.getEstimatedArchiveMediaSize()) }
refreshBackupMediaSizeState()
}
viewModelScope.launch(Dispatchers.IO) {
@@ -105,7 +107,7 @@ class RemoteBackupsSettingsViewModel : ViewModel() {
.attachmentUpdates()
.throttleLatest(5.seconds)
.collectLatest {
_state.update { it.copy(backupMediaSize = SignalDatabase.attachments.getEstimatedArchiveMediaSize()) }
refreshBackupMediaSizeState()
}
}
@@ -209,10 +211,14 @@ class RemoteBackupsSettingsViewModel : ViewModel() {
}
fun turnOffAndDeleteBackups() {
requestDialog(RemoteBackupsSettingsState.Dialog.PROGRESS_SPINNER)
viewModelScope.launch {
requestDialog(RemoteBackupsSettingsState.Dialog.PROGRESS_SPINNER)
viewModelScope.launch(Dispatchers.IO) {
BackupRepository.turnOffAndDisableBackups()
withContext(Dispatchers.IO) {
BackupRepository.turnOffAndDisableBackups()
}
requestDialog(RemoteBackupsSettingsState.Dialog.NONE)
}
}
@@ -229,6 +235,20 @@ class RemoteBackupsSettingsViewModel : ViewModel() {
_state.update { it.copy(includeDebuglog = includeDebuglog) }
}
private fun refreshBackupMediaSizeState() {
_state.update {
it.copy(
backupMediaSize = SignalDatabase.attachments.getEstimatedArchiveMediaSize(),
backupMediaDetails = if (RemoteConfig.internalUser || Environment.IS_STAGING) {
RemoteBackupsSettingsState.BackupMediaDetails(
awaitingRestore = SignalDatabase.attachments.getRemainingRestorableAttachmentSize().bytes,
offloaded = SignalDatabase.attachments.getOptimizedMediaAttachmentSize().bytes
)
} else null
)
}
}
private suspend fun refreshState(lastPurchase: InAppPaymentTable.InAppPayment?) {
try {
Log.i(TAG, "Performing a state refresh.")