Elevate RegistrationScaffold footer surface when content scrolls underneath.

This commit is contained in:
jeffrey-signal
2026-06-03 07:12:56 -04:00
committed by Cody Henthorne
parent f3a5bba3f2
commit 4c942f39b0
17 changed files with 184 additions and 132 deletions
@@ -154,13 +154,13 @@ object RegistrationScaffold {
@Composable
fun FooterSurface(
isContentScrolledUnder: Boolean,
isElevated: Boolean,
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
Surface(
modifier = modifier.fillMaxWidth(),
shadowElevation = if (isContentScrolledUnder) 8.dp else 0.dp,
shadowElevation = if (isElevated) 8.dp else 0.dp,
content = content
)
}
@@ -84,7 +84,7 @@ private fun OnePaneLayout(
},
footer = {
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = scrollState.canScrollForward
isElevated = scrollState.canScrollForward
) {
Column(
modifier = modifier
@@ -146,7 +146,7 @@ private fun TwoPaneLayout(
},
footer = {
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward
isElevated = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward
) {
Row(
modifier = modifier
@@ -97,7 +97,7 @@ private fun OnePaneLayout(
},
footer = {
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = scrollState.canScrollForward
isElevated = scrollState.canScrollForward
) {
Row(
modifier = Modifier
@@ -160,7 +160,7 @@ private fun TwoPaneLayout(
},
footer = {
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward
isElevated = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward
) {
Row(
horizontalArrangement = Arrangement.End,
@@ -63,23 +63,25 @@ private fun OnePane(params: RegistrationScaffold.Params.OnePane, permissionState
OnePaneRegistrationScaffold(
params = params,
content = { paddingValues ->
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.verticalScroll(scrollState)
.padding(paddingValues)
) {
FirstPaneContent()
SecondPaneContent()
}
},
footer = {
FooterContent(
permissionState = permissionState,
isElevated = scrollState.canScrollForward,
onProceed = onProceed
)
}
) { paddingValues ->
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.verticalScroll(scrollState)
.padding(paddingValues)
) {
FirstPaneContent()
SecondPaneContent()
}
}
)
}
@OptIn(ExperimentalPermissionsApi::class)
@@ -111,6 +113,7 @@ private fun TwoPane(params: RegistrationScaffold.Params.TwoPane, permissionState
footer = {
FooterContent(
permissionState = permissionState,
isElevated = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward,
onProceed = onProceed
)
}
@@ -151,32 +154,37 @@ private fun SecondPaneContent(
@Composable
private fun FooterContent(
permissionState: PermissionState,
isElevated: Boolean,
onProceed: () -> Unit
) {
Row(
horizontalArrangement = spacedBy(56.dp),
modifier = Modifier.padding(56.dp)
RegistrationScaffold.FooterSurface(
isElevated = isElevated
) {
Spacer(modifier = Modifier.weight(1f))
TextButton(
onClick = onProceed,
modifier = Modifier.testTag(TestTags.ALLOW_NOTIFICATIONS_NOT_NOW_BUTTON)
Row(
horizontalArrangement = spacedBy(56.dp),
modifier = Modifier.padding(56.dp)
) {
Text(text = stringResource(R.string.AllowNotificationsScreen__not_now))
}
Spacer(modifier = Modifier.weight(1f))
Buttons.LargeTonal(
onClick = {
if (permissionState.status.isGranted) {
onProceed()
} else {
permissionState.launchPermissionRequest()
}
},
modifier = Modifier.testTag(TestTags.ALLOW_NOTIFICATIONS_NEXT_BUTTON)
) {
Text(text = stringResource(R.string.AllowNotificationsScreen__next))
TextButton(
onClick = onProceed,
modifier = Modifier.testTag(TestTags.ALLOW_NOTIFICATIONS_NOT_NOW_BUTTON)
) {
Text(text = stringResource(R.string.AllowNotificationsScreen__not_now))
}
Buttons.LargeTonal(
onClick = {
if (permissionState.status.isGranted) {
onProceed()
} else {
permissionState.launchPermissionRequest()
}
},
modifier = Modifier.testTag(TestTags.ALLOW_NOTIFICATIONS_NEXT_BUTTON)
) {
Text(text = stringResource(R.string.AllowNotificationsScreen__next))
}
}
}
}
@@ -107,6 +107,7 @@ fun CaptchaScreen(
CircularProgressIndicator(modifier = Modifier.size(48.dp))
}
}
CaptchaLoadState.Error -> {
Box(
modifier = Modifier.fillMaxSize(),
@@ -117,22 +117,28 @@ private fun OnePane(
OnePaneRegistrationScaffold(
params = params,
footer = { OnePaneFooterContent(onEvent = onEvent) }
) { paddingValues ->
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = spacedBy(64.dp),
modifier = Modifier
.padding(paddingValues)
.verticalScroll(scrollState)
) {
Title()
content = { paddingValues ->
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = spacedBy(64.dp),
modifier = Modifier
.padding(paddingValues)
.verticalScroll(scrollState)
) {
Title()
QrCodeContent(state = state, onEvent = onEvent)
QrCodeContent(state = state, onEvent = onEvent)
Steps(verticalArrangement = spacedBy(32.dp), onEvent)
Steps(verticalArrangement = spacedBy(32.dp), onEvent)
}
},
footer = {
OnePaneFooterContent(
isElevated = scrollState.canScrollForward,
onEvent = onEvent
)
}
}
)
}
@Composable
@@ -160,7 +166,12 @@ private fun TwoPane(
.padding(paddingValues)
)
},
footer = { TwoPaneFooterContent(onEvent = onEvent) }
footer = {
TwoPaneFooterContent(
isElevated = false,
onEvent = onEvent
)
}
)
}
@@ -454,37 +465,47 @@ fun getQrCodeSize(isInOverlay: Boolean): Dp {
@Composable
private fun OnePaneFooterContent(
isElevated: Boolean,
onEvent: (LinkAccountScreenEvent) -> Unit,
modifier: Modifier = Modifier
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier
.padding(start = 36.dp, end = 36.dp, bottom = 16.dp)
.fillMaxWidth()
RegistrationScaffold.FooterSurface(
isElevated = isElevated
) {
Row {
DontHaveSignal()
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier
.padding(start = 36.dp, end = 36.dp, bottom = 16.dp)
.fillMaxWidth()
) {
Row {
DontHaveSignal()
}
CreateAccount(onEvent)
}
CreateAccount(onEvent)
}
}
@Composable
private fun TwoPaneFooterContent(
isElevated: Boolean,
onEvent: (LinkAccountScreenEvent) -> Unit,
modifier: Modifier = Modifier
) {
Row(
horizontalArrangement = spacedBy(8.dp),
modifier = modifier.padding(36.dp)
RegistrationScaffold.FooterSurface(
isElevated = isElevated
) {
Spacer(modifier = Modifier.weight(1f))
Row(
horizontalArrangement = spacedBy(8.dp),
modifier = modifier.padding(36.dp)
) {
Spacer(modifier = Modifier.weight(1f))
DontHaveSignal()
CreateAccount(onEvent)
DontHaveSignal()
CreateAccount(onEvent)
Spacer(modifier = Modifier.weight(1f))
Spacer(modifier = Modifier.weight(1f))
}
}
}
@@ -130,6 +130,7 @@ private fun OnePaneLayout(
FooterButtons(
isValid = isValid,
passphrase = passphrase,
isElevated = scrollState.canScrollForward,
onSubmit = onSubmit,
onCancel = onCancel
)
@@ -186,6 +187,7 @@ private fun TwoPaneLayout(
FooterButtons(
isValid = isValid,
passphrase = passphrase,
isElevated = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward,
onSubmit = onSubmit,
onCancel = onCancel
)
@@ -276,30 +278,35 @@ private fun PassphraseTextField(
private fun FooterButtons(
isValid: Boolean,
passphrase: String,
isElevated: Boolean,
onSubmit: (String) -> Unit,
onCancel: () -> Unit
) {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp, vertical = 16.dp)
RegistrationScaffold.FooterSurface(
isElevated = isElevated
) {
TextButton(
modifier = Modifier.weight(weight = 1f, fill = false),
onClick = onCancel,
shape = RoundedCornerShape(0.dp)
Row(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp, vertical = 16.dp)
) {
Text(text = stringResource(R.string.LocalBackupRestoreScreen__no_passphrase))
}
TextButton(
modifier = Modifier.weight(weight = 1f, fill = false),
onClick = onCancel,
shape = RoundedCornerShape(0.dp)
) {
Text(text = stringResource(R.string.LocalBackupRestoreScreen__no_passphrase))
}
Spacer(modifier = Modifier.size(24.dp))
Spacer(modifier = Modifier.size(24.dp))
Buttons.LargeTonal(
enabled = isValid,
onClick = { onSubmit(passphrase) }
) {
Text(text = stringResource(R.string.LocalBackupRestoreScreen__next))
Buttons.LargeTonal(
enabled = isValid,
onClick = { onSubmit(passphrase) }
) {
Text(text = stringResource(R.string.LocalBackupRestoreScreen__next))
}
}
}
}
@@ -64,7 +64,7 @@ internal fun LocalBackupRestoreLayout(
},
footer = {
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = scrollState.canScrollForward
isElevated = scrollState.canScrollForward
) {
if (primaryButton != null || secondaryButton != null) {
Column(
@@ -119,7 +119,7 @@ internal fun LocalBackupRestoreLayout(
},
footer = {
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward
isElevated = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward
) {
if (primaryButton != null || secondaryButton != null) {
Row(
@@ -75,18 +75,25 @@ private fun OnePane(params: RegistrationScaffold.Params.OnePane, state: MessageS
OnePaneRegistrationScaffold(
params = params,
footer = { FooterContent(params = params, onEvent = onEvent) }
) { paddingValues ->
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.verticalScroll(scrollState)
.padding(paddingValues)
) {
FirstPaneContent(state)
SecondPaneContent()
content = { paddingValues ->
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.verticalScroll(scrollState)
.padding(paddingValues)
) {
FirstPaneContent(state)
SecondPaneContent()
}
},
footer = {
FooterContent(
params = params,
isElevated = scrollState.canScrollForward,
onEvent = onEvent
)
}
}
)
}
@Composable
@@ -114,7 +121,13 @@ private fun TwoPane(params: RegistrationScaffold.Params.TwoPane, state: MessageS
.padding(paddingValues)
)
},
footer = { FooterContent(params = params, onEvent = onEvent) }
footer = {
FooterContent(
params = params,
isElevated = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward,
onEvent = onEvent
)
}
)
}
@@ -180,15 +193,20 @@ private fun SecondPaneContent(
@Composable
private fun FooterContent(
params: RegistrationScaffold.Params,
isElevated: Boolean,
onEvent: (MessageSyncScreenEvent) -> Unit,
modifier: Modifier = Modifier
) {
val breakpoint = rememberWindowBreakpoint()
when (breakpoint) {
is WindowBreakpoint.Small -> StackedFooter(params, modifier, onEvent)
is WindowBreakpoint.Medium -> StackedFooter(params, modifier, onEvent)
is WindowBreakpoint.Large -> InlineFooter(modifier, onEvent)
RegistrationScaffold.FooterSurface(
isElevated = isElevated
) {
when (breakpoint) {
is WindowBreakpoint.Small -> StackedFooter(params, modifier, onEvent)
is WindowBreakpoint.Medium -> StackedFooter(params, modifier, onEvent)
is WindowBreakpoint.Large -> InlineFooter(modifier, onEvent)
}
}
}
@@ -133,7 +133,7 @@ private fun OnePaneLayout(
PermissionButtons(
onProceed = onProceed,
permissionsState = permissionsState,
showElevation = scrollState.canScrollForward,
isElevated = scrollState.canScrollForward,
modifier = Modifier.padding(params.footerPadding)
)
}
@@ -193,7 +193,7 @@ private fun TwoPaneLayout(
PermissionButtons(
onProceed = onProceed,
permissionsState = permissionsState,
showElevation = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward,
isElevated = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward,
modifier = Modifier.padding(params.footerPadding)
)
}
@@ -277,12 +277,11 @@ private fun PermissionRow(
private fun PermissionButtons(
onProceed: () -> Unit,
permissionsState: MultiplePermissionsState,
showElevation: Boolean,
isElevated: Boolean,
modifier: Modifier = Modifier
) {
Surface(
modifier = Modifier.fillMaxWidth(),
shadowElevation = if (showElevation) 8.dp else 0.dp
RegistrationScaffold.FooterSurface(
isElevated = isElevated
) {
Row(
horizontalArrangement = Arrangement.End,
@@ -177,7 +177,7 @@ private fun OnePaneLayout(
},
footer = {
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = scrollState.canScrollForward
isElevated = scrollState.canScrollForward
) {
NextButton(state, onEvent)
}
@@ -245,7 +245,7 @@ private fun TwoPaneLayout(
},
footer = {
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward
isElevated = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward
) {
NextButton(state, onEvent)
}
@@ -22,7 +22,6 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TextField
@@ -152,7 +151,7 @@ private fun OnePaneLayout(
NextButton(
params = params,
canSubmitPin = canSubmitPin,
showElevation = scrollState.canScrollForward,
isElevated = scrollState.canScrollForward,
onNext = { onEvent(PinCreationScreenEvents.PinSubmitted(pin)) }
)
}
@@ -221,7 +220,7 @@ private fun TwoPaneLayout(
NextButton(
params = params,
canSubmitPin = canSubmitPin,
showElevation = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward,
isElevated = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward,
onNext = { onEvent(PinCreationScreenEvents.PinSubmitted(pin)) }
)
}
@@ -362,13 +361,13 @@ private fun KeyboardToggleButton(
private fun NextButton(
params: RegistrationScaffold.Params,
canSubmitPin: Boolean,
showElevation: Boolean,
isElevated: Boolean,
onNext: () -> Unit,
modifier: Modifier = Modifier
) {
Surface(
modifier = modifier.fillMaxWidth(),
shadowElevation = if (showElevation) 8.dp else 0.dp
RegistrationScaffold.FooterSurface(
isElevated = isElevated,
modifier = modifier
) {
Row(
horizontalArrangement = Arrangement.End,
@@ -23,7 +23,6 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TextField
@@ -157,7 +156,7 @@ private fun OnePaneLayout(
ContinueButton(
params = params,
canSubmitPin = canSubmitPin,
showElevation = scrollState.canScrollForward,
isElevated = scrollState.canScrollForward,
onContinue = { onEvent(PinEntryScreenEvents.PinEntered(pin)) }
)
}
@@ -234,7 +233,7 @@ private fun TwoPaneLayout(
ContinueButton(
params = params,
canSubmitPin = canSubmitPin,
showElevation = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward,
isElevated = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward,
onContinue = { onEvent(PinEntryScreenEvents.PinEntered(pin)) }
)
}
@@ -361,13 +360,13 @@ private fun KeyboardToggleButton(
private fun ContinueButton(
params: RegistrationScaffold.Params,
canSubmitPin: Boolean,
showElevation: Boolean,
isElevated: Boolean,
onContinue: () -> Unit,
modifier: Modifier = Modifier
) {
Surface(
modifier = modifier.fillMaxWidth(),
shadowElevation = if (showElevation) 8.dp else 0.dp
RegistrationScaffold.FooterSurface(
isElevated = isElevated,
modifier = modifier
) {
Row(
horizontalArrangement = Arrangement.End,
@@ -97,7 +97,7 @@ private fun OnePaneLayout(
},
footer = {
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = scrollState.canScrollForward
isElevated = scrollState.canScrollForward
) {
CancelFooter(onEvent)
}
@@ -145,7 +145,7 @@ private fun TwoPaneLayout(
},
footer = {
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward
isElevated = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward
) {
CancelFooter(onEvent)
}
@@ -123,7 +123,7 @@ private fun OnePaneLayout(
},
footer = {
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = scrollState.canScrollForward
isElevated = scrollState.canScrollForward
) {
Column(
modifier = Modifier
@@ -179,7 +179,7 @@ private fun TwoPaneLayout(
},
footer = {
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward
isElevated = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward
) {
Row(
modifier = Modifier
@@ -98,7 +98,7 @@ private fun OnePaneLayout(
},
footer = {
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = scrollState.canScrollForward
isElevated = scrollState.canScrollForward
) {
if (state.showSkipButton) {
Row(
@@ -152,7 +152,7 @@ private fun TwoPaneLayout(
},
footer = {
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward
isElevated = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward
) {
if (state.showSkipButton) {
Row(
@@ -206,7 +206,7 @@ private fun OnePaneLayout(
},
footer = {
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = scrollState.canScrollForward
isElevated = scrollState.canScrollForward
) {
Row(
modifier = Modifier
@@ -272,7 +272,7 @@ private fun TwoPaneLayout(
},
footer = {
RegistrationScaffold.FooterSurface(
isContentScrolledUnder = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward
isElevated = firstPaneScrollState.canScrollForward || secondPaneScrollState.canScrollForward
) {
Row(
modifier = Modifier