From 0f94e18e36df9b5366058267d32337f1f54cbf2d Mon Sep 17 00:00:00 2001 From: VelikovPetar Date: Mon, 30 Mar 2026 14:58:57 +0200 Subject: [PATCH] Fix swipe-to-reply direction and icon mirroring in RTL layouts Co-Authored-By: Claude --- .../ui/messages/list/MessageContainer.kt | 27 ++++++++++++------- .../res/drawable/stream_compose_ic_reply.xml | 3 ++- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/list/MessageContainer.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/list/MessageContainer.kt index 20c4579f78f..831829b90a8 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/list/MessageContainer.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/list/MessageContainer.kt @@ -32,8 +32,8 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.absoluteOffset import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -59,6 +59,7 @@ import androidx.compose.ui.layout.Layout import androidx.compose.ui.layout.layout import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.platform.LocalHapticFeedback +import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource @@ -66,6 +67,7 @@ import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex import androidx.core.view.HapticFeedbackConstantsCompat @@ -826,6 +828,7 @@ private fun SwipeToReply( val offset = remember { Animatable(initialValue = 0f) } val scope = rememberCoroutineScope() val view = LocalView.current + val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl Box( modifier = modifier @@ -836,11 +839,14 @@ private fun SwipeToReply( modifier = Modifier .align(Alignment.CenterStart) .onSizeChanged { swipeToReplyWidth = it.width.toFloat() } - .offset { + .absoluteOffset { val roundToInt = swipeToReplyWidth.roundToInt() IntOffset( - x = (offset.value.roundToInt() - roundToInt) - .coerceIn(-roundToInt, roundToInt), + x = if (isRtl) { + (roundToInt + offset.value.roundToInt()).coerceIn(-roundToInt, roundToInt) + } else { + (offset.value.roundToInt() - roundToInt).coerceIn(-roundToInt, roundToInt) + }, y = 0, ) }, @@ -855,16 +861,19 @@ private fun SwipeToReply( modifier = Modifier .fillMaxWidth() .onSizeChanged { rowWidth = it.width.toFloat() } - .offset { IntOffset(x = offset.value.roundToInt(), y = 0) } - .pointerInput(swipeToReplyWidth, isSwipeable) { + .absoluteOffset { IntOffset(x = offset.value.roundToInt(), y = 0) } + .pointerInput(swipeToReplyWidth, isSwipeable, isRtl) { if (isSwipeable) { detectHorizontalDragGestures( onHorizontalDrag = { change, dragAmount -> // Only consume if horizontal drag dominates vertical if (change.positionChange().x.absoluteValue > change.positionChange().y.absoluteValue) { scope.launch { - val newOffset = (offset.value + dragAmount) - .coerceIn(0f, maxOf((rowWidth / 2), swipeToReplyWidth)) + val maxSwipe = maxOf((rowWidth / 2), swipeToReplyWidth) + val newOffset = (offset.value + dragAmount).coerceIn( + minimumValue = if (isRtl) -maxSwipe else 0f, + maximumValue = if (isRtl) 0f else maxSwipe, + ) offset.snapTo(newOffset) } } else { @@ -873,7 +882,7 @@ private fun SwipeToReply( }, onDragEnd = { scope.launch { - if (offset.value >= swipeToReplyWidth) { + if (offset.value.absoluteValue >= swipeToReplyWidth) { view.performHapticFeedback(HapticFeedbackConstantsCompat.CONFIRM) onReply() } diff --git a/stream-chat-android-compose/src/main/res/drawable/stream_compose_ic_reply.xml b/stream-chat-android-compose/src/main/res/drawable/stream_compose_ic_reply.xml index 55fd67609de..60bdb327b61 100644 --- a/stream-chat-android-compose/src/main/res/drawable/stream_compose_ic_reply.xml +++ b/stream-chat-android-compose/src/main/res/drawable/stream_compose_ic_reply.xml @@ -18,7 +18,8 @@ android:width="20dp" android:height="20dp" android:viewportWidth="20" - android:viewportHeight="20"> + android:viewportHeight="20" + android:autoMirrored="true">