package chats.presentation.components import androidx.compose.foundation.background import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.draggable import androidx.compose.foundation.gestures.rememberDraggableState import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Reply import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch import kotlin.math.roundToInt @Composable fun SwipeableMessageItem( onReply: () -> Unit, content: @Composable () -> Unit ) { val density = LocalDensity.current val scope = rememberCoroutineScope() val replyThreshold = with(density) { 60.dp.toPx() } val maxDrag = with(density) { 90.dp.toPx() } var offsetX by remember { mutableFloatStateOf(0f) } var isTriggered by remember { mutableStateOf(false) } Box( modifier = Modifier .fillMaxWidth() .draggable( orientation = Orientation.Horizontal, state = rememberDraggableState { delta -> // We only allow swiping to the left (negative delta) val newOffset = (offsetX + delta).coerceIn(-maxDrag, 0f) offsetX = newOffset isTriggered = newOffset <= -replyThreshold }, onDragStopped = { if (offsetX <= -replyThreshold) { onReply() } offsetX = 0f isTriggered = false } ) ) { // Reply Icon (Underneath) Box( modifier = Modifier .align(Alignment.CenterEnd) .padding(end = 16.dp) .size(36.dp) .alpha(((-offsetX) / replyThreshold).coerceIn(0f, 1f)) .clip(CircleShape) .background(if (isTriggered) MaterialTheme.colorScheme.primary else Color.Gray.copy(alpha = 0.2f)), contentAlignment = Alignment.Center ) { Icon( imageVector = Icons.Default.Reply, contentDescription = "Reply", tint = Color.White, modifier = Modifier.size(20.dp) ) } // Message Content Box( modifier = Modifier .offset { IntOffset(offsetX.roundToInt(), 0) } .fillMaxWidth() ) { content() } } }