From 53946e2e6ee226737b8979b073a5dc9c6f62cce7 Mon Sep 17 00:00:00 2001 From: k1ngsterr1 Date: Thu, 19 Mar 2026 16:50:13 +0500 Subject: [PATCH] =?UTF-8?q?=D0=A1=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB=20=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D0=B1=D0=B8=D0=BB=D1=8C=D0=BD=D0=BE=D0=B5=20=D0=BF?= =?UTF-8?q?=D0=BE=D1=8F=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=80=D0=B5?= =?UTF-8?q?=D0=BA=D0=B2=D0=B5=D1=81=D1=82=D0=BE=D0=B2=20=D0=BF=D1=80=D0=B8?= =?UTF-8?q?=20=D0=BE=D1=82=D1=82=D1=8F=D0=B3=D0=B8=D0=B2=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=B8=20=D1=81=D0=BF=D0=B8=D1=81=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../messenger/ui/chats/ChatsListScreen.kt | 102 +++++++++++++++--- 1 file changed, 85 insertions(+), 17 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 4d07e8b..b83fcf9 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 @@ -1797,6 +1797,13 @@ fun ChatsListScreen( var lastAutoScrolledVerificationId by remember { mutableStateOf(null) } + val localDensity = LocalDensity.current + val requestsRevealThresholdPx = + remember(localDensity) { with(localDensity) { 56.dp.toPx() } } + val requestsHideThresholdPx = + remember(localDensity) { with(localDensity) { 16.dp.toPx() } } + val topOffsetTolerancePx = + remember(localDensity) { with(localDensity) { 3.dp.toPx() } } AnimatedContent( targetState = showDownloadsScreen, @@ -2061,9 +2068,6 @@ fun ChatsListScreen( } } - // Track scroll direction to hide/show Requests - val hapticFeedback = LocalHapticFeedback.current - // When a new device confirmation banner appears at the top, // smoothly bring the list to top so the banner is visible. LaunchedEffect(pendingDeviceVerification?.deviceId) { @@ -2087,31 +2091,94 @@ fun ChatsListScreen( lastAutoScrolledVerificationId = verificationId } - // NestedScroll — ловим направление свайпа даже без скролла - // Для появления: накапливаем pull down дельту, нужен сильный жест - val requestsNestedScroll = remember(hapticFeedback) { + // Pull-to-show/hide для реквестов: работаем только когда + // список реально дотянут до верха и пользователь тянет вниз. + val requestsNestedScroll = + remember( + chatListState, + requestsCount, + requestsRevealThresholdPx, + requestsHideThresholdPx, + topOffsetTolerancePx, + hapticFeedback + ) { var accumulatedPullDown = 0f + var hapticSent = false object : androidx.compose.ui.input.nestedscroll.NestedScrollConnection { + fun isAtTop(): Boolean { + return chatListState.firstVisibleItemIndex == 0 && + chatListState.firstVisibleItemScrollOffset <= + topOffsetTolerancePx.roundToInt() + } + override fun onPreScroll( available: androidx.compose.ui.geometry.Offset, source: androidx.compose.ui.input.nestedscroll.NestedScrollSource ): androidx.compose.ui.geometry.Offset { - if (available.y < -10f) { - // Свайп вверх — прячем легко + if (source != androidx.compose.ui.input.nestedscroll.NestedScrollSource.Drag || + requestsCount <= 0 + ) { accumulatedPullDown = 0f - isRequestsVisible = false - } else if (available.y > 0f && !isRequestsVisible) { - // Свайп вниз — накапливаем для появления - accumulatedPullDown += available.y - if (accumulatedPullDown > 120f) { + hapticSent = false + return androidx.compose.ui.geometry.Offset.Zero + } + + if (available.y < -requestsHideThresholdPx) { + // Свайп вверх — быстро прячем блок реквестов. + accumulatedPullDown = 0f + hapticSent = false + if (isRequestsVisible) { + isRequestsVisible = false + } + } + return androidx.compose.ui.geometry.Offset.Zero + } + + override fun onPostScroll( + consumed: androidx.compose.ui.geometry.Offset, + available: androidx.compose.ui.geometry.Offset, + source: androidx.compose.ui.input.nestedscroll.NestedScrollSource + ): androidx.compose.ui.geometry.Offset { + if (source != androidx.compose.ui.input.nestedscroll.NestedScrollSource.Drag || + requestsCount <= 0 + ) { + accumulatedPullDown = 0f + hapticSent = false + return androidx.compose.ui.geometry.Offset.Zero + } + + if (isRequestsVisible) { + accumulatedPullDown = 0f + hapticSent = false + return androidx.compose.ui.geometry.Offset.Zero + } + + val pullDownDelta = + if (available.y > 0f && isAtTop()) + available.y + else 0f + + if (pullDownDelta > 0f) { + accumulatedPullDown = + (accumulatedPullDown + + pullDownDelta) + .coerceAtMost( + requestsRevealThresholdPx * + 2f + ) + if (accumulatedPullDown >= requestsRevealThresholdPx) { isRequestsVisible = true accumulatedPullDown = 0f - hapticFeedback.performHapticFeedback( - HapticFeedbackType.LongPress - ) + if (!hapticSent) { + hapticFeedback.performHapticFeedback( + HapticFeedbackType.LongPress + ) + hapticSent = true + } } - } else if (available.y <= 0f) { + } else if (available.y < 0f || !isAtTop()) { accumulatedPullDown = 0f + hapticSent = false } return androidx.compose.ui.geometry.Offset.Zero } @@ -2121,6 +2188,7 @@ fun ChatsListScreen( available: androidx.compose.ui.unit.Velocity ): androidx.compose.ui.unit.Velocity { accumulatedPullDown = 0f + hapticSent = false return androidx.compose.ui.unit.Velocity.Zero } }