Add link+sync error flows.

This commit is contained in:
Michelle Tang
2024-12-05 18:33:12 -05:00
committed by Greyson Parrelli
parent e1d4566dfd
commit df5ef06109
8 changed files with 104 additions and 25 deletions

View File

@@ -179,7 +179,7 @@ class LinkDeviceFragment : ComposeFragment() {
onLinkNewDeviceClicked = { navController.navigateToQrScannerIfAuthed() },
onDeviceSelectedForRemoval = { device -> viewModel.setDeviceToRemove(device) },
onDeviceRemovalConfirmed = { device -> viewModel.removeDevice(device) },
onSyncFailureRetryRequested = { deviceId -> viewModel.onSyncErrorRetryRequested(deviceId) },
onSyncFailureRetryRequested = { viewModel.onSyncErrorRetryRequested() },
onSyncFailureIgnored = { viewModel.onSyncErrorIgnored() },
onEditDevice = { device ->
viewModel.setDeviceToEdit(device)
@@ -228,7 +228,7 @@ fun DeviceListScreen(
onLinkNewDeviceClicked: () -> Unit = {},
onDeviceSelectedForRemoval: (Device?) -> Unit = {},
onDeviceRemovalConfirmed: (Device) -> Unit = {},
onSyncFailureRetryRequested: (Int?) -> Unit = {},
onSyncFailureRetryRequested: () -> Unit = {},
onSyncFailureIgnored: () -> Unit = {},
onEditDevice: (Device) -> Unit = {}
) {
@@ -253,15 +253,10 @@ fun DeviceListScreen(
title = stringResource(R.string.LinkDeviceFragment__sync_failure_title),
body = stringResource(R.string.LinkDeviceFragment__sync_failure_body),
confirm = stringResource(R.string.LinkDeviceFragment__sync_failure_retry_button),
onConfirm = {
if (state.dialogState is DialogState.SyncingFailed) {
onSyncFailureRetryRequested(state.dialogState.deviceId)
} else {
onSyncFailureRetryRequested(null)
}
},
onConfirm = onSyncFailureRetryRequested,
dismiss = stringResource(R.string.LinkDeviceFragment__sync_failure_dismiss_button),
onDismiss = onSyncFailureIgnored
onDismissRequest = onSyncFailureIgnored,
onDeny = onSyncFailureIgnored
)
}
}

View File

@@ -24,6 +24,7 @@ import org.thoughtcrime.securesms.registration.secondary.DeviceNameCipher
import org.whispersystems.signalservice.api.NetworkResult
import org.whispersystems.signalservice.api.backup.MessageBackupKey
import org.whispersystems.signalservice.api.link.LinkedDeviceVerificationCodeResponse
import org.whispersystems.signalservice.api.link.TransferArchiveError
import org.whispersystems.signalservice.api.link.WaitForLinkedDeviceResponse
import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo
import org.whispersystems.signalservice.api.push.SignalServiceAddress
@@ -351,6 +352,24 @@ object LinkDeviceRepository {
return NetworkResult.NetworkError(IOException("Hit max retries!"))
}
/**
* If [createAndUploadArchive] fails to upload an archive, alert the linked device of the failure and if the user will try again
*/
fun sendTransferArchiveError(deviceId: Int, deviceCreatedAt: Long, error: TransferArchiveError) {
val archiveErrorResult = SignalNetwork.linkDevice.setTransferArchiveError(
destinationDeviceId = deviceId,
destinationDeviceCreated = deviceCreatedAt,
error = error
)
when (archiveErrorResult) {
is NetworkResult.Success -> Log.i(TAG, "[sendTransferArchiveError] Successfully sent transfer archive error.")
is NetworkResult.ApplicationError -> throw archiveErrorResult.throwable
is NetworkResult.NetworkError -> Log.w(TAG, "[sendTransferArchiveError] Network error when sending transfer archive error.", archiveErrorResult.exception)
is NetworkResult.StatusCodeError -> Log.w(TAG, "[sendTransferArchiveError] Status code error when sending transfer archive error.", archiveErrorResult.exception)
}
}
/**
* Changes the name of a linked device and sends a sync message if successful
*/

View File

@@ -27,7 +27,7 @@ data class LinkDeviceSettingsState(
data object Unlinking : DialogState
data object SyncingMessages : DialogState
data object SyncingTimedOut : DialogState
data class SyncingFailed(val deviceId: Int) : DialogState
data class SyncingFailed(val deviceId: Int, val deviceCreatedAt: Long) : DialogState
}
sealed interface OneTimeEvent {

View File

@@ -18,6 +18,7 @@ import org.thoughtcrime.securesms.linkdevice.LinkDeviceSettingsState.QrCodeState
import org.thoughtcrime.securesms.util.RemoteConfig
import org.thoughtcrime.securesms.util.Util
import org.whispersystems.signalservice.api.backup.MessageBackupKey
import org.whispersystems.signalservice.api.link.TransferArchiveError
import org.whispersystems.signalservice.api.link.WaitForLinkedDeviceResponse
import kotlin.time.Duration.Companion.seconds
@@ -260,7 +261,7 @@ class LinkDeviceViewModel : ViewModel() {
Log.w(TAG, "[addDeviceWithSync] Failed to upload the archive! Result: $uploadResult")
_state.update {
it.copy(
dialogState = DialogState.SyncingFailed(waitResult.id)
dialogState = DialogState.SyncingFailed(waitResult.id, waitResult.created)
)
}
}
@@ -309,16 +310,29 @@ class LinkDeviceViewModel : ViewModel() {
return this.getQueryParameter("capabilities")?.split(",")?.contains("backup") == true
}
fun onSyncErrorIgnored() {
fun onSyncErrorIgnored() = viewModelScope.launch(Dispatchers.IO) {
val dialogState = _state.value.dialogState
if (dialogState is DialogState.SyncingFailed) {
Log.i(TAG, "Alerting linked device of sync failure - will not retry")
LinkDeviceRepository.sendTransferArchiveError(dialogState.deviceId, dialogState.deviceCreatedAt, TransferArchiveError.CONTINUE_WITHOUT_UPLOAD)
}
_state.update {
it.copy(dialogState = DialogState.None)
it.copy(
linkDeviceResult = LinkDeviceResult.None,
dialogState = DialogState.None
)
}
}
fun onSyncErrorRetryRequested(deviceId: Int?) = viewModelScope.launch(Dispatchers.IO) {
if (deviceId != null) {
fun onSyncErrorRetryRequested() = viewModelScope.launch(Dispatchers.IO) {
val dialogState = _state.value.dialogState
if (dialogState is DialogState.SyncingFailed) {
Log.i(TAG, "Alerting linked device of sync failure - will retry")
LinkDeviceRepository.sendTransferArchiveError(dialogState.deviceId, dialogState.deviceCreatedAt, TransferArchiveError.RELINK_REQUESTED)
Log.i(TAG, "Need to unlink device first...")
val success = LinkDeviceRepository.removeDevice(deviceId)
val success = LinkDeviceRepository.removeDevice(dialogState.deviceId)
if (!success) {
Log.w(TAG, "Failed to remove device! We did our best. Continuing.")
}
@@ -326,6 +340,7 @@ class LinkDeviceViewModel : ViewModel() {
_state.update {
it.copy(
linkDeviceResult = LinkDeviceResult.None,
dialogState = DialogState.None,
oneTimeEvent = OneTimeEvent.LaunchQrCodeScanner
)