88 lines
3.0 KiB
Kotlin
88 lines
3.0 KiB
Kotlin
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()
|
|
}
|
|
}
|
|
}
|