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 } }