Keep message footer layout and click gesture states consistent
This commit is contained in:
1 parent
f71e5531e0
commit
fc79fa7345
3 files changed
+76
-75
No files matched your search
+52
-52
@@ -158,62 +158,62 @@ fun MessageBubble(message: Message, playbackState: ChatViewModel.PlaybackState =
|
||||
horizontalArrangement = alignment
|
||||
) {
|
||||
MessageWithFooter(footer) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.widthIn(min = minBubbleWidth, max = if (isVoice) voiceMaxWidth else 300.dp)
|
||||
// Тень рисуем до clip, чтобы выступающие справа и снизу края оставались видны.
|
||||
.drawBehind {
|
||||
val offset = 2.dp.toPx()
|
||||
val radius = 12.dp.toPx()
|
||||
drawRoundRect(ChatColors.BubbleShadow, topLeft = Offset(offset, offset), size = size,
|
||||
cornerRadius = CornerRadius(radius, radius))
|
||||
}
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(bgColor)
|
||||
.padding(12.dp)
|
||||
) {
|
||||
if (!isVoice) Text(if (message.isOutgoing) stringResource(R.string.you) else message.author,
|
||||
fontSize = 13.sp, fontWeight = FontWeight.Bold, color = ChatColors.Secondary)
|
||||
Spacer(Modifier.height(2.dp))
|
||||
|
||||
when {
|
||||
message.inviteGroupName != null -> {
|
||||
val clipboard = LocalClipboardManager.current
|
||||
LaunchedEffect(message.id, message.inviteGroupName) {
|
||||
LogManager.addLog("DEBUG", "PM", "invitation rendered: seq=${message.id} " +
|
||||
"group_name_bytes=${message.inviteGroupName.toByteArray().size} link_bytes=${message.text.length}")
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.widthIn(min = minBubbleWidth, max = if (isVoice) voiceMaxWidth else 300.dp)
|
||||
// Тень рисуем до clip, чтобы выступающие справа и снизу края оставались видны.
|
||||
.drawBehind {
|
||||
val offset = 2.dp.toPx()
|
||||
val radius = 12.dp.toPx()
|
||||
drawRoundRect(ChatColors.BubbleShadow, topLeft = Offset(offset, offset), size = size,
|
||||
cornerRadius = CornerRadius(radius, radius))
|
||||
}
|
||||
Text(stringResource(R.string.group_invitation, message.inviteGroupName), fontSize = 17.sp, color = textColor)
|
||||
Text(message.text, fontSize = 17.sp, color = Color(0xFF3390EC), textDecoration = TextDecoration.Underline,
|
||||
maxLines = 1, overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.fillMaxWidth().combinedClickable(
|
||||
onClick = { onLink(message.text) },
|
||||
onLongClickLabel = stringResource(R.string.copy_link),
|
||||
onLongClick = {
|
||||
clipboard.setText(AnnotatedString(message.text))
|
||||
LogManager.addLog("DEBUG", "PM", "invitation link copied: seq=${message.id} bytes=${message.text.length}")
|
||||
}))
|
||||
}
|
||||
isVoice -> VoiceBubble(message, textColor, isActive, playbackState, onPlayVoice, onSeekVoice, onDownloadFile)
|
||||
isVideo -> VideoBubble(message, textColor, onDownloadFile, onPlayVideo)
|
||||
isImage -> ImageBubble(message, textColor, onDownloadFile, onOpenImage)
|
||||
isMedia -> FileBubble(message, textColor, onDownloadFile, onOpenFile)
|
||||
else -> {
|
||||
EmojiText(message.text, fontSize = 17.sp, color = textColor, onLink = onLink)
|
||||
}
|
||||
}
|
||||
|
||||
if (!isVoice) {
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(bgColor)
|
||||
.padding(12.dp)
|
||||
) {
|
||||
if (!isVoice) Text(if (message.isOutgoing) stringResource(R.string.you) else message.author,
|
||||
fontSize = 13.sp, fontWeight = FontWeight.Bold, color = ChatColors.Secondary)
|
||||
Spacer(Modifier.height(2.dp))
|
||||
Text(
|
||||
listOf(formatTime(message.ts), deliveryLabel(message)).filter { it.isNotEmpty() }.joinToString(" · "),
|
||||
fontSize = 11.sp,
|
||||
color = ChatColors.Secondary,
|
||||
modifier = Modifier.align(Alignment.End)
|
||||
)
|
||||
|
||||
when {
|
||||
message.inviteGroupName != null -> {
|
||||
val clipboard = LocalClipboardManager.current
|
||||
LaunchedEffect(message.id, message.inviteGroupName) {
|
||||
LogManager.addLog("DEBUG", "PM", "invitation rendered: seq=${message.id} " +
|
||||
"group_name_bytes=${message.inviteGroupName.toByteArray().size} link_bytes=${message.text.length}")
|
||||
}
|
||||
Text(stringResource(R.string.group_invitation, message.inviteGroupName), fontSize = 17.sp, color = textColor)
|
||||
Text(message.text, fontSize = 17.sp, color = Color(0xFF3390EC), textDecoration = TextDecoration.Underline,
|
||||
maxLines = 1, overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.fillMaxWidth().combinedClickable(
|
||||
onClick = { onLink(message.text) },
|
||||
onLongClickLabel = stringResource(R.string.copy_link),
|
||||
onLongClick = {
|
||||
clipboard.setText(AnnotatedString(message.text))
|
||||
LogManager.addLog("DEBUG", "PM", "invitation link copied: seq=${message.id} bytes=${message.text.length}")
|
||||
}))
|
||||
}
|
||||
isVoice -> VoiceBubble(message, textColor, isActive, playbackState, onPlayVoice, onSeekVoice, onDownloadFile)
|
||||
isVideo -> VideoBubble(message, textColor, onDownloadFile, onPlayVideo)
|
||||
isImage -> ImageBubble(message, textColor, onDownloadFile, onOpenImage)
|
||||
isMedia -> FileBubble(message, textColor, onDownloadFile, onOpenFile)
|
||||
else -> {
|
||||
EmojiText(message.text, fontSize = 17.sp, color = textColor, onLink = onLink)
|
||||
}
|
||||
}
|
||||
|
||||
if (!isVoice) {
|
||||
Spacer(Modifier.height(2.dp))
|
||||
Text(
|
||||
listOf(formatTime(message.ts), deliveryLabel(message)).filter { it.isNotEmpty() }.joinToString(" · "),
|
||||
fontSize = 11.sp,
|
||||
color = ChatColors.Secondary,
|
||||
modifier = Modifier.align(Alignment.End)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@Composable
|
||||
|
||||
@@ -60,31 +60,31 @@ fun PollMessageBubble(message: Message, onVote: (Message, List<Int>, String) ->
|
||||
val poll = message.feedback?.poll
|
||||
Row(Modifier.fillMaxWidth().padding(horizontal = 8.dp, vertical = 2.dp),
|
||||
horizontalArrangement = if (message.isOutgoing) Arrangement.End else Arrangement.Start) {
|
||||
MessageWithFooter(footer) {
|
||||
Card(onClick = { if (poll != null) showVote = true }, modifier = Modifier.widthIn(max = 300.dp),
|
||||
colors = CardDefaults.cardColors(containerColor = if (message.isOutgoing) MaterialTheme.colorScheme.primaryContainer
|
||||
else MaterialTheme.colorScheme.surfaceVariant)) {
|
||||
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
if (!message.isOutgoing) Text(message.author, style = MaterialTheme.typography.labelMedium)
|
||||
if (poll == null) Text(stringResource(R.string.poll_pending))
|
||||
else {
|
||||
Text(poll.question, style = MaterialTheme.typography.titleMedium)
|
||||
Text(stringResource(if (poll.multiple) R.string.poll_multiple else R.string.poll_single),
|
||||
style = MaterialTheme.typography.labelSmall)
|
||||
for (option in poll.options) {
|
||||
Text(option.text)
|
||||
LinearProgressIndicator(progress = { (option.percent / 100).toFloat().coerceIn(0f, 1f) },
|
||||
modifier = Modifier.fillMaxWidth())
|
||||
Text(stringResource(R.string.poll_option_result, option.count, option.percent),
|
||||
style = MaterialTheme.typography.labelSmall)
|
||||
MessageWithFooter(footer) {
|
||||
Card(onClick = { if (poll != null) showVote = true }, modifier = Modifier.widthIn(max = 300.dp),
|
||||
colors = CardDefaults.cardColors(containerColor = if (message.isOutgoing) MaterialTheme.colorScheme.primaryContainer
|
||||
else MaterialTheme.colorScheme.surfaceVariant)) {
|
||||
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
if (!message.isOutgoing) Text(message.author, style = MaterialTheme.typography.labelMedium)
|
||||
if (poll == null) Text(stringResource(R.string.poll_pending))
|
||||
else {
|
||||
Text(poll.question, style = MaterialTheme.typography.titleMedium)
|
||||
Text(stringResource(if (poll.multiple) R.string.poll_multiple else R.string.poll_single),
|
||||
style = MaterialTheme.typography.labelSmall)
|
||||
for (option in poll.options) {
|
||||
Text(option.text)
|
||||
LinearProgressIndicator(progress = { (option.percent / 100).toFloat().coerceIn(0f, 1f) },
|
||||
modifier = Modifier.fillMaxWidth())
|
||||
Text(stringResource(R.string.poll_option_result, option.count, option.percent),
|
||||
style = MaterialTheme.typography.labelSmall)
|
||||
}
|
||||
Text(stringResource(R.string.poll_voters, poll.voters))
|
||||
TextButton(onClick = { showVote = true }) { Text(stringResource(R.string.poll_vote_results)) }
|
||||
}
|
||||
}
|
||||
Text(stringResource(R.string.poll_voters, poll.voters))
|
||||
TextButton(onClick = { showVote = true }) { Text(stringResource(R.string.poll_vote_results)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (showVote && poll != null) PollVoteDialog(message, poll, { showVote = false }, onVote, authorName)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user