MessageBackupsCheckoutFlow free tier happy path.

This commit is contained in:
Alex Hart
2025-01-24 13:05:35 -04:00
committed by Greyson Parrelli
parent bc09df97b0
commit 4c72a88a50
11 changed files with 393 additions and 131 deletions

View File

@@ -46,6 +46,7 @@ fun MessageBackupsEducationScreen(
) {
Scaffolds.Settings(
onNavigationClick = onNavigationClick,
navigationContentDescription = stringResource(android.R.string.cancel),
navigationIconPainter = painterResource(id = R.drawable.symbol_x_24),
title = ""
) {

View File

@@ -9,6 +9,7 @@ import android.app.Activity
import android.os.Bundle
import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -20,12 +21,14 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import kotlinx.coroutines.rx3.asFlowable
import org.signal.core.util.getSerializableCompat
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
import org.thoughtcrime.securesms.components.settings.app.subscription.donate.InAppPaymentCheckoutDelegate
import org.thoughtcrime.securesms.compose.ComposeFragment
import org.thoughtcrime.securesms.compose.Nav
import org.thoughtcrime.securesms.database.InAppPaymentTable
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.util.CommunicationActions
import org.thoughtcrime.securesms.util.Util
import org.thoughtcrime.securesms.util.viewModel
@@ -36,7 +39,8 @@ class MessageBackupsFlowFragment : ComposeFragment(), InAppPaymentCheckoutDelega
companion object {
private const val TIER = "tier"
@VisibleForTesting
const val TIER = "tier"
fun create(messageBackupTier: MessageBackupTier?): MessageBackupsFlowFragment {
return MessageBackupsFlowFragment().apply {
@@ -88,7 +92,9 @@ class MessageBackupsFlowFragment : ComposeFragment(), InAppPaymentCheckoutDelega
MessageBackupsEducationScreen(
onNavigationClick = viewModel::goToPreviousStage,
onEnableBackups = viewModel::goToNextStage,
onLearnMore = {}
onLearnMore = {
CommunicationActions.openBrowserLink(requireContext(), getString(R.string.backup_support_url))
}
)
}

View File

@@ -16,6 +16,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Checkbox
import androidx.compose.material3.ExperimentalMaterial3Api
@@ -33,6 +34,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
@@ -70,6 +72,10 @@ fun MessageBackupsKeyRecordScreen(
skipPartiallyExpanded = true
)
val backupKeyString = remember(backupKey) {
backupKey.chunked(4).joinToString(" ")
}
Scaffolds.Settings(
title = "",
navigationIconPainter = painterResource(R.drawable.symbol_arrow_left_24),
@@ -82,67 +88,79 @@ fun MessageBackupsKeyRecordScreen(
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painter = painterResource(R.drawable.image_signal_backups_lock),
contentDescription = null,
LazyColumn(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.padding(top = 24.dp)
.size(80.dp)
)
Text(
text = stringResource(R.string.MessageBackupsKeyRecordScreen__record_your_backup_key),
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier.padding(top = 16.dp)
)
Text(
text = stringResource(R.string.MessageBackupsKeyRecordScreen__this_key_is_required_to_recover),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = 12.dp)
)
val backupKeyString = remember(backupKey) {
backupKey.chunked(4).joinToString(" ")
}
Box(
modifier = Modifier
.padding(top = 24.dp, bottom = 16.dp)
.background(
color = SignalTheme.colors.colorSurface1,
shape = RoundedCornerShape(10.dp)
.weight(1f)
.testTag("message-backups-key-record-screen-lazy-column")
) {
item {
Image(
painter = painterResource(R.drawable.image_signal_backups_lock),
contentDescription = null,
modifier = Modifier
.padding(top = 24.dp)
.size(80.dp)
)
.padding(24.dp)
) {
Text(
text = backupKeyString,
style = MaterialTheme.typography.bodyLarge
.copy(
fontSize = 18.sp,
fontWeight = FontWeight(400),
letterSpacing = 1.44.sp,
lineHeight = 36.sp,
textAlign = TextAlign.Center,
fontFamily = FontFamily.Monospace
)
)
}
}
Buttons.Small(
onClick = { onCopyToClipboardClick(backupKeyString) }
) {
Text(
text = stringResource(R.string.MessageBackupsKeyRecordScreen__copy_to_clipboard)
)
item {
Text(
text = stringResource(R.string.MessageBackupsKeyRecordScreen__record_your_backup_key),
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier.padding(top = 16.dp)
)
}
item {
Text(
text = stringResource(R.string.MessageBackupsKeyRecordScreen__this_key_is_required_to_recover),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = 12.dp)
)
}
item {
Box(
modifier = Modifier
.padding(top = 24.dp, bottom = 16.dp)
.background(
color = SignalTheme.colors.colorSurface1,
shape = RoundedCornerShape(10.dp)
)
.padding(24.dp)
) {
Text(
text = backupKeyString,
style = MaterialTheme.typography.bodyLarge
.copy(
fontSize = 18.sp,
fontWeight = FontWeight(400),
letterSpacing = 1.44.sp,
lineHeight = 36.sp,
textAlign = TextAlign.Center,
fontFamily = FontFamily.Monospace
)
)
}
}
item {
Buttons.Small(
onClick = { onCopyToClipboardClick(backupKeyString) }
) {
Text(
text = stringResource(R.string.MessageBackupsKeyRecordScreen__copy_to_clipboard)
)
}
}
}
Box(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.padding(bottom = 24.dp)
) {
Buttons.LargeTonal(
@@ -189,66 +207,80 @@ private fun BottomSheetContent(
) {
var checked by remember { mutableStateOf(false) }
Column(
LazyColumn(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = dimensionResource(CoreUiR.dimen.gutter))
.testTag("message-backups-key-record-screen-sheet-content")
) {
BottomSheets.Handle()
Text(
text = stringResource(R.string.MessageBackupsKeyRecordScreen__keep_your_key_safe),
style = MaterialTheme.typography.titleLarge,
textAlign = TextAlign.Center,
modifier = Modifier.padding(top = 30.dp)
)
Text(
text = stringResource(R.string.MessageBackupsKeyRecordScreen__signal_will_not),
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant,
textAlign = TextAlign.Center,
modifier = Modifier.padding(top = 12.dp)
)
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(vertical = 24.dp)
.defaultMinSize(minWidth = 220.dp)
.clip(shape = RoundedCornerShape(percent = 50))
.clickable(onClick = { checked = !checked })
) {
Checkbox(
checked = checked,
onCheckedChange = { checked = it }
)
item {
BottomSheets.Handle()
}
item {
Text(
text = stringResource(R.string.MessageBackupsKeyRecordScreen__ive_recorded_my_key),
style = MaterialTheme.typography.bodyLarge
text = stringResource(R.string.MessageBackupsKeyRecordScreen__keep_your_key_safe),
style = MaterialTheme.typography.titleLarge,
textAlign = TextAlign.Center,
modifier = Modifier.padding(top = 30.dp)
)
}
Buttons.LargeTonal(
enabled = checked,
onClick = onContinueClick,
modifier = Modifier
.padding(bottom = 16.dp)
.defaultMinSize(minWidth = 220.dp)
) {
Text(text = stringResource(R.string.MessageBackupsKeyRecordScreen__continue))
item {
Text(
text = stringResource(R.string.MessageBackupsKeyRecordScreen__signal_will_not),
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant,
textAlign = TextAlign.Center,
modifier = Modifier.padding(top = 12.dp)
)
}
TextButton(
onClick = onSeeKeyAgainClick,
modifier = Modifier
.padding(bottom = 24.dp)
.defaultMinSize(minWidth = 220.dp)
) {
Text(
text = stringResource(R.string.MessageBackupsKeyRecordScreen__see_key_again)
)
item {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(vertical = 24.dp)
.defaultMinSize(minWidth = 220.dp)
.clip(shape = RoundedCornerShape(percent = 50))
.clickable(onClick = { checked = !checked })
) {
Checkbox(
checked = checked,
onCheckedChange = { checked = it }
)
Text(
text = stringResource(R.string.MessageBackupsKeyRecordScreen__ive_recorded_my_key),
style = MaterialTheme.typography.bodyLarge
)
}
}
item {
Buttons.LargeTonal(
enabled = checked,
onClick = onContinueClick,
modifier = Modifier
.padding(bottom = 16.dp)
.defaultMinSize(minWidth = 220.dp)
) {
Text(text = stringResource(R.string.MessageBackupsKeyRecordScreen__continue))
}
}
item {
TextButton(
onClick = onSeeKeyAgainClick,
modifier = Modifier
.padding(bottom = 24.dp)
.defaultMinSize(minWidth = 220.dp)
) {
Text(
text = stringResource(R.string.MessageBackupsKeyRecordScreen__see_key_again)
)
}
}
}
}

View File

@@ -31,6 +31,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.pluralStringResource
@@ -93,6 +94,7 @@ fun MessageBackupsTypeSelectionScreen(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.testTag("message-backups-type-selection-screen-lazy-column")
) {
item {
Image(

View File

@@ -5,9 +5,8 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.fragment.compose.content
import org.signal.core.ui.theme.SignalTheme
import org.thoughtcrime.securesms.LoggingFragment
import org.thoughtcrime.securesms.util.DynamicTheme
@@ -16,16 +15,11 @@ import org.thoughtcrime.securesms.util.DynamicTheme
* Generic ComposeFragment which can be subclassed to build UI with compose.
*/
abstract class ComposeFragment : LoggingFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
SignalTheme(
isDarkMode = DynamicTheme.isDarkTheme(LocalContext.current)
) {
FragmentContent()
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = content {
SignalTheme(
isDarkMode = DynamicTheme.isDarkTheme(LocalContext.current)
) {
FragmentContent()
}
}