Ensure consistent regV5 footer padding and elevation.

This commit is contained in:
jeffrey-signal
2026-05-27 16:01:25 -04:00
committed by Michelle Tang
parent 2cb9685024
commit 5b1f5a2a20
9 changed files with 239 additions and 118 deletions
@@ -16,6 +16,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -131,6 +132,19 @@ object RegistrationScaffold {
WindowBreakpoint.LARGE_HEIGHT -> largeHeightLayoutParams
}
}
@Composable
fun FooterSurface(
isContentScrolledUnder: Boolean,
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
Surface(
modifier = modifier.fillMaxWidth(),
shadowElevation = if (isContentScrolledUnder) 8.dp else 0.dp,
content = content
)
}
}
/**
@@ -83,17 +83,26 @@ private fun OnePaneLayout(
}
},
footer = {
Column(
modifier = modifier
.fillMaxWidth()
.padding(vertical = 16.dp)
.horizontalGutters(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = scrollState.canScrollForward
) {
NextButton(onEvent, modifier = Modifier.widthIn(max = params.maxButtonWidth).fillMaxWidth())
Spacer(modifier = Modifier.height(16.dp))
LearnMore(onEvent)
Column(
modifier = modifier
.fillMaxWidth()
.padding(vertical = 16.dp)
.horizontalGutters(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
NextButton(
onEvent,
modifier = Modifier
.widthIn(max = params.maxButtonWidth)
.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
LearnMore(onEvent)
}
}
}
)
@@ -136,17 +145,21 @@ private fun TwoPaneLayout(
}
},
footer = {
Row(
modifier = modifier
.padding(vertical = 16.dp)
.fillMaxWidth()
.horizontalGutters(),
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward
) {
LearnMore(onEvent)
Spacer(modifier = Modifier.size(16.dp))
NextButton(onEvent, modifier = Modifier.widthIn(max = params.maxButtonWidth))
Row(
modifier = modifier
.padding(vertical = 16.dp)
.fillMaxWidth()
.horizontalGutters(),
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically
) {
LearnMore(onEvent)
Spacer(modifier = Modifier.size(16.dp))
NextButton(onEvent, modifier = Modifier.widthIn(max = params.maxButtonWidth))
}
}
}
)
@@ -157,7 +170,9 @@ private fun Title() {
Text(
text = stringResource(R.string.AccountLockedScreen__account_locked),
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier.fillMaxWidth().attachDebugLogHelper()
modifier = Modifier
.fillMaxWidth()
.attachDebugLogHelper()
)
}
@@ -95,21 +95,27 @@ private fun OnePaneLayout(
}
},
footer = {
Row(
modifier = Modifier.fillMaxWidth().padding(16.dp),
horizontalArrangement = Arrangement.SpaceEvenly
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = scrollState.canScrollForward
) {
Box(
modifier = Modifier.weight(1f),
contentAlignment = Alignment.CenterStart
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceEvenly
) {
NoRecoverKeyButton(onEvent)
}
Box(
modifier = Modifier.weight(1f),
contentAlignment = Alignment.CenterEnd
) {
NextButton(state, onEvent)
Box(
modifier = Modifier.weight(1f),
contentAlignment = Alignment.CenterStart
) {
NoRecoverKeyButton(onEvent)
}
Box(
modifier = Modifier.weight(1f),
contentAlignment = Alignment.CenterEnd
) {
NextButton(state, onEvent)
}
}
}
}
@@ -150,13 +156,19 @@ private fun TwoPaneLayout(
}
},
footer = {
Row(
horizontalArrangement = Arrangement.End,
modifier = Modifier.fillMaxWidth().padding(16.dp)
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = scrollState.canScrollForward
) {
NoRecoverKeyButton(onEvent)
Spacer(modifier = Modifier.size(24.dp))
NextButton(state, onEvent)
Row(
horizontalArrangement = Arrangement.End,
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
NoRecoverKeyButton(onEvent)
Spacer(modifier = Modifier.size(24.dp))
NextButton(state, onEvent)
}
}
}
)
@@ -167,7 +179,9 @@ private fun Description() {
Text(
text = stringResource(R.string.EnterAepScreen__enter_your_recovery_key),
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier.fillMaxWidth().attachDebugLogHelper()
modifier = Modifier
.fillMaxWidth()
.attachDebugLogHelper()
)
Spacer(modifier = Modifier.size(8.dp))
@@ -62,18 +62,22 @@ internal fun LocalBackupRestoreLayout(
}
},
footer = {
if (primaryButton != null || secondaryButton != null) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp, vertical = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
primaryButton?.invoke(Modifier.fillMaxWidth())
if (secondaryButton != null) {
Spacer(modifier = Modifier.height(8.dp))
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = scrollState.canScrollForward
) {
if (primaryButton != null || secondaryButton != null) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp, vertical = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
primaryButton?.invoke(Modifier.fillMaxWidth())
if (secondaryButton != null) {
Spacer(modifier = Modifier.height(8.dp))
}
secondaryButton?.invoke(Modifier.fillMaxWidth())
}
secondaryButton?.invoke(Modifier.fillMaxWidth())
}
}
}
@@ -112,19 +116,23 @@ internal fun LocalBackupRestoreLayout(
}
},
footer = {
if (primaryButton != null || secondaryButton != null) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp, vertical = 16.dp),
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically
) {
secondaryButton?.invoke(Modifier)
if (secondaryButton != null) {
Spacer(modifier = Modifier.width(16.dp))
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward
) {
if (primaryButton != null || secondaryButton != null) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp, vertical = 16.dp),
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically
) {
secondaryButton?.invoke(Modifier)
if (secondaryButton != null) {
Spacer(modifier = Modifier.width(16.dp))
}
primaryButton?.invoke(Modifier)
}
primaryButton?.invoke(Modifier)
}
}
}
@@ -138,7 +146,9 @@ internal fun Description(headline: String, body: String) {
Text(
text = headline,
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier.fillMaxWidth().attachDebugLogHelper()
modifier = Modifier
.fillMaxWidth()
.attachDebugLogHelper()
)
Spacer(modifier = Modifier.height(8.dp))
@@ -215,7 +215,11 @@ private fun OnePaneLayout(
}
},
footer = {
NextButton(state, onEvent)
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = scrollState.canScrollForward
) {
NextButton(state, onEvent)
}
}
)
}
@@ -276,7 +280,11 @@ private fun TwoPaneLayout(
}
},
footer = {
NextButton(state, onEvent)
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = scrollState.canScrollForward
) {
NextButton(state, onEvent)
}
}
)
}
@@ -286,7 +294,9 @@ private fun Description() {
Text(
text = stringResource(R.string.RegistrationActivity_phone_number),
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier.fillMaxWidth().attachDebugLogHelper()
modifier = Modifier
.fillMaxWidth()
.attachDebugLogHelper()
)
Spacer(modifier = Modifier.height(16.dp))
@@ -96,7 +96,11 @@ private fun OnePaneLayout(
}
},
footer = {
CancelFooter(onEvent)
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = scrollState.canScrollForward
) {
CancelFooter(onEvent)
}
}
)
}
@@ -140,7 +144,11 @@ private fun TwoPaneLayout(
}
},
footer = {
CancelFooter(onEvent)
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward
) {
CancelFooter(onEvent)
}
}
)
}
@@ -151,7 +159,9 @@ private fun Heading() {
text = stringResource(R.string.QuickRestoreQRScreen__scan),
style = MaterialTheme.typography.headlineMedium,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth().attachDebugLogHelper()
modifier = Modifier
.fillMaxWidth()
.attachDebugLogHelper()
)
}
@@ -103,6 +103,8 @@ private fun OnePaneLayout(
onEvent: (RemoteBackupRestoreScreenEvents) -> Unit,
modifier: Modifier = Modifier
) {
val scrollState = rememberScrollState()
OnePaneRegistrationScaffold(
modifier = modifier,
params = params,
@@ -111,7 +113,7 @@ private fun OnePaneLayout(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.verticalScroll(scrollState)
.padding(paddingValues)
) {
BackupInfoContent(state = state)
@@ -120,13 +122,17 @@ private fun OnePaneLayout(
RestoreStateDialogs(state = state, onEvent = onEvent)
},
footer = {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(24.dp)
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = scrollState.canScrollForward
) {
RestoreButton(onEvent, Modifier.fillMaxWidth())
CancelButton(onEvent, Modifier.fillMaxWidth())
Column(
modifier = Modifier
.fillMaxWidth()
.padding(24.dp)
) {
RestoreButton(onEvent, Modifier.fillMaxWidth())
CancelButton(onEvent, Modifier.fillMaxWidth())
}
}
}
)
@@ -139,6 +145,9 @@ private fun TwoPaneLayout(
onEvent: (RemoteBackupRestoreScreenEvents) -> Unit,
modifier: Modifier = Modifier
) {
val firstPaneScrollState = rememberScrollState()
val secondPaneScrollState = rememberScrollState()
TwoPaneRegistrationScaffold(
modifier = modifier,
params = params,
@@ -148,7 +157,7 @@ private fun TwoPaneLayout(
modifier = Modifier
.weight(1f)
.fillMaxHeight()
.verticalScroll(rememberScrollState())
.verticalScroll(firstPaneScrollState)
.padding(paddingValues)
) {
BackupInfoHeading()
@@ -160,7 +169,7 @@ private fun TwoPaneLayout(
modifier = Modifier
.weight(1f)
.fillMaxHeight()
.verticalScroll(rememberScrollState())
.verticalScroll(secondPaneScrollState)
.padding(paddingValues)
) {
BackupInfoDetails(state = state)
@@ -169,15 +178,19 @@ private fun TwoPaneLayout(
RestoreStateDialogs(state = state, onEvent = onEvent)
},
footer = {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(24.dp),
horizontalArrangement = Arrangement.End
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward
) {
CancelButton(onEvent, Modifier)
Spacer(modifier = Modifier.size(8.dp))
RestoreButton(onEvent, Modifier)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(24.dp),
horizontalArrangement = Arrangement.End
) {
CancelButton(onEvent, Modifier)
Spacer(modifier = Modifier.size(8.dp))
RestoreButton(onEvent, Modifier)
}
}
}
)
@@ -209,7 +222,9 @@ private fun BackupInfoHeading() {
text = stringResource(R.string.RemoteRestoreScreen__restore_from_backup),
style = MaterialTheme.typography.headlineMedium,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth().attachDebugLogHelper()
modifier = Modifier
.fillMaxWidth()
.attachDebugLogHelper()
)
}
@@ -282,6 +297,7 @@ private fun RestoreStateDialogs(
RemoteBackupRestoreState.RestoreState.InProgress -> {
RestoreProgressDialog(restoreProgress = state.restoreProgress)
}
RemoteBackupRestoreState.RestoreState.Restored -> Unit
RemoteBackupRestoreState.RestoreState.NetworkFailure -> {
Dialogs.SimpleAlertDialog(
@@ -292,6 +308,7 @@ private fun RestoreStateDialogs(
onDismiss = { onEvent(RemoteBackupRestoreScreenEvents.DismissError) }
)
}
RemoteBackupRestoreState.RestoreState.InvalidBackupVersion -> {
Dialogs.SimpleAlertDialog(
title = stringResource(R.string.RemoteRestoreScreen__couldnt_restore_this_backup),
@@ -302,6 +319,7 @@ private fun RestoreStateDialogs(
onDismiss = { onEvent(RemoteBackupRestoreScreenEvents.DismissError) }
)
}
RemoteBackupRestoreState.RestoreState.PermanentSvrBFailure -> {
Dialogs.SimpleAlertDialog(
title = stringResource(R.string.RemoteRestoreScreen__cant_restore_this_backup),
@@ -312,6 +330,7 @@ private fun RestoreStateDialogs(
onDismiss = { onEvent(RemoteBackupRestoreScreenEvents.DismissError) }
)
}
RemoteBackupRestoreState.RestoreState.Failed -> {
Dialogs.SimpleAlertDialog(
title = stringResource(R.string.RemoteRestoreScreen__couldnt_finish_restore),
@@ -97,12 +97,18 @@ private fun OnePaneLayout(
}
},
footer = {
if (state.showSkipButton) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
SkipRestoreButton(onEvent)
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = scrollState.canScrollForward
) {
if (state.showSkipButton) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(params.footerPadding),
horizontalArrangement = Arrangement.Center
) {
SkipRestoreButton(onEvent)
}
}
}
}
@@ -143,12 +149,18 @@ private fun TwoPaneLayout(
}
},
footer = {
if (state.showSkipButton) {
Row(
modifier = Modifier.fillMaxWidth().padding(end = 32.dp),
horizontalArrangement = Arrangement.End
) {
SkipRestoreButton(onEvent)
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = scrollState.canScrollForward
) {
if (state.showSkipButton) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(params.footerPadding),
horizontalArrangement = Arrangement.End
) {
SkipRestoreButton(onEvent)
}
}
}
}
@@ -160,7 +172,9 @@ private fun Description() {
Text(
text = stringResource(R.string.ArchiveRestoreSelectionScreen__restore_or_transfer_account),
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier.fillMaxWidth().attachDebugLogHelper()
modifier = Modifier
.fillMaxWidth()
.attachDebugLogHelper()
)
Spacer(modifier = Modifier.height(8.dp))
@@ -191,7 +205,6 @@ private fun SkipRestoreButton(onEvent: (ArchiveRestoreSelectionScreenEvents) ->
TextButton(
onClick = { onEvent(ArchiveRestoreSelectionScreenEvents.Skip) },
modifier = Modifier
.padding(bottom = 32.dp)
.testTag(TestTags.ARCHIVE_RESTORE_SELECTION_SKIP)
) {
Text(
@@ -217,6 +230,7 @@ private fun RestoreOptionCard(
modifier = modifier.testTag(TestTags.ARCHIVE_RESTORE_SELECTION_FROM_SIGNAL_BACKUPS)
)
}
ArchiveRestoreOption.DeviceTransfer -> {
SelectionCard(
imageVector = ImageVector.vectorResource(R.drawable.symbol_transfer_24),
@@ -226,6 +240,7 @@ private fun RestoreOptionCard(
modifier = modifier.testTag(TestTags.ARCHIVE_RESTORE_SELECTION_DEVICE_TRANSFER)
)
}
ArchiveRestoreOption.LocalBackup -> {
SelectionCard(
imageVector = ImageVector.vectorResource(R.drawable.symbol_folder_24),
@@ -101,21 +101,27 @@ fun VerificationCodeScreen(
focusRequesters[0].requestFocus()
snackbarHostState.showSnackbar(resources.getString(R.string.VerificationCodeScreen__incorrect_code))
}
VerificationCodeState.OneTimeEvent.NetworkError -> {
snackbarHostState.showSnackbar(resources.getString(R.string.VerificationCodeScreen__network_error))
}
is VerificationCodeState.OneTimeEvent.RateLimited -> {
snackbarHostState.showSnackbar(resources.getString(R.string.VerificationCodeScreen__too_many_attempts_try_again_in_s, event.retryAfter.toString()))
}
VerificationCodeState.OneTimeEvent.UnableToSendSms -> {
snackbarHostState.showSnackbar(resources.getString(R.string.VerificationCodeScreen__unable_to_send_sms))
}
VerificationCodeState.OneTimeEvent.CouldNotRequestCodeWithSelectedTransport -> {
snackbarHostState.showSnackbar(resources.getString(R.string.VerificationCodeScreen__could_not_send_code_via_selected_method))
}
VerificationCodeState.OneTimeEvent.UnknownError -> {
snackbarHostState.showSnackbar(resources.getString(R.string.VerificationCodeScreen__an_unexpected_error_occurred))
}
VerificationCodeState.OneTimeEvent.RegistrationError -> {
snackbarHostState.showSnackbar(resources.getString(R.string.VerificationCodeScreen__registration_error))
}
@@ -199,14 +205,17 @@ private fun OnePaneLayout(
}
},
footer = {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 16.dp),
horizontalArrangement = Arrangement.SpaceAround,
verticalAlignment = Alignment.Bottom
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = scrollState.canScrollForward
) {
AlternateCodeOptions(state, onEvent)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(params.footerPadding),
horizontalArrangement = Arrangement.SpaceAround
) {
AlternateCodeOptions(state, onEvent)
}
}
}
)
@@ -260,14 +269,17 @@ private fun TwoPaneLayout(
}
},
footer = {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 16.dp, start = 24.dp, end = 24.dp),
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.Bottom
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = scrollState.canScrollForward
) {
AlternateCodeOptions(state, onEvent)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(params.footerPadding),
horizontalArrangement = Arrangement.End
) {
AlternateCodeOptions(state, onEvent)
}
}
}
)
@@ -431,7 +443,9 @@ private fun Description(state: VerificationCodeState, onEvent: (VerificationCode
Text(
text = stringResource(R.string.VerificationCodeScreen__verification_code),
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier.fillMaxWidth().attachDebugLogHelper()
modifier = Modifier
.fillMaxWidth()
.attachDebugLogHelper()
)
Spacer(modifier = Modifier.height(16.dp))