From 1ba173be54e799c0dc2d0430186fd2585d8b9be7 Mon Sep 17 00:00:00 2001 From: k1ngsterr1 Date: Thu, 19 Mar 2026 22:22:01 +0500 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B2=D0=B5=D0=BB=20pull-=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=BC=D0=B0=D1=86=D0=B8=D1=8E=20=D1=80=D0=B5=D0=BA?= =?UTF-8?q?=D0=B2=D0=B5=D1=81=D1=82=D0=BE=D0=B2:=20=D0=BC=D0=BE=D0=BC?= =?UTF-8?q?=D0=B5=D0=BD=D1=82=D0=B0=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=BA=D0=B0=D0=B7=20=D0=BF=D0=B5=D1=80=D0=B2=D1=8B=D0=BC?= =?UTF-8?q?=20=D1=8D=D0=BB=D0=B5=D0=BC=D0=B5=D0=BD=D1=82=D0=BE=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../messenger/ui/chats/ChatsListScreen.kt | 325 +++++++----------- 1 file changed, 121 insertions(+), 204 deletions(-) diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt index 8a038ed..ccb394c 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt @@ -2118,7 +2118,9 @@ fun ChatsListScreen( val atTop = !chatListState.canScrollBackward val nearTop = - chatListState.firstVisibleItemIndex == 0 + chatListState.firstVisibleItemIndex == 0 && + chatListState.firstVisibleItemScrollOffset <= + 2 if (available.y < 0f && isRequestsVisible && @@ -2132,32 +2134,63 @@ fun ChatsListScreen( isRequestsVisible = false accumulatedPullUp = 0f } - return androidx.compose.ui.geometry.Offset.Zero + return androidx.compose.ui.geometry.Offset( + 0f, + available.y + ) + } + if (available.y >= 0f) { + accumulatedPullUp = 0f } - accumulatedPullUp = 0f - if (available.y > 0f && atTop && !isRequestsVisible) { - accumulatedPullDown = - (accumulatedPullDown + available.y) - .coerceAtMost( - pullDownLimit + if (!isRequestsVisible && atTop) { + if (available.y > 0f) { + accumulatedPullDown = + (accumulatedPullDown + available.y) + .coerceAtMost( + pullDownLimit + ) + requestsPullProgress = + (accumulatedPullDown / requestsRevealThresholdPx) + .coerceIn( + 0f, + 1.15f + ) + if (accumulatedPullDown >= requestsRevealThresholdPx) { + isRequestsVisible = true + accumulatedPullDown = 0f + accumulatedPullUp = 0f + requestsPullProgress = 0f + hapticFeedback.performHapticFeedback( + HapticFeedbackType.LongPress ) - requestsPullProgress = - (accumulatedPullDown / requestsRevealThresholdPx) - .coerceIn( - 0f, - 1.15f - ) - if (accumulatedPullDown >= requestsRevealThresholdPx) { - isRequestsVisible = true - accumulatedPullDown = 0f - accumulatedPullUp = 0f - requestsPullProgress = 0f - hapticFeedback.performHapticFeedback( - HapticFeedbackType.LongPress + } + return androidx.compose.ui.geometry.Offset( + 0f, + available.y ) } - } else if (available.y <= 0f || !atTop) { + + if (available.y < 0f && + accumulatedPullDown > 0f + ) { + accumulatedPullDown = + (accumulatedPullDown + available.y) + .coerceAtLeast( + 0f + ) + requestsPullProgress = + (accumulatedPullDown / requestsRevealThresholdPx) + .coerceIn( + 0f, + 1.15f + ) + return androidx.compose.ui.geometry.Offset( + 0f, + available.y + ) + } + } else if (!isRequestsVisible && !atTop) { accumulatedPullDown = 0f requestsPullProgress = 0f } @@ -2165,6 +2198,16 @@ fun ChatsListScreen( return androidx.compose.ui.geometry.Offset.Zero } + override suspend fun onPreFling( + available: androidx.compose.ui.unit.Velocity + ): androidx.compose.ui.unit.Velocity { + if (!isRequestsVisible) { + accumulatedPullDown = 0f + requestsPullProgress = 0f + } + return androidx.compose.ui.unit.Velocity.Zero + } + override suspend fun onPostFling( consumed: androidx.compose.ui.unit.Velocity, available: androidx.compose.ui.unit.Velocity @@ -2177,18 +2220,6 @@ fun ChatsListScreen( } } - LaunchedEffect( - chatListState.isScrollInProgress, - isRequestsVisible - ) { - if (!chatListState.isScrollInProgress && - !isRequestsVisible && - requestsPullProgress != 0f - ) { - requestsPullProgress = 0f - } - } - LazyColumn( state = chatListState, modifier = @@ -2227,56 +2258,70 @@ fun ChatsListScreen( } if (requestsCount > 0) { - if (!isRequestsVisible) { - item(key = "requests_reopen_handle") { - val animatedPullProgress by - animateFloatAsState( - targetValue = - requestsPullProgress, - animationSpec = - spring( - dampingRatio = - Spring.DampingRatioNoBouncy, - stiffness = - Spring.StiffnessMediumLow - ), - label = - "requestsPullProgress" - ) - RequestsRevealHandle( - isDarkTheme = isDarkTheme, - pullProgress = - animatedPullProgress, - onClick = { - isRequestsVisible = true - requestsPullProgress = - 0f - hapticFeedback.performHapticFeedback( - HapticFeedbackType.LongPress - ) - } + item(key = "requests_section") { + val requestsSectionProgress by + animateFloatAsState( + targetValue = + if (isRequestsVisible) 1f + else requestsPullProgress, + animationSpec = + spring( + dampingRatio = + Spring.DampingRatioNoBouncy, + stiffness = + Spring.StiffnessMediumLow + ), + label = + "requestsSectionProgress" ) - val revealProgress = - FastOutSlowInEasing - .transform( - animatedPullProgress - .coerceIn( - 0f, - 1f - ) - ) - if (revealProgress > 0.001f) { + val clampedProgress = + requestsSectionProgress + .coerceIn( + 0f, + 1.15f + ) + val revealProgress = + FastOutSlowInEasing + .transform( + clampedProgress + .coerceIn( + 0f, + 1f + ) + ) + val stretchOvershoot = + (clampedProgress - 1f) + .coerceAtLeast( + 0f + ) + val sectionHeight = + 76.dp * + revealProgress + + 10.dp * + stretchOvershoot + val sectionAlpha = + (0.55f + revealProgress * 0.45f) + .coerceIn( + 0f, + 1f + ) + + if (isRequestsVisible || + sectionHeight > 0.5.dp + ) { + Column { Box( modifier = Modifier.fillMaxWidth() .height( - 76.dp * - revealProgress + sectionHeight ) .clipToBounds() .graphicsLayer { alpha = - revealProgress + if (isRequestsVisible) + 1f + else sectionAlpha } ) { RequestsSection( @@ -2291,83 +2336,10 @@ fun ChatsListScreen( true requestsPullProgress = 0f - hapticFeedback.performHapticFeedback( - HapticFeedbackType.LongPress - ) + openRequestsRouteSafely() } ) } - } - Divider( - color = dividerColor, - thickness = 0.5.dp - ) - } - } - - item(key = "requests_section") { - AnimatedVisibility( - visible = - isRequestsVisible, - enter = - expandVertically( - expandFrom = - Alignment - .Top, - animationSpec = - tween( - durationMillis = - 260, - easing = - FastOutSlowInEasing - ) - ) + - fadeIn( - animationSpec = - tween( - durationMillis = - 180, - easing = - LinearOutSlowInEasing - ), - initialAlpha = - 0.7f - ), - exit = - shrinkVertically( - shrinkTowards = - Alignment - .Top, - animationSpec = - tween( - durationMillis = - 220, - easing = - FastOutSlowInEasing - ) - ) + - fadeOut( - animationSpec = - tween( - durationMillis = - 140, - easing = - FastOutLinearInEasing - ) - ) - ) { - Column { - RequestsSection( - count = - requestsCount, - requests = - requests, - isDarkTheme = - isDarkTheme, - onClick = { - openRequestsRouteSafely() - } - ) Divider( color = dividerColor, @@ -4635,61 +4607,6 @@ fun TypingIndicatorSmall() { } } -@Composable -private fun RequestsRevealHandle( - isDarkTheme: Boolean, - 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( - modifier = - Modifier.fillMaxWidth() - .graphicsLayer { - 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, - verticalAlignment = Alignment.CenterVertically - ) { - Icon( - imageVector = TablerIcons.ChevronDown, - contentDescription = "Show requests", - tint = iconColor, - modifier = - Modifier.size(18.dp + (2.dp * revealProgress)).graphicsLayer { - rotationZ = 180f * revealProgress - } - ) - Spacer(modifier = Modifier.width(6.dp)) - Text( - text = - if (clampedProgress >= 0.92f) "Release to open requests" - else "Pull down to show requests", - color = textColor, - fontSize = 13.sp, - fontWeight = FontWeight.Medium - ) - } -} - /** 📬 Секция Requests — Telegram Archived Chats style */ @Composable fun RequestsSection(