mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-24 13:08:46 +00:00
Add specific registration error cases for SVRB.
This commit is contained in:
committed by
Cody Henthorne
parent
0726c29528
commit
9dcc704a9e
@@ -2178,6 +2178,9 @@ sealed interface RemoteRestoreResult {
|
||||
data object NetworkError : RemoteRestoreResult
|
||||
data object Canceled : RemoteRestoreResult
|
||||
data object Failure : RemoteRestoreResult
|
||||
|
||||
/** SVRB has failed in such a way that recovering a backup is impossible. */
|
||||
data object PermanentSvrBFailure : RemoteRestoreResult
|
||||
}
|
||||
|
||||
sealed interface RestoreTimestampResult {
|
||||
|
||||
@@ -27,6 +27,11 @@ interface ContactSupportCallbacks {
|
||||
override fun submitWithoutDebuglog() = Unit
|
||||
override fun cancel() = Unit
|
||||
}
|
||||
|
||||
fun interface StringForReason<Reason> {
|
||||
@StringRes
|
||||
operator fun invoke(reason: Reason?): Int
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,23 +63,23 @@ fun ContactSupportDialog(
|
||||
* sending an email when ready.
|
||||
*/
|
||||
@Composable
|
||||
fun SendSupportEmailEffect(
|
||||
contactSupportState: ContactSupportViewModel.ContactSupportState,
|
||||
@StringRes subjectRes: Int,
|
||||
@StringRes filterRes: Int,
|
||||
fun <Reason> SendSupportEmailEffect(
|
||||
contactSupportState: ContactSupportViewModel.ContactSupportState<Reason>,
|
||||
subjectRes: ContactSupportCallbacks.StringForReason<Reason>,
|
||||
filterRes: ContactSupportCallbacks.StringForReason<Reason>,
|
||||
hide: () -> Unit
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
LaunchedEffect(contactSupportState.sendEmail) {
|
||||
if (contactSupportState.sendEmail) {
|
||||
val subject = context.getString(subjectRes)
|
||||
val subject = context.getString(subjectRes(contactSupportState.reason))
|
||||
val prefix = if (contactSupportState.debugLogUrl != null) {
|
||||
"\n${context.getString(R.string.HelpFragment__debug_log)} ${contactSupportState.debugLogUrl}\n\n"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
|
||||
val body = SupportEmailUtil.generateSupportEmailBody(context, filterRes, prefix, null)
|
||||
val body = SupportEmailUtil.generateSupportEmailBody(context, filterRes(contactSupportState.reason), prefix, null)
|
||||
CommunicationActions.openEmail(context, SupportEmailUtil.getSupportEmailAddress(context), subject, body)
|
||||
hide()
|
||||
}
|
||||
|
||||
@@ -35,23 +35,20 @@ class ContactSupportDialogFragment : ComposeDialogFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
private val contactSupportViewModel: ContactSupportViewModel by viewModel {
|
||||
private val contactSupportViewModel: ContactSupportViewModel<Unit> by viewModel {
|
||||
ContactSupportViewModel(
|
||||
showInitially = true
|
||||
)
|
||||
}
|
||||
|
||||
private val subject: Int by lazy { requireArguments().getInt(SUBJECT) }
|
||||
private val filter: Int by lazy { requireArguments().getInt(FILTER) }
|
||||
|
||||
@Composable
|
||||
override fun DialogContent() {
|
||||
val contactSupportState by contactSupportViewModel.state.collectAsStateWithLifecycle()
|
||||
|
||||
SendSupportEmailEffect(
|
||||
contactSupportState = contactSupportState,
|
||||
subjectRes = subject,
|
||||
filterRes = filter
|
||||
subjectRes = { requireArguments().getInt(SUBJECT) },
|
||||
filterRes = { requireArguments().getInt(FILTER) }
|
||||
) {
|
||||
contactSupportViewModel.hideContactSupport()
|
||||
dismissAllowingStateLoss()
|
||||
|
||||
@@ -17,18 +17,21 @@ import org.thoughtcrime.securesms.logsubmit.SubmitDebugLogRepository
|
||||
|
||||
/**
|
||||
* Intended to be used to drive [ContactSupportDialog].
|
||||
*
|
||||
* @param Reason A type that can be supplied as the reason for showing the dialog when you invoke [showContactSupport]. Useful for when you may want to show
|
||||
* the option for different reasons. Will be given back to you, if set, via [state] in [ContactSupportState.reason].
|
||||
*/
|
||||
class ContactSupportViewModel(
|
||||
class ContactSupportViewModel<Reason>(
|
||||
val showInitially: Boolean = false
|
||||
) : ViewModel(), ContactSupportCallbacks {
|
||||
private val submitDebugLogRepository: SubmitDebugLogRepository = SubmitDebugLogRepository()
|
||||
|
||||
private val store: MutableStateFlow<ContactSupportState> = MutableStateFlow(ContactSupportState(show = showInitially))
|
||||
private val store: MutableStateFlow<ContactSupportState<Reason>> = MutableStateFlow(ContactSupportState(show = showInitially))
|
||||
|
||||
val state: StateFlow<ContactSupportState> = store.asStateFlow()
|
||||
val state: StateFlow<ContactSupportState<Reason>> = store.asStateFlow()
|
||||
|
||||
fun showContactSupport() {
|
||||
store.update { it.copy(show = true) }
|
||||
fun showContactSupport(reason: Reason? = null) {
|
||||
store.update { it.copy(show = true, reason = reason) }
|
||||
}
|
||||
|
||||
fun hideContactSupport() {
|
||||
@@ -60,10 +63,11 @@ class ContactSupportViewModel(
|
||||
hideContactSupport()
|
||||
}
|
||||
|
||||
data class ContactSupportState(
|
||||
data class ContactSupportState<Reason>(
|
||||
val show: Boolean = false,
|
||||
val showAsProgress: Boolean = false,
|
||||
val sendEmail: Boolean = false,
|
||||
val debugLogUrl: String? = null
|
||||
val debugLogUrl: String? = null,
|
||||
val reason: Reason? = null
|
||||
)
|
||||
}
|
||||
|
||||
@@ -360,6 +360,7 @@ class InternalBackupPlaygroundViewModel : ViewModel() {
|
||||
RemoteRestoreResult.Success -> _state.value = _state.value.copy(statusMessage = "Import complete!")
|
||||
RemoteRestoreResult.Canceled,
|
||||
RemoteRestoreResult.Failure,
|
||||
RemoteRestoreResult.PermanentSvrBFailure,
|
||||
RemoteRestoreResult.NetworkError -> {
|
||||
_state.value = _state.value.copy(statusMessage = "Import failed! $result")
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ class EnterBackupKeyFragment : ComposeFragment() {
|
||||
|
||||
private val sharedViewModel by activityViewModels<RegistrationViewModel>()
|
||||
private val viewModel by viewModels<EnterBackupKeyViewModel>()
|
||||
private val contactSupportViewModel: ContactSupportViewModel by viewModels()
|
||||
private val contactSupportViewModel: ContactSupportViewModel<Unit> by viewModels()
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
@@ -79,12 +79,12 @@ class EnterBackupKeyFragment : ComposeFragment() {
|
||||
override fun FragmentContent() {
|
||||
val state by viewModel.state.collectAsStateWithLifecycle()
|
||||
val sharedState by sharedViewModel.state.collectAsStateWithLifecycle()
|
||||
val contactSupportState: ContactSupportViewModel.ContactSupportState by contactSupportViewModel.state.collectAsStateWithLifecycle()
|
||||
val contactSupportState: ContactSupportViewModel.ContactSupportState<Unit> by contactSupportViewModel.state.collectAsStateWithLifecycle()
|
||||
|
||||
SendSupportEmailEffect(
|
||||
contactSupportState = contactSupportState,
|
||||
subjectRes = R.string.EnterBackupKey_network_failure_support_email,
|
||||
filterRes = R.string.EnterBackupKey_network_failure_support_email_filter
|
||||
subjectRes = { R.string.EnterBackupKey_network_failure_support_email },
|
||||
filterRes = { R.string.EnterBackupKey_network_failure_support_email_filter }
|
||||
) {
|
||||
contactSupportViewModel.hideContactSupport()
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ class RemoteRestoreActivity : BaseActivity() {
|
||||
RemoteRestoreViewModel(intent.getBooleanExtra(KEY_ONLY_OPTION, false))
|
||||
}
|
||||
|
||||
private val contactSupportViewModel: ContactSupportViewModel by viewModels()
|
||||
private val contactSupportViewModel: ContactSupportViewModel<ContactSupportReason> by viewModels()
|
||||
|
||||
private lateinit var wakeLock: RemoteRestoreWakeLock
|
||||
|
||||
@@ -146,12 +146,22 @@ class RemoteRestoreActivity : BaseActivity() {
|
||||
|
||||
setContent {
|
||||
val state: RemoteRestoreViewModel.ScreenState by viewModel.state.collectAsStateWithLifecycle()
|
||||
val contactSupportState: ContactSupportViewModel.ContactSupportState by contactSupportViewModel.state.collectAsStateWithLifecycle()
|
||||
val contactSupportState: ContactSupportViewModel.ContactSupportState<ContactSupportReason> by contactSupportViewModel.state.collectAsStateWithLifecycle()
|
||||
|
||||
SendSupportEmailEffect(
|
||||
contactSupportState = contactSupportState,
|
||||
subjectRes = R.string.EnterBackupKey_network_failure_support_email,
|
||||
filterRes = R.string.EnterBackupKey_network_failure_support_email_filter
|
||||
subjectRes = { reason ->
|
||||
when (reason) {
|
||||
ContactSupportReason.SvrBFailure -> R.string.EnterBackupKey_permanent_failure_support_email
|
||||
else -> R.string.EnterBackupKey_network_failure_support_email
|
||||
}
|
||||
},
|
||||
filterRes = { reason ->
|
||||
when (reason) {
|
||||
ContactSupportReason.SvrBFailure -> R.string.EnterBackupKey_permanent_failure_support_email_filter
|
||||
else -> R.string.EnterBackupKey_network_failure_support_email_filter
|
||||
}
|
||||
}
|
||||
) {
|
||||
contactSupportViewModel.hideContactSupport()
|
||||
}
|
||||
@@ -192,12 +202,16 @@ class RemoteRestoreActivity : BaseActivity() {
|
||||
fun onEvent(restoreEvent: RestoreV2Event) {
|
||||
viewModel.updateRestoreProgress(restoreEvent)
|
||||
}
|
||||
|
||||
enum class ContactSupportReason {
|
||||
NetworkError, SvrBFailure
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RestoreFromBackupContent(
|
||||
state: RemoteRestoreViewModel.ScreenState,
|
||||
contactSupportState: ContactSupportViewModel.ContactSupportState = ContactSupportViewModel.ContactSupportState(),
|
||||
contactSupportState: ContactSupportViewModel.ContactSupportState<RemoteRestoreActivity.ContactSupportReason> = ContactSupportViewModel.ContactSupportState(),
|
||||
onRestoreBackupClick: () -> Unit = {},
|
||||
onRetryRestoreTier: () -> Unit = {},
|
||||
onContactSupport: () -> Unit = {},
|
||||
@@ -219,7 +233,8 @@ private fun RestoreFromBackupContent(
|
||||
onRestoreBackupClick = onRestoreBackupClick,
|
||||
onCancelClick = onCancelClick,
|
||||
onImportErrorDialogDismiss = onImportErrorDialogDismiss,
|
||||
onUpdateSignal = onUpdateSignal
|
||||
onUpdateSignal = onUpdateSignal,
|
||||
onContactSupport = onContactSupport
|
||||
)
|
||||
}
|
||||
|
||||
@@ -255,7 +270,8 @@ private fun BackupAvailableContent(
|
||||
onRestoreBackupClick: () -> Unit,
|
||||
onCancelClick: () -> Unit,
|
||||
onImportErrorDialogDismiss: () -> Unit,
|
||||
onUpdateSignal: () -> Unit
|
||||
onUpdateSignal: () -> Unit,
|
||||
onContactSupport: () -> Unit
|
||||
) {
|
||||
val subtitle = if (state.backupSize.bytes > 0) {
|
||||
stringResource(
|
||||
@@ -377,6 +393,9 @@ private fun BackupAvailableContent(
|
||||
RestoreFailedDialog(onDismiss = onImportErrorDialogDismiss)
|
||||
}
|
||||
}
|
||||
RemoteRestoreViewModel.ImportState.FailureWithLogPrompt -> {
|
||||
RestoreFailedWithLogPromptDialog(onDismiss = onImportErrorDialogDismiss, onContactSupport = onContactSupport)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -565,6 +584,21 @@ fun RestoreFailedDialog(
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RestoreFailedWithLogPromptDialog(
|
||||
onDismiss: () -> Unit = {},
|
||||
onContactSupport: () -> Unit = {}
|
||||
) {
|
||||
Dialogs.SimpleAlertDialog(
|
||||
title = stringResource(R.string.RemoteRestoreActivity__failure_with_log_prompt_title),
|
||||
body = stringResource(R.string.RemoteRestoreActivity__failure_with_log_prompt_body),
|
||||
confirm = stringResource(R.string.RemoteRestoreActivity__failure_with_log_prompt_contact_button),
|
||||
dismiss = stringResource(android.R.string.ok),
|
||||
onConfirm = onContactSupport,
|
||||
onDismiss = onDismiss
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RestoreNetworkFailedDialog(
|
||||
onDismiss: () -> Unit = {}
|
||||
@@ -617,6 +651,14 @@ private fun RestoreFailedDialogPreview() {
|
||||
}
|
||||
}
|
||||
|
||||
@SignalPreview
|
||||
@Composable
|
||||
private fun RestoreFailedWithLogPromptDialogPreview() {
|
||||
Previews.Preview {
|
||||
RestoreFailedWithLogPromptDialog()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun InvalidBackupVersionDialog(
|
||||
onUpdateSignal: () -> Unit = {},
|
||||
|
||||
@@ -120,6 +120,11 @@ class RemoteRestoreViewModel(isOnlyRestoreOption: Boolean) : ViewModel() {
|
||||
Log.w(TAG, "Restore failed with $result")
|
||||
store.update { it.copy(importState = ImportState.Failed) }
|
||||
}
|
||||
|
||||
RemoteRestoreResult.PermanentSvrBFailure -> {
|
||||
Log.w(TAG, "Hit a permanent SVRB error.")
|
||||
store.update { it.copy(importState = ImportState.FailureWithLogPrompt) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -181,5 +186,6 @@ class RemoteRestoreViewModel(isOnlyRestoreOption: Boolean) : ViewModel() {
|
||||
data object Restored : ImportState
|
||||
data object NetworkFailure : ImportState
|
||||
data object Failed : ImportState
|
||||
data object FailureWithLogPrompt : ImportState
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1483,6 +1483,12 @@
|
||||
<string name="RemoteRestoreActivity__skip_restore">Skip restore</string>
|
||||
<!-- Dialog title displayed when remote restore failed -->
|
||||
<string name="RemoteRestoreActivity__couldnt_transfer">Couldn\'t finish transfer</string>
|
||||
<!-- Dialog title displayed when remote restore failed and we want them to contact support -->
|
||||
<string name="RemoteRestoreActivity__failure_with_log_prompt_title">Can\'t restore backup</string>
|
||||
<!-- Dialog body displayed when remote restore failed and we want them to contact support -->
|
||||
<string name="RemoteRestoreActivity__failure_with_log_prompt_body">An error occurred while restoring your backup. Your backup is not recoverable. Please contact support for help.</string>
|
||||
<!-- Dialog action button that will link users to a flow to contact support, displayed when remote restore failed -->
|
||||
<string name="RemoteRestoreActivity__failure_with_log_prompt_contact_button">Contact support</string>
|
||||
<!-- Dialog message displayed when remote restore failed -->
|
||||
<string name="RemoteRestoreActivity__error_occurred">An error occurred and your account couldn’t be transferred. Try again by choosing your transfer method.</string>
|
||||
<!-- Dialog title displayed when remote restore failed because of an outdated backup version. -->
|
||||
@@ -8590,6 +8596,9 @@
|
||||
<!-- Email subject when contacting support on a restore backup network issue -->
|
||||
<string name="EnterBackupKey_network_failure_support_email">Signal Android Backup restore network error</string>
|
||||
<string name="EnterBackupKey_network_failure_support_email_filter" translatable="false">Android SignalBackups Import Failed</string>
|
||||
<!-- Email subject when contacting support on a permanent backup import failure -->
|
||||
<string name="EnterBackupKey_permanent_failure_support_email">Signal Android Backup restore permanent failure</string>
|
||||
<string name="EnterBackupKey_permanent_failure_support_email_filter" translatable="false">Android SignalBackups Import Permanent Failure</string>
|
||||
|
||||
<!-- Email subject when contacting support on a create backup failure -->
|
||||
<string name="BackupAlertBottomSheet_network_failure_support_email">Signal Android Backup export network error</string>
|
||||
|
||||
Reference in New Issue
Block a user