Сделал плавную вытягивающуюся анимацию реквестов в чат-листе
This commit is contained in:
@@ -1794,6 +1794,7 @@ fun ChatsListScreen(
|
|||||||
val showSkeleton = isLoading
|
val showSkeleton = isLoading
|
||||||
val chatListState = rememberLazyListState()
|
val chatListState = rememberLazyListState()
|
||||||
var isRequestsVisible by remember { mutableStateOf(true) }
|
var isRequestsVisible by remember { mutableStateOf(true) }
|
||||||
|
var requestsPullProgress by remember { mutableStateOf(0f) }
|
||||||
var lastAutoScrolledVerificationId by remember {
|
var lastAutoScrolledVerificationId by remember {
|
||||||
mutableStateOf<String?>(null)
|
mutableStateOf<String?>(null)
|
||||||
}
|
}
|
||||||
@@ -2099,6 +2100,8 @@ fun ChatsListScreen(
|
|||||||
) {
|
) {
|
||||||
var accumulatedPullDown = 0f
|
var accumulatedPullDown = 0f
|
||||||
var accumulatedPullUp = 0f
|
var accumulatedPullUp = 0f
|
||||||
|
val pullDownLimit =
|
||||||
|
requestsRevealThresholdPx * 1.25f
|
||||||
object : androidx.compose.ui.input.nestedscroll.NestedScrollConnection {
|
object : androidx.compose.ui.input.nestedscroll.NestedScrollConnection {
|
||||||
override fun onPreScroll(
|
override fun onPreScroll(
|
||||||
available: androidx.compose.ui.geometry.Offset,
|
available: androidx.compose.ui.geometry.Offset,
|
||||||
@@ -2109,6 +2112,7 @@ fun ChatsListScreen(
|
|||||||
) {
|
) {
|
||||||
accumulatedPullDown = 0f
|
accumulatedPullDown = 0f
|
||||||
accumulatedPullUp = 0f
|
accumulatedPullUp = 0f
|
||||||
|
requestsPullProgress = 0f
|
||||||
return androidx.compose.ui.geometry.Offset.Zero
|
return androidx.compose.ui.geometry.Offset.Zero
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2123,6 +2127,7 @@ fun ChatsListScreen(
|
|||||||
) {
|
) {
|
||||||
accumulatedPullUp += -available.y
|
accumulatedPullUp += -available.y
|
||||||
accumulatedPullDown = 0f
|
accumulatedPullDown = 0f
|
||||||
|
requestsPullProgress = 0f
|
||||||
if (accumulatedPullUp >= requestsHideThresholdPx) {
|
if (accumulatedPullUp >= requestsHideThresholdPx) {
|
||||||
isRequestsVisible = false
|
isRequestsVisible = false
|
||||||
accumulatedPullUp = 0f
|
accumulatedPullUp = 0f
|
||||||
@@ -2135,18 +2140,26 @@ fun ChatsListScreen(
|
|||||||
accumulatedPullDown =
|
accumulatedPullDown =
|
||||||
(accumulatedPullDown + available.y)
|
(accumulatedPullDown + available.y)
|
||||||
.coerceAtMost(
|
.coerceAtMost(
|
||||||
requestsRevealThresholdPx
|
pullDownLimit
|
||||||
|
)
|
||||||
|
requestsPullProgress =
|
||||||
|
(accumulatedPullDown / requestsRevealThresholdPx)
|
||||||
|
.coerceIn(
|
||||||
|
0f,
|
||||||
|
1.15f
|
||||||
)
|
)
|
||||||
if (accumulatedPullDown >= requestsRevealThresholdPx) {
|
if (accumulatedPullDown >= requestsRevealThresholdPx) {
|
||||||
isRequestsVisible = true
|
isRequestsVisible = true
|
||||||
accumulatedPullDown = 0f
|
accumulatedPullDown = 0f
|
||||||
accumulatedPullUp = 0f
|
accumulatedPullUp = 0f
|
||||||
|
requestsPullProgress = 0f
|
||||||
hapticFeedback.performHapticFeedback(
|
hapticFeedback.performHapticFeedback(
|
||||||
HapticFeedbackType.LongPress
|
HapticFeedbackType.LongPress
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else if (available.y <= 0f || !atTop) {
|
} else if (available.y <= 0f || !atTop) {
|
||||||
accumulatedPullDown = 0f
|
accumulatedPullDown = 0f
|
||||||
|
requestsPullProgress = 0f
|
||||||
}
|
}
|
||||||
|
|
||||||
return androidx.compose.ui.geometry.Offset.Zero
|
return androidx.compose.ui.geometry.Offset.Zero
|
||||||
@@ -2158,11 +2171,24 @@ fun ChatsListScreen(
|
|||||||
): androidx.compose.ui.unit.Velocity {
|
): androidx.compose.ui.unit.Velocity {
|
||||||
accumulatedPullDown = 0f
|
accumulatedPullDown = 0f
|
||||||
accumulatedPullUp = 0f
|
accumulatedPullUp = 0f
|
||||||
|
requestsPullProgress = 0f
|
||||||
return androidx.compose.ui.unit.Velocity.Zero
|
return androidx.compose.ui.unit.Velocity.Zero
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(
|
||||||
|
chatListState.isScrollInProgress,
|
||||||
|
isRequestsVisible
|
||||||
|
) {
|
||||||
|
if (!chatListState.isScrollInProgress &&
|
||||||
|
!isRequestsVisible &&
|
||||||
|
requestsPullProgress != 0f
|
||||||
|
) {
|
||||||
|
requestsPullProgress = 0f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
state = chatListState,
|
state = chatListState,
|
||||||
modifier =
|
modifier =
|
||||||
@@ -2203,15 +2229,75 @@ fun ChatsListScreen(
|
|||||||
if (requestsCount > 0) {
|
if (requestsCount > 0) {
|
||||||
if (!isRequestsVisible) {
|
if (!isRequestsVisible) {
|
||||||
item(key = "requests_reopen_handle") {
|
item(key = "requests_reopen_handle") {
|
||||||
|
val animatedPullProgress by
|
||||||
|
animateFloatAsState(
|
||||||
|
targetValue =
|
||||||
|
requestsPullProgress,
|
||||||
|
animationSpec =
|
||||||
|
spring(
|
||||||
|
dampingRatio =
|
||||||
|
Spring.DampingRatioNoBouncy,
|
||||||
|
stiffness =
|
||||||
|
Spring.StiffnessMediumLow
|
||||||
|
),
|
||||||
|
label =
|
||||||
|
"requestsPullProgress"
|
||||||
|
)
|
||||||
RequestsRevealHandle(
|
RequestsRevealHandle(
|
||||||
isDarkTheme = isDarkTheme,
|
isDarkTheme = isDarkTheme,
|
||||||
|
pullProgress =
|
||||||
|
animatedPullProgress,
|
||||||
onClick = {
|
onClick = {
|
||||||
isRequestsVisible = true
|
isRequestsVisible = true
|
||||||
|
requestsPullProgress =
|
||||||
|
0f
|
||||||
hapticFeedback.performHapticFeedback(
|
hapticFeedback.performHapticFeedback(
|
||||||
HapticFeedbackType.LongPress
|
HapticFeedbackType.LongPress
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
val revealProgress =
|
||||||
|
FastOutSlowInEasing
|
||||||
|
.transform(
|
||||||
|
animatedPullProgress
|
||||||
|
.coerceIn(
|
||||||
|
0f,
|
||||||
|
1f
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (revealProgress > 0.001f) {
|
||||||
|
Box(
|
||||||
|
modifier =
|
||||||
|
Modifier.fillMaxWidth()
|
||||||
|
.height(
|
||||||
|
76.dp *
|
||||||
|
revealProgress
|
||||||
|
)
|
||||||
|
.clipToBounds()
|
||||||
|
.graphicsLayer {
|
||||||
|
alpha =
|
||||||
|
revealProgress
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
RequestsSection(
|
||||||
|
count =
|
||||||
|
requestsCount,
|
||||||
|
requests =
|
||||||
|
requests,
|
||||||
|
isDarkTheme =
|
||||||
|
isDarkTheme,
|
||||||
|
onClick = {
|
||||||
|
isRequestsVisible =
|
||||||
|
true
|
||||||
|
requestsPullProgress =
|
||||||
|
0f
|
||||||
|
hapticFeedback.performHapticFeedback(
|
||||||
|
HapticFeedbackType.LongPress
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Divider(
|
Divider(
|
||||||
color = dividerColor,
|
color = dividerColor,
|
||||||
thickness = 0.5.dp
|
thickness = 0.5.dp
|
||||||
@@ -4550,16 +4636,36 @@ fun TypingIndicatorSmall() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun RequestsRevealHandle(isDarkTheme: Boolean, onClick: () -> Unit) {
|
private fun RequestsRevealHandle(
|
||||||
val textColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF6C6C70)
|
isDarkTheme: Boolean,
|
||||||
val iconColor = if (isDarkTheme) Color(0xFF7E7E84) else Color(0xFF8A8A90)
|
pullProgress: Float,
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
|
val clampedProgress = pullProgress.coerceIn(0f, 1.15f)
|
||||||
|
val revealProgress = FastOutSlowInEasing.transform(clampedProgress.coerceIn(0f, 1f))
|
||||||
|
val stretchOvershoot = (clampedProgress - 1f).coerceAtLeast(0f)
|
||||||
|
|
||||||
|
val textColor =
|
||||||
|
if (isDarkTheme) Color(0xFF8E8E93).copy(alpha = 0.84f + revealProgress * 0.16f)
|
||||||
|
else Color(0xFF6C6C70).copy(alpha = 0.84f + revealProgress * 0.16f)
|
||||||
|
val iconColor =
|
||||||
|
if (isDarkTheme) Color(0xFF7E7E84).copy(alpha = 0.88f + revealProgress * 0.12f)
|
||||||
|
else Color(0xFF8A8A90).copy(alpha = 0.88f + revealProgress * 0.12f)
|
||||||
|
val verticalPadding = 10.dp + (6.dp * revealProgress) + (4.dp * stretchOvershoot)
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier.fillMaxWidth().clickable(onClick = onClick).padding(
|
Modifier.fillMaxWidth()
|
||||||
horizontal = TELEGRAM_DIALOG_AVATAR_START,
|
.graphicsLayer {
|
||||||
vertical = 10.dp
|
val scaleBoost =
|
||||||
),
|
revealProgress * 0.015f + stretchOvershoot * 0.06f
|
||||||
|
scaleY = 1f + scaleBoost
|
||||||
|
}
|
||||||
|
.clickable(onClick = onClick)
|
||||||
|
.padding(
|
||||||
|
horizontal = TELEGRAM_DIALOG_AVATAR_START,
|
||||||
|
vertical = verticalPadding
|
||||||
|
),
|
||||||
horizontalArrangement = Arrangement.Center,
|
horizontalArrangement = Arrangement.Center,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
@@ -4567,11 +4673,16 @@ private fun RequestsRevealHandle(isDarkTheme: Boolean, onClick: () -> Unit) {
|
|||||||
imageVector = TablerIcons.ChevronDown,
|
imageVector = TablerIcons.ChevronDown,
|
||||||
contentDescription = "Show requests",
|
contentDescription = "Show requests",
|
||||||
tint = iconColor,
|
tint = iconColor,
|
||||||
modifier = Modifier.size(18.dp)
|
modifier =
|
||||||
|
Modifier.size(18.dp + (2.dp * revealProgress)).graphicsLayer {
|
||||||
|
rotationZ = 180f * revealProgress
|
||||||
|
}
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.width(6.dp))
|
Spacer(modifier = Modifier.width(6.dp))
|
||||||
Text(
|
Text(
|
||||||
text = "Pull down to show requests",
|
text =
|
||||||
|
if (clampedProgress >= 0.92f) "Release to open requests"
|
||||||
|
else "Pull down to show requests",
|
||||||
color = textColor,
|
color = textColor,
|
||||||
fontSize = 13.sp,
|
fontSize = 13.sp,
|
||||||
fontWeight = FontWeight.Medium
|
fontWeight = FontWeight.Medium
|
||||||
|
|||||||
Reference in New Issue
Block a user