mirror of
https://github.com/fromchat-messenger/app.git
synced 2026-09-22 19:15:05 +03:00
@@ -11,6 +11,7 @@ import androidx.compose.foundation.gestures.detectTapGestures
|
|||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.IntrinsicSize
|
import androidx.compose.foundation.layout.IntrinsicSize
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@@ -90,143 +91,135 @@ fun MessageItem(
|
|||||||
Spacer(modifier = Modifier.width(8.dp))
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(
|
BoxWithConstraints(
|
||||||
modifier = Modifier
|
modifier = Modifier.weight(1f, fill = false)
|
||||||
.weight(1f, fill = false)
|
|
||||||
.widthIn(max = 280.dp),
|
|
||||||
horizontalAlignment = if (isAuthor) Alignment.End else Alignment.Start
|
|
||||||
) {
|
) {
|
||||||
// Message bubble
|
// Allow bubbles to grow up to 70% of the available row width
|
||||||
val isDark = isSystemInDarkTheme()
|
val maxBubbleWidth = maxWidth * 0.7f
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
Column(
|
||||||
.width(IntrinsicSize.Min)
|
horizontalAlignment = if (isAuthor) Alignment.End else Alignment.Start
|
||||||
.clip(
|
|
||||||
RoundedCornerShape(
|
|
||||||
topStart = 20.dp,
|
|
||||||
topEnd = 20.dp,
|
|
||||||
bottomStart = if (isAuthor) 20.dp else 8.dp,
|
|
||||||
bottomEnd = if (isAuthor) 8.dp else 20.dp
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.conditional(
|
|
||||||
isAuthor,
|
|
||||||
`if` = {
|
|
||||||
it
|
|
||||||
.shadow(
|
|
||||||
elevation = 8.dp,
|
|
||||||
shape = RoundedCornerShape(
|
|
||||||
topStart = 20.dp,
|
|
||||||
topEnd = 20.dp,
|
|
||||||
bottomStart = 20.dp,
|
|
||||||
bottomEnd = 8.dp
|
|
||||||
),
|
|
||||||
spotColor = if (isDark) Color(0x66000000) else Color(0x33000000)
|
|
||||||
)
|
|
||||||
.background(getMessageGradient(isDark))
|
|
||||||
},
|
|
||||||
`else` = {
|
|
||||||
background(MaterialTheme.colorScheme.surfaceContainerHighest)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.padding(top = 6.dp)
|
|
||||||
) {
|
) {
|
||||||
Column {
|
// Message bubble
|
||||||
// Username inside bubble (for received messages)
|
val isDark = isSystemInDarkTheme()
|
||||||
if (showUsername && !isAuthor) {
|
Box(
|
||||||
Text(
|
modifier = Modifier
|
||||||
text = message.username,
|
// Max width: 70% of row, min width: content-driven
|
||||||
style = MaterialTheme.typography.labelMedium,
|
.widthIn(max = maxBubbleWidth)
|
||||||
fontWeight = FontWeight.SemiBold,
|
.clip(
|
||||||
color = MaterialTheme.colorScheme.primary,
|
RoundedCornerShape(
|
||||||
modifier = Modifier.padding(start = 12.dp, end = 12.dp, bottom = 4.dp)
|
topStart = 20.dp,
|
||||||
|
topEnd = 20.dp,
|
||||||
|
bottomStart = if (isAuthor) 20.dp else 8.dp,
|
||||||
|
bottomEnd = if (isAuthor) 8.dp else 20.dp
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
.conditional(
|
||||||
|
isAuthor,
|
||||||
// Reply preview
|
`if` = {
|
||||||
message.reply_to?.let { replyTo ->
|
it
|
||||||
Box(
|
.shadow(
|
||||||
Modifier.padding(bottom = 4.dp, start = 6.dp, end = 6.dp)
|
elevation = 8.dp,
|
||||||
) {
|
shape = RoundedCornerShape(
|
||||||
Row(
|
topStart = 20.dp,
|
||||||
modifier = Modifier
|
topEnd = 20.dp,
|
||||||
.clip(RoundedCornerShape(12.dp))
|
bottomStart = 20.dp,
|
||||||
.fillMaxWidth()
|
bottomEnd = 8.dp
|
||||||
.height(IntrinsicSize.Min)
|
),
|
||||||
.conditional(
|
spotColor = if (isDark) Color(0x66000000) else Color(0x33000000)
|
||||||
isAuthor,
|
|
||||||
`if` = {
|
|
||||||
background(getReplyMessageGradient(isDark))
|
|
||||||
},
|
|
||||||
`else` = {
|
|
||||||
background(MaterialTheme.colorScheme.surfaceVariant)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
) {
|
.background(getMessageGradient(isDark))
|
||||||
Box(
|
},
|
||||||
Modifier
|
`else` = {
|
||||||
.background(MaterialTheme.colorScheme.primary)
|
background(MaterialTheme.colorScheme.surfaceContainerHighest)
|
||||||
.width(3.dp)
|
}
|
||||||
.fillMaxHeight()
|
)
|
||||||
)
|
.padding(top = 6.dp)
|
||||||
|
) {
|
||||||
|
Column {
|
||||||
|
// Username inside bubble (for received messages)
|
||||||
|
if (showUsername && !isAuthor) {
|
||||||
|
Text(
|
||||||
|
text = message.username,
|
||||||
|
style = MaterialTheme.typography.labelMedium,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
modifier = Modifier.padding(start = 12.dp, end = 12.dp, bottom = 4.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Column(
|
// Reply preview
|
||||||
Modifier.padding(horizontal = 8.dp, vertical = 6.dp)
|
message.reply_to?.let { replyTo ->
|
||||||
|
Box(
|
||||||
|
Modifier.padding(bottom = 4.dp, start = 6.dp, end = 6.dp)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.clip(RoundedCornerShape(12.dp))
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(IntrinsicSize.Min)
|
||||||
|
.conditional(
|
||||||
|
isAuthor,
|
||||||
|
`if` = {
|
||||||
|
background(getReplyMessageGradient(isDark))
|
||||||
|
},
|
||||||
|
`else` = {
|
||||||
|
background(MaterialTheme.colorScheme.surfaceVariant)
|
||||||
|
}
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
if (showUsername) {
|
Box(
|
||||||
|
Modifier
|
||||||
|
.background(MaterialTheme.colorScheme.primary)
|
||||||
|
.width(3.dp)
|
||||||
|
.fillMaxHeight()
|
||||||
|
)
|
||||||
|
|
||||||
|
Column(
|
||||||
|
Modifier.padding(horizontal = 8.dp, vertical = 6.dp)
|
||||||
|
) {
|
||||||
|
if (showUsername) {
|
||||||
|
Text(
|
||||||
|
text = replyTo.username,
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
fontSize = 11.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
Text(
|
Text(
|
||||||
text = replyTo.username,
|
text = replyTo.content.take(50) + if (replyTo.content.length > 50) "..." else "",
|
||||||
style = MaterialTheme.typography.labelSmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
fontWeight = FontWeight.SemiBold,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
color = MaterialTheme.colorScheme.primary,
|
fontSize = 12.sp,
|
||||||
fontSize = 11.sp
|
maxLines = 1
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Text(
|
|
||||||
text = replyTo.content.take(50) + if (replyTo.content.length > 50) "..." else "",
|
|
||||||
style = MaterialTheme.typography.bodySmall,
|
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
fontSize = 12.sp,
|
|
||||||
maxLines = 1
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Message content
|
// Message content
|
||||||
Text(
|
|
||||||
text = message.content,
|
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
|
||||||
color = if (isAuthor) {
|
|
||||||
Color.White
|
|
||||||
} else {
|
|
||||||
MaterialTheme.colorScheme.onSurface
|
|
||||||
},
|
|
||||||
modifier = Modifier.padding(horizontal = 12.dp)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Timestamp and edited indicator
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(start = 12.dp, end = 12.dp, top = 4.dp, bottom = 8.dp),
|
|
||||||
horizontalArrangement = Arrangement.End,
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
Text(
|
Text(
|
||||||
text = formatTime(message.timestamp),
|
text = message.content,
|
||||||
style = MaterialTheme.typography.labelSmall,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
fontSize = 11.sp,
|
|
||||||
color = if (isAuthor) {
|
color = if (isAuthor) {
|
||||||
Color.White.copy(alpha = 0.7f)
|
Color.White
|
||||||
} else {
|
} else {
|
||||||
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f)
|
MaterialTheme.colorScheme.onSurface
|
||||||
}
|
},
|
||||||
|
modifier = Modifier.padding(horizontal = 12.dp)
|
||||||
)
|
)
|
||||||
if (message.is_edited) {
|
|
||||||
Spacer(modifier = Modifier.width(4.dp))
|
// Timestamp and edited indicator
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(start = 12.dp, end = 12.dp, top = 4.dp, bottom = 8.dp),
|
||||||
|
horizontalArrangement = Arrangement.End,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "(edited)",
|
text = formatTime(message.timestamp),
|
||||||
style = MaterialTheme.typography.labelSmall,
|
style = MaterialTheme.typography.labelSmall,
|
||||||
fontSize = 11.sp,
|
fontSize = 11.sp,
|
||||||
color = if (isAuthor) {
|
color = if (isAuthor) {
|
||||||
@@ -235,6 +228,19 @@ fun MessageItem(
|
|||||||
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f)
|
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
if (message.is_edited) {
|
||||||
|
Spacer(modifier = Modifier.width(4.dp))
|
||||||
|
Text(
|
||||||
|
text = "(edited)",
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
fontSize = 11.sp,
|
||||||
|
color = if (isAuthor) {
|
||||||
|
Color.White.copy(alpha = 0.7f)
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user