Add proper gesture when user navigates to or from a conversation.

This commit is contained in:
Alex Hart
2025-09-29 11:13:06 -03:00
committed by Michelle Tang
parent 51897bb74f
commit 019df97a22
2 changed files with 104 additions and 2 deletions

View File

@@ -7,6 +7,8 @@ package org.thoughtcrime.securesms.window
import android.content.res.Configuration
import android.content.res.Resources
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
@@ -14,6 +16,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.displayCutoutPadding
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.LocalMinimumInteractiveComponentSize
@@ -30,9 +33,11 @@ import androidx.compose.material3.adaptive.navigation.NavigableListDetailPaneSca
import androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.layout
@@ -42,6 +47,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import androidx.window.core.ExperimentalWindowCoreApi
import androidx.window.core.layout.WindowHeightSizeClass
import androidx.window.core.layout.WindowWidthSizeClass
@@ -228,9 +234,28 @@ fun AppScaffold(
NavigableListDetailPaneScaffold(
navigator = navigator,
listPane = {
AnimatedPane {
val offset by animateDp(
targetWhenHiding = {
(-48).dp
},
targetWhenShowing = {
0.dp
}
)
val alpha by animateFloat {
1f
}
AnimatedPane(
enterTransition = EnterTransition.None,
exitTransition = ExitTransition.None,
modifier = Modifier.zIndex(0f)
) {
Box(
modifier = Modifier
.alpha(alpha)
.offset(x = offset)
.clipToBounds()
.layout { measurable, constraints ->
val width = max(minPaneWidth.roundToPx(), constraints.maxWidth)
@@ -258,9 +283,28 @@ fun AppScaffold(
}
},
detailPane = {
AnimatedPane {
val offset by animateDp(
targetWhenHiding = {
48.dp
},
targetWhenShowing = {
0.dp
}
)
val alpha by animateFloat {
1f
}
AnimatedPane(
enterTransition = EnterTransition.None,
exitTransition = ExitTransition.None,
modifier = Modifier.zIndex(1f)
) {
Box(
modifier = Modifier
.alpha(alpha)
.offset(x = offset)
.clipToBounds()
.layout { measurable, constraints ->
val width = max(minPaneWidth.roundToPx(), constraints.maxWidth)

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2025 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.window
import androidx.compose.animation.core.CubicBezierEasing
import androidx.compose.animation.core.animateDp
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.tween
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
import androidx.compose.material3.adaptive.layout.PaneAdaptedValue
import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
private val easing = CubicBezierEasing(0.4f, 0.0f, 0.2f, 1f)
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ThreePaneScaffoldPaneScope.animateDp(
targetWhenHiding: () -> Dp = { 0.dp },
targetWhenShowing: () -> Dp
): State<Dp> {
return scaffoldStateTransition.animateDp(
transitionSpec = { tween(durationMillis = 200, easing = easing) }
) {
val isHiding = it[paneRole] == PaneAdaptedValue.Hidden
if (isHiding) {
targetWhenHiding()
} else {
targetWhenShowing()
}
}
}
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ThreePaneScaffoldPaneScope.animateFloat(
targetWhenHiding: () -> Float = { 0f },
targetWhenShowing: () -> Float
): State<Float> {
return scaffoldStateTransition.animateFloat(
transitionSpec = { tween(durationMillis = 200, easing = easing) }
) {
val isHiding = it[paneRole] == PaneAdaptedValue.Hidden
if (isHiding) {
targetWhenHiding()
} else {
targetWhenShowing()
}
}
}