Update link+sync ui.

This commit is contained in:
Michelle Tang
2024-11-19 14:17:00 -08:00
committed by Greyson Parrelli
parent 66f851e92a
commit 75f4fed9ce
9 changed files with 162 additions and 9 deletions

View File

@@ -62,7 +62,7 @@ class AddLinkDeviceFragment : ComposeFragment() {
onQrCodeScanned = { data -> viewModel.onQrCodeScanned(data) },
onQrCodeApproved = {
navController.popBackStack()
viewModel.addDevice()
viewModel.addDevice(shouldSync = false)
},
onQrCodeDismissed = { viewModel.onQrCodeDismissed() },
onQrCodeRetry = { viewModel.onQrCodeScanned(state.linkUri.toString()) },
@@ -125,6 +125,7 @@ private fun MainScreen(
linkDeviceResult = state.linkDeviceResult,
onLinkDeviceSuccess = onLinkDeviceSuccess,
onLinkDeviceFailure = onLinkDeviceFailure,
navController = navController,
modifier = Modifier.padding(contentPadding)
)
}

View File

@@ -12,12 +12,14 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.stringResource
import androidx.navigation.NavController
import org.signal.core.ui.Dialogs
import org.signal.qr.QrScannerView
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.linkdevice.LinkDeviceRepository.LinkDeviceResult
import org.thoughtcrime.securesms.mediasend.camerax.CameraXModelBlocklist
import org.thoughtcrime.securesms.qr.QrScanScreens
import org.thoughtcrime.securesms.util.navigation.safeNavigate
import java.util.concurrent.TimeUnit
/**
@@ -36,6 +38,7 @@ fun LinkDeviceQrScanScreen(
linkDeviceResult: LinkDeviceResult,
onLinkDeviceSuccess: () -> Unit,
onLinkDeviceFailure: () -> Unit,
navController: NavController?,
modifier: Modifier = Modifier
) {
val lifecycleOwner = LocalLifecycleOwner.current
@@ -45,7 +48,10 @@ fun LinkDeviceQrScanScreen(
LinkDeviceSettingsState.QrCodeState.NONE -> {
Unit
}
LinkDeviceSettingsState.QrCodeState.VALID -> {
LinkDeviceSettingsState.QrCodeState.VALID_WITH_SYNC -> {
navController?.safeNavigate(R.id.action_addLinkDeviceFragment_to_linkDeviceSyncBottomSheet)
}
LinkDeviceSettingsState.QrCodeState.VALID_WITHOUT_SYNC -> {
Dialogs.SimpleAlertDialog(
title = stringResource(id = R.string.DeviceProvisioningActivity_link_this_device),
body = stringResource(id = R.string.AddLinkDeviceFragment__this_device_will_see_your_groups_contacts),

View File

@@ -40,6 +40,6 @@ data class LinkDeviceSettingsState(
}
enum class QrCodeState {
NONE, VALID, INVALID
NONE, VALID_WITH_SYNC, VALID_WITHOUT_SYNC, INVALID
}
}

View File

@@ -0,0 +1,127 @@
package org.thoughtcrime.securesms.linkdevice
import android.content.DialogInterface
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import org.signal.core.ui.BottomSheets
import org.signal.core.ui.Previews
import org.signal.core.ui.SignalPreview
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.compose.ComposeBottomSheetDialogFragment
/**
* Bottom sheet dialog allowing users to choose whether to transfer their message history
*/
class LinkDeviceSyncBottomSheet : ComposeBottomSheetDialogFragment() {
private val viewModel: LinkDeviceViewModel by activityViewModels()
@Composable
override fun SheetContent() {
SyncSheet(
onLink = { shouldSync ->
viewModel.addDevice(shouldSync)
findNavController().popBackStack(R.id.linkDeviceFragment, false)
}
)
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
viewModel.onQrCodeDismissed()
}
}
@Composable
fun SyncSheet(
onLink: (Boolean) -> Unit
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.padding(horizontal = 24.dp)
) {
BottomSheets.Handle()
Spacer(modifier = Modifier.size(12.dp))
SheetOption(
painterResource(R.drawable.symbol_chat_check),
stringResource(R.string.LinkDeviceSyncBottomSheet_transfer),
stringResource(R.string.LinkDeviceSyncBottomSheet_transfer_your_text)
) { onLink(true) }
SheetOption(
painterResource(R.drawable.symbol_chat_x),
stringResource(R.string.LinkDeviceSyncBottomSheet_dont_transfer),
stringResource(R.string.LinkDeviceSyncBottomSheet_no_old_messages)
) { onLink(false) }
Spacer(modifier = Modifier.size(60.dp))
}
}
@Composable
private fun SheetOption(
icon: Painter,
title: String,
description: String,
onLink: () -> Unit
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp)
.defaultMinSize(minHeight = 96.dp)
.background(color = MaterialTheme.colorScheme.surface, shape = RoundedCornerShape(18.dp))
.clickable { onLink() },
verticalAlignment = Alignment.CenterVertically
) {
Icon(
icon,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(start = 24.dp, end = 16.dp).size(44.dp)
)
Column(
modifier = Modifier.padding(end = 24.dp)
) {
Text(
text = title,
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurface
)
Text(
text = description,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
}
@SignalPreview
@Composable
fun SyncSheetSheetSheetPreview() {
Previews.BottomSheetPreview {
SyncSheet(onLink = {})
}
}

View File

@@ -116,7 +116,7 @@ class LinkDeviceViewModel : ViewModel() {
if (LinkDeviceRepository.isValidQr(uri)) {
_state.update {
it.copy(
qrCodeState = QrCodeState.VALID,
qrCodeState = if (uri.supportsLinkAndSync() && RemoteConfig.linkAndSync) QrCodeState.VALID_WITH_SYNC else QrCodeState.VALID_WITHOUT_SYNC,
linkUri = uri,
showFrontCamera = null
)
@@ -140,7 +140,7 @@ class LinkDeviceViewModel : ViewModel() {
}
}
fun addDevice() = viewModelScope.launch(Dispatchers.IO) {
fun addDevice(shouldSync: Boolean) = viewModelScope.launch(Dispatchers.IO) {
val linkUri: Uri = _state.value.linkUri!!
_state.update {
@@ -151,11 +151,11 @@ class LinkDeviceViewModel : ViewModel() {
)
}
if (linkUri.supportsLinkAndSync() && RemoteConfig.linkAndSync) {
Log.i(TAG, "Link+Sync supported.")
if (shouldSync) {
Log.i(TAG, "Adding device with sync.")
addDeviceWithSync(linkUri)
} else {
Log.i(TAG, "Link+Sync not supported. (uri: ${linkUri.supportsLinkAndSync()}, remoteConfig: ${RemoteConfig.linkAndSync})")
Log.i(TAG, "Adding device without sync. (uri: ${linkUri.supportsLinkAndSync()}, remoteConfig: ${RemoteConfig.linkAndSync})")
addDeviceWithoutSync(linkUri)
}
}
@@ -210,6 +210,7 @@ class LinkDeviceViewModel : ViewModel() {
}
if (result !is LinkDeviceResult.Success) {
Log.w(TAG, "Unable to link device $result")
return
}
@@ -244,6 +245,7 @@ class LinkDeviceViewModel : ViewModel() {
dialogState = DialogState.None
)
}
loadDevices()
}
is LinkDeviceRepository.LinkUploadArchiveResult.BackupCreationFailure,
is LinkDeviceRepository.LinkUploadArchiveResult.BadRequest,
@@ -269,6 +271,7 @@ class LinkDeviceViewModel : ViewModel() {
}
if (result !is LinkDeviceResult.Success) {
Log.w(TAG, "Unable to link device $result")
return
}

View File

@@ -238,7 +238,7 @@ private fun Content(
)
} else {
AboutRow(
startIcon = painterResource(id = R.drawable.chat_x),
startIcon = painterResource(id = R.drawable.symbol_chat_x),
text = stringResource(id = R.string.AboutSheet__no_direct_message, model.shortName),
modifier = Modifier.align(alignment = Alignment.Start),
onClick = onClickSignalConnections