From 8839015249998a0412138343ac0a77fa49772eb8 Mon Sep 17 00:00:00 2001 From: Danil Nikolaev Date: Mon, 15 Dec 2025 22:58:50 +0300 Subject: [PATCH] Fix: Ensure sender's name truncates correctly in incoming messages This commit resolves an issue where the sender's name in incoming message bubbles would not truncate properly, potentially breaking the layout. The fix ensures the name text correctly adapts to the width of the message bubble. Additionally, this change introduces `ImmutableList` for message attachments to improve performance and refactors where the conversion to `ImmutableList` happens, moving it into the `MessageMapper`. Key changes: - The `MessageBubble` now reports its width, allowing the sender's name `Text` to be constrained correctly. - Sender's name now uses `labelMedium` typography. - Enabled showing the sender's name by default in `MessagesHistoryViewModelImpl`. - Changed `UiItem.Message.attachments` from `List` to `ImmutableList` for better Compose performance. - Moved the `toImmutableList()` conversion for attachments into the `MessageMapper`. --- .../MessagesHistoryViewModelImpl.kt | 2 +- .../fast/messageshistory/model/UiItem.kt | 3 ++- .../presentation/MessageBubble.kt | 7 ++++- .../presentation/MessageBubbleIncoming.kt | 26 ++++++++++++++----- .../presentation/MessageBubbleOutgoing.kt | 2 +- .../messageshistory/util/MessageMapper.kt | 3 ++- 6 files changed, 32 insertions(+), 11 deletions(-) diff --git a/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/MessagesHistoryViewModelImpl.kt b/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/MessagesHistoryViewModelImpl.kt index 6b1c6fa3..6a9f3714 100644 --- a/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/MessagesHistoryViewModelImpl.kt +++ b/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/MessagesHistoryViewModelImpl.kt @@ -1231,7 +1231,7 @@ class MessagesHistoryViewModelImpl( val newUiMessages = messages.mapIndexed { index, message -> message.asPresentation( resourceProvider = resourceProvider, - showName = false, + showName = true, prevMessage = messages.getOrNull(index + 1), nextMessage = messages.getOrNull(index - 1), showTimeInActionMessages = AppSettings.Experimental.showTimeInActionMessages, diff --git a/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/model/UiItem.kt b/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/model/UiItem.kt index ab710e20..6ba346c7 100644 --- a/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/model/UiItem.kt +++ b/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/model/UiItem.kt @@ -4,6 +4,7 @@ import androidx.compose.runtime.Stable import androidx.compose.ui.text.AnnotatedString import dev.meloda.fast.common.model.UiImage import dev.meloda.fast.model.api.domain.VkAttachment +import dev.meloda.fast.ui.util.ImmutableList sealed class UiItem( open val id: Long, @@ -31,7 +32,7 @@ sealed class UiItem( val isSelected: Boolean, val isPinned: Boolean, val isImportant: Boolean, - val attachments: List?, + val attachments: ImmutableList?, val replyCmId: Long?, val replyTitle: String?, val replySummary: String? diff --git a/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/presentation/MessageBubble.kt b/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/presentation/MessageBubble.kt index 98d3c822..2ace12a4 100644 --- a/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/presentation/MessageBubble.kt +++ b/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/presentation/MessageBubble.kt @@ -27,6 +27,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.tooling.preview.Preview @@ -58,7 +59,8 @@ fun MessageBubble( replySummary: String? = null, onClick: (VkAttachment) -> Unit = {}, onLongClick: (VkAttachment) -> Unit = {}, - onReplyClick: () -> Unit = {} + onReplyClick: () -> Unit = {}, + onBubbleWidthChange: (Int) -> Unit = {}, ) { val density = LocalDensity.current @@ -149,6 +151,9 @@ fun MessageBubble( .onGloballyPositioned { bubbleContainerWidth = it.size.width } + .onSizeChanged { + onBubbleWidthChange(it.width) + } .widthIn(min = if (shouldFill) attachmentsContainerWidth.dp else 56.dp) .clip( RoundedCornerShape( diff --git a/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/presentation/MessageBubbleIncoming.kt b/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/presentation/MessageBubbleIncoming.kt index 7954defd..663d370f 100644 --- a/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/presentation/MessageBubbleIncoming.kt +++ b/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/presentation/MessageBubbleIncoming.kt @@ -20,12 +20,16 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.runtime.setValue 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.platform.LocalContext +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.IntOffset @@ -36,7 +40,6 @@ import com.conena.nanokt.android.content.dpInPx import dev.meloda.fast.messageshistory.model.UiItem import dev.meloda.fast.model.api.domain.VkAttachment import dev.meloda.fast.ui.R -import dev.meloda.fast.ui.util.ImmutableList.Companion.toImmutableList import kotlin.math.roundToInt @Composable @@ -49,10 +52,16 @@ fun IncomingMessageBubble( onLongClick: (VkAttachment) -> Unit = {}, onReplyClick: () -> Unit = {} ) { + val density = LocalDensity.current + val currentOnClick by rememberUpdatedState(onClick) val currentOnLongClick by rememberUpdatedState(onLongClick) val currentOnReplyClick by rememberUpdatedState(onReplyClick) + var bubbleContainerWidth by remember { + mutableStateOf(0.dp) + } + Row( modifier = modifier .fillMaxWidth() @@ -103,9 +112,12 @@ fun IncomingMessageBubble( Text( modifier = Modifier .padding(start = 12.dp) - .widthIn(max = 140.dp), + .widthIn( + max = (bubbleContainerWidth.takeIf { it > 0.dp } + ?: 140.dp) - 24.dp + ), text = message.name, - style = MaterialTheme.typography.bodySmall, + style = MaterialTheme.typography.labelMedium, color = MaterialTheme.colorScheme.primary, maxLines = 1, overflow = TextOverflow.Ellipsis, @@ -123,14 +135,16 @@ fun IncomingMessageBubble( isPinned = message.isPinned, isImportant = message.isImportant, isSelected = message.isSelected, - attachments = message.attachments?.toImmutableList(), + attachments = message.attachments, replyTitle = message.replyTitle, replySummary = message.replySummary, onClick = currentOnClick, onLongClick = currentOnLongClick, - onReplyClick = currentOnReplyClick + onReplyClick = currentOnReplyClick, + onBubbleWidthChange = { + bubbleContainerWidth = with(density) { it.toDp() } + } ) - } } Spacer(modifier = Modifier.fillMaxWidth(0.25f)) diff --git a/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/presentation/MessageBubbleOutgoing.kt b/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/presentation/MessageBubbleOutgoing.kt index 7ca744e5..9e38a9f1 100644 --- a/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/presentation/MessageBubbleOutgoing.kt +++ b/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/presentation/MessageBubbleOutgoing.kt @@ -80,7 +80,7 @@ fun OutgoingMessageBubble( isPinned = message.isPinned, isImportant = message.isImportant, isSelected = message.isSelected, - attachments = message.attachments?.toImmutableList(), + attachments = message.attachments, replyTitle = message.replyTitle, replySummary = message.replySummary, onClick = currentOnClick, diff --git a/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/util/MessageMapper.kt b/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/util/MessageMapper.kt index 93bbb03a..12ae68ca 100644 --- a/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/util/MessageMapper.kt +++ b/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/util/MessageMapper.kt @@ -24,6 +24,7 @@ import dev.meloda.fast.model.api.domain.FormatDataType import dev.meloda.fast.model.api.domain.VkConversation import dev.meloda.fast.model.api.domain.VkMessage import dev.meloda.fast.ui.R +import dev.meloda.fast.ui.util.ImmutableList.Companion.toImmutableList import java.text.SimpleDateFormat import java.util.Locale @@ -156,7 +157,7 @@ fun VkMessage.asPresentation( isSelected = isSelected, isPinned = isPinned, isImportant = isImportant, - attachments = attachments?.ifEmpty { null }, + attachments = attachments?.ifEmpty { null }?.toImmutableList(), replyCmId = replyMessage?.cmId, replyTitle = extractReplyTitle(), replySummary = extractReplySummary()