Introduce HeaderFooterPage.

This commit is contained in:
Benoit Marty 2023-06-05 13:01:16 +02:00
parent 32026ea5fb
commit 2f1b82ef12
2 changed files with 132 additions and 36 deletions

View file

@ -21,10 +21,10 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.widthIn
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
@ -34,24 +34,21 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.ElementTextStyles import io.element.android.libraries.designsystem.ElementTextStyles
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
import io.element.android.libraries.designsystem.components.button.ButtonWithProgress import io.element.android.libraries.designsystem.components.button.ButtonWithProgress
import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
import io.element.android.libraries.designsystem.theme.components.Surface
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.matrix.api.verification.VerificationEmoji import io.element.android.libraries.matrix.api.verification.VerificationEmoji
import io.element.android.features.verifysession.impl.VerifySelfSessionState.VerificationStep as FlowStep import io.element.android.features.verifysession.impl.VerifySelfSessionState.VerificationStep as FlowStep
@ -77,24 +74,18 @@ fun VerifySelfSessionView(
val buttonsVisible by remember(verificationFlowStep) { val buttonsVisible by remember(verificationFlowStep) {
derivedStateOf { verificationFlowStep != FlowStep.AwaitingOtherDeviceResponse && verificationFlowStep != FlowStep.Completed } derivedStateOf { verificationFlowStep != FlowStep.AwaitingOtherDeviceResponse && verificationFlowStep != FlowStep.Completed }
} }
Surface { HeaderFooterPage(
Column( modifier = modifier,
modifier = modifier header = {
.systemBarsPadding()
.padding(horizontal = 20.dp)
) {
Column(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
) {
HeaderContent(verificationFlowStep = verificationFlowStep) HeaderContent(verificationFlowStep = verificationFlowStep)
Content(modifier = Modifier.weight(1f), flowState = verificationFlowStep) },
} footer = {
if (buttonsVisible) { if (buttonsVisible) {
BottomMenu(screenState = state, goBack = ::goBackAndCancelIfNeeded) BottomMenu(screenState = state, goBack = ::goBackAndCancelIfNeeded)
} }
} }
) {
Content(flowState = verificationFlowStep)
} }
} }
@ -120,7 +111,7 @@ internal fun HeaderContent(verificationFlowStep: FlowStep, modifier: Modifier =
} }
IconTitleSubtitleMolecule( IconTitleSubtitleMolecule(
modifier = modifier.padding(top = 80.dp), modifier = modifier.padding(top = 60.dp),
iconResourceId = iconResourceId, iconResourceId = iconResourceId,
title = stringResource(id = titleTextId), title = stringResource(id = titleTextId),
subTitle = stringResource(id = subtitleTextId) subTitle = stringResource(id = subtitleTextId)
@ -129,14 +120,12 @@ internal fun HeaderContent(verificationFlowStep: FlowStep, modifier: Modifier =
@Composable @Composable
internal fun Content(flowState: FlowStep, modifier: Modifier = Modifier) { internal fun Content(flowState: FlowStep, modifier: Modifier = Modifier) {
Column(modifier, verticalArrangement = Arrangement.Center) { Column(modifier.fillMaxHeight(), verticalArrangement = Arrangement.Center) {
Spacer(Modifier.shrinkableHeight(min = 20.dp, max = 56.dp))
when (flowState) { when (flowState) {
FlowStep.Initial, FlowStep.Ready, FlowStep.Canceled, FlowStep.Completed -> Unit FlowStep.Initial, FlowStep.Ready, FlowStep.Canceled, FlowStep.Completed -> Unit
FlowStep.AwaitingOtherDeviceResponse -> ContentWaiting() FlowStep.AwaitingOtherDeviceResponse -> ContentWaiting()
is FlowStep.Verifying -> ContentVerifying(flowState) is FlowStep.Verifying -> ContentVerifying(flowState)
} }
Spacer(Modifier.shrinkableHeight(min = 20.dp, max = 56.dp))
} }
} }
@ -224,7 +213,7 @@ internal fun BottomMenu(screenState: VerifySelfSessionState, goBack: () -> Unit)
} }
ButtonColumnMolecule( ButtonColumnMolecule(
modifier = Modifier.padding(bottom = 40.dp) modifier = Modifier.padding(bottom = 20.dp)
) { ) {
ButtonWithProgress( ButtonWithProgress(
text = positiveButtonTitle?.let { stringResource(it) }, text = positiveButtonTitle?.let { stringResource(it) },
@ -261,15 +250,3 @@ private fun ContentToPreview(state: VerifySelfSessionState) {
goBack = {}, goBack = {},
) )
} }
private fun Modifier.shrinkableHeight(
min: Dp,
max: Dp,
minScreenHeight: Int = 720
): Modifier = composed {
if (LocalConfiguration.current.screenHeightDp >= minScreenHeight) {
then(Modifier.height(max))
} else {
then(Modifier.height(min))
}
}

View file

@ -0,0 +1,119 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.designsystem.atomic.pages
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Text
/**
* @param modifier Classical modifier.
* @param header optional header.
* @param footer optional footer.
* @param content main content.
*/
@Composable
fun HeaderFooterPage(
modifier: Modifier = Modifier,
header: @Composable () -> Unit = {},
footer: @Composable () -> Unit = {},
content: @Composable () -> Unit = {},
) {
Column(
modifier = modifier
.fillMaxSize()
.systemBarsPadding()
.padding(all = 20.dp),
) {
// Header
header()
// Content
Column(
modifier = Modifier
.weight(1f)
.fillMaxWidth(),
) {
content()
}
// Footer
footer()
}
}
@Preview
@Composable
internal fun HeaderFooterPageLightPreview() =
ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
internal fun HeaderFooterPageDarkPreview() =
ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
HeaderFooterPage(
content = {
Box(
Modifier
.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(
text = "Content",
fontSize = 40.sp
)
}
},
header = {
Box(
Modifier
.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
Text(
text = "Header",
fontSize = 40.sp
)
}
},
footer = {
Box(
Modifier
.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
Text(
text = "Footer",
fontSize = 40.sp
)
}
}
)
}