Приложение
This commit is contained in:
@@ -0,0 +1,208 @@
|
||||
package chats.presentation.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.window.Popup
|
||||
import chats.domain.model.Message
|
||||
import chats.domain.model.MediaType
|
||||
import coil.compose.AsyncImage
|
||||
import core.presentation.components.AppVideoPlayer
|
||||
import core.presentation.components.AppAudioPlayer
|
||||
|
||||
import chats.presentation.components.LinkPreview
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
@OptIn(androidx.compose.foundation.ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun MessageBubble(
|
||||
message: Message,
|
||||
isCurrentUser: Boolean,
|
||||
onReactionClick: (String) -> Unit = {}
|
||||
) {
|
||||
var showReactionPicker by remember { mutableStateOf(false) }
|
||||
|
||||
val backgroundColor = if (isCurrentUser) {
|
||||
MaterialTheme.colorScheme.primary
|
||||
} else {
|
||||
MaterialTheme.colorScheme.surfaceVariant
|
||||
}
|
||||
|
||||
val contentColor = if (isCurrentUser) {
|
||||
MaterialTheme.colorScheme.onPrimary
|
||||
} else {
|
||||
MaterialTheme.colorScheme.onSurfaceVariant
|
||||
}
|
||||
|
||||
val alignment = if (isCurrentUser) Alignment.CenterEnd else Alignment.CenterStart
|
||||
val shape = if (isCurrentUser) {
|
||||
RoundedCornerShape(16.dp, 16.dp, 4.dp, 16.dp)
|
||||
} else {
|
||||
RoundedCornerShape(16.dp, 16.dp, 16.dp, 4.dp)
|
||||
}
|
||||
|
||||
val isVoiceMessage = message.mediaType == MediaType.AUDIO &&
|
||||
(message.content == null || message.content.isEmpty())
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp),
|
||||
contentAlignment = alignment
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.clip(shape)
|
||||
.background(backgroundColor)
|
||||
.combinedClickable(
|
||||
onClick = { /* Handle normal click */ },
|
||||
onLongClick = { showReactionPicker = true }
|
||||
)
|
||||
.padding(8.dp)
|
||||
.widthIn(max = 300.dp)
|
||||
) {
|
||||
if (showReactionPicker) {
|
||||
Popup(
|
||||
alignment = Alignment.TopCenter,
|
||||
offset = IntOffset(0, -100),
|
||||
onDismissRequest = { showReactionPicker = false }
|
||||
) {
|
||||
ReactionPicker(onReactionSelected = {
|
||||
onReactionClick(it)
|
||||
showReactionPicker = false
|
||||
})
|
||||
}
|
||||
}
|
||||
// Sender Name (only for others)
|
||||
if (!isCurrentUser) {
|
||||
Text(
|
||||
text = message.senderName,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = contentColor.copy(alpha = 0.7f),
|
||||
modifier = Modifier.padding(bottom = 2.dp)
|
||||
)
|
||||
}
|
||||
|
||||
// Reply Info
|
||||
message.replyTo?.let { reply ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 4.dp)
|
||||
.clip(RoundedCornerShape(4.dp))
|
||||
.background(contentColor.copy(alpha = 0.1f))
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
text = reply.senderName,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = contentColor,
|
||||
maxLines = 1
|
||||
)
|
||||
Text(
|
||||
text = reply.content ?: "[Media]",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = contentColor.copy(alpha = 0.7f),
|
||||
maxLines = 1
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Media Content
|
||||
if (message.mediaUrl != null) {
|
||||
when (message.mediaType) {
|
||||
MediaType.IMAGE -> {
|
||||
AsyncImage(
|
||||
model = message.mediaUrl,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(max = 200.dp)
|
||||
.clip(RoundedCornerShape(12.dp)),
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
}
|
||||
MediaType.VIDEO -> {
|
||||
AppVideoPlayer(
|
||||
url = message.mediaUrl,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(200.dp)
|
||||
.clip(RoundedCornerShape(12.dp)),
|
||||
useController = true,
|
||||
autoPlay = false
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
}
|
||||
MediaType.AUDIO -> {
|
||||
AppAudioPlayer(
|
||||
url = message.mediaUrl,
|
||||
isVoiceMessage = isVoiceMessage,
|
||||
contentColor = contentColor
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
// Text Content (Story reply or simple text)
|
||||
if (message.mediaType == MediaType.STORY_REPLY) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 4.dp)
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(contentColor.copy(alpha = 0.1f))
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Text("Story Reply: ${message.content}", color = contentColor, style = MaterialTheme.typography.bodyMedium)
|
||||
}
|
||||
} else if (!message.content.isNullOrBlank() && !isVoiceMessage) {
|
||||
Text(
|
||||
text = message.content,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = contentColor
|
||||
)
|
||||
}
|
||||
|
||||
// Time and Status
|
||||
Row(
|
||||
modifier = Modifier.align(Alignment.End),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = message.createdAt.takeLast(5),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = contentColor.copy(alpha = 0.6f)
|
||||
)
|
||||
}
|
||||
|
||||
// Reactions
|
||||
if (message.reactions.isNotEmpty()) {
|
||||
MessageReactions(
|
||||
reactions = message.reactions,
|
||||
onReactionClick = onReactionClick
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user