fix: adjust image dimensions and bubble width for better layout in chat components
This commit is contained in:
@@ -48,9 +48,51 @@ import compose.icons.tablericons.*
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
private const val TAG = "AttachmentComponents"
|
private const val TAG = "AttachmentComponents"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 📐 Telegram Bubble Specification
|
||||||
|
* Все константы взяты из ChatMessageCell.java и Theme.java
|
||||||
|
*/
|
||||||
|
object TelegramBubbleSpec {
|
||||||
|
// === BUBBLE RADIUS ===
|
||||||
|
val bubbleRadius = 17.dp // chat_msgTextPaintOneEmoji
|
||||||
|
val nearRadius = 6.dp // smallRad для хвостика
|
||||||
|
val minRadius = 6.dp // Минимальный радиус
|
||||||
|
val photoRadius = 15.dp // bubbleRadius - 2 для медиа
|
||||||
|
|
||||||
|
// === PHOTO SIZING ===
|
||||||
|
// maxWidth = min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.5f
|
||||||
|
// Telegram использует ~50-65% ширины экрана
|
||||||
|
fun maxPhotoWidth(screenWidth: Int): Int = (screenWidth * 0.65f).toInt()
|
||||||
|
val maxPhotoHeight = 360 // dp, примерно maxWidth + 100
|
||||||
|
val minPhotoWidth = 150 // dp
|
||||||
|
val minPhotoHeight = 120 // dp
|
||||||
|
|
||||||
|
// === CAPTION PADDING ===
|
||||||
|
val captionPaddingTop = 9.dp // Отступ от фото до caption
|
||||||
|
val captionPaddingBottom = 6.dp
|
||||||
|
val captionPaddingStartOutgoing = 11.dp // Исходящие
|
||||||
|
val captionPaddingStartIncoming = 13.dp // Входящие
|
||||||
|
val captionPaddingEnd = 10.dp
|
||||||
|
|
||||||
|
// === TIME OVERLAY (на медиа) ===
|
||||||
|
val timeOverlayPadding = 6.dp
|
||||||
|
val timeOverlayRadius = 10.dp
|
||||||
|
val timeOverlayAlpha = 0.5f
|
||||||
|
val timeTextSize = 11.sp
|
||||||
|
|
||||||
|
// === COLLAGE ===
|
||||||
|
val collageSpacing = 2.dp // Отступ между фото в коллаже
|
||||||
|
|
||||||
|
// === TEXT ===
|
||||||
|
val messageTextSize = 16.sp
|
||||||
|
val captionTextSize = 16.sp
|
||||||
|
}
|
||||||
|
|
||||||
/** Статус скачивания attachment (как в desktop) */
|
/** Статус скачивания attachment (как в desktop) */
|
||||||
enum class DownloadStatus {
|
enum class DownloadStatus {
|
||||||
DOWNLOADED,
|
DOWNLOADED,
|
||||||
@@ -78,6 +120,7 @@ fun MessageAttachments(
|
|||||||
avatarRepository: AvatarRepository? = null,
|
avatarRepository: AvatarRepository? = null,
|
||||||
currentUserPublicKey: String = "",
|
currentUserPublicKey: String = "",
|
||||||
hasCaption: Boolean = false, // Если есть caption - время показывается под фото, не на фото
|
hasCaption: Boolean = false, // Если есть caption - время показывается под фото, не на фото
|
||||||
|
showTail: Boolean = true, // Показывать хвостик пузырька
|
||||||
onImageClick: (attachmentId: String, bounds: ImageSourceBounds?) -> Unit = { _, _ -> },
|
onImageClick: (attachmentId: String, bounds: ImageSourceBounds?) -> Unit = { _, _ -> },
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
@@ -103,6 +146,7 @@ fun MessageAttachments(
|
|||||||
timestamp = timestamp,
|
timestamp = timestamp,
|
||||||
messageStatus = messageStatus,
|
messageStatus = messageStatus,
|
||||||
hasCaption = hasCaption,
|
hasCaption = hasCaption,
|
||||||
|
showTail = showTail,
|
||||||
onImageClick = onImageClick
|
onImageClick = onImageClick
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -162,32 +206,35 @@ fun ImageCollage(
|
|||||||
timestamp: java.util.Date,
|
timestamp: java.util.Date,
|
||||||
messageStatus: MessageStatus = MessageStatus.READ,
|
messageStatus: MessageStatus = MessageStatus.READ,
|
||||||
hasCaption: Boolean = false, // Если есть caption - время показывается под фото
|
hasCaption: Boolean = false, // Если есть caption - время показывается под фото
|
||||||
|
showTail: Boolean = true, // Показывать хвостик пузырька
|
||||||
onImageClick: (attachmentId: String, bounds: ImageSourceBounds?) -> Unit = { _, _ -> },
|
onImageClick: (attachmentId: String, bounds: ImageSourceBounds?) -> Unit = { _, _ -> },
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val count = attachments.size
|
val count = attachments.size
|
||||||
val spacing = 2.dp
|
val spacing = TelegramBubbleSpec.collageSpacing
|
||||||
|
|
||||||
// Показываем время и статус только если нет caption
|
// Показываем время и статус только если нет caption
|
||||||
val showOverlayOnLast = !hasCaption
|
val showOverlayOnLast = !hasCaption
|
||||||
|
|
||||||
// Закругление: если есть caption - только сверху, снизу прямые углы
|
// Telegram: bubbleRadius=17dp, photoRadius=15dp (bubbleRadius-2), minRadius=6dp
|
||||||
val collageShape =
|
val collageShape =
|
||||||
if (hasCaption) {
|
if (hasCaption) {
|
||||||
|
// Фото над caption: верх=15dp, низ=0dp (плавный переход к caption)
|
||||||
RoundedCornerShape(
|
RoundedCornerShape(
|
||||||
topStart = 16.dp,
|
topStart = TelegramBubbleSpec.photoRadius,
|
||||||
topEnd = 16.dp,
|
topEnd = TelegramBubbleSpec.photoRadius,
|
||||||
bottomStart = 0.dp,
|
bottomStart = 0.dp,
|
||||||
bottomEnd = 0.dp
|
bottomEnd = 0.dp
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
RoundedCornerShape(16.dp)
|
// Фото без caption - простое закругление
|
||||||
|
RoundedCornerShape(TelegramBubbleSpec.photoRadius)
|
||||||
}
|
}
|
||||||
|
|
||||||
Box(modifier = modifier.clip(collageShape)) {
|
Box(modifier = modifier.clip(collageShape)) {
|
||||||
when (count) {
|
when (count) {
|
||||||
1 -> {
|
1 -> {
|
||||||
// Одно фото - полная ширина
|
// Одно фото - размер определяется самим фото (Telegram style)
|
||||||
ImageAttachment(
|
ImageAttachment(
|
||||||
attachment = attachments[0],
|
attachment = attachments[0],
|
||||||
chachaKey = chachaKey,
|
chachaKey = chachaKey,
|
||||||
@@ -198,6 +245,7 @@ fun ImageCollage(
|
|||||||
timestamp = timestamp,
|
timestamp = timestamp,
|
||||||
messageStatus = messageStatus,
|
messageStatus = messageStatus,
|
||||||
showTimeOverlay = showOverlayOnLast,
|
showTimeOverlay = showOverlayOnLast,
|
||||||
|
hasCaption = hasCaption,
|
||||||
onImageClick = onImageClick
|
onImageClick = onImageClick
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -464,6 +512,7 @@ fun ImageAttachment(
|
|||||||
showTimeOverlay: Boolean = true,
|
showTimeOverlay: Boolean = true,
|
||||||
aspectRatio: Float? = null,
|
aspectRatio: Float? = null,
|
||||||
fillMaxSize: Boolean = false,
|
fillMaxSize: Boolean = false,
|
||||||
|
hasCaption: Boolean = false,
|
||||||
onImageClick: (attachmentId: String, bounds: ImageSourceBounds?) -> Unit = { _, _ -> }
|
onImageClick: (attachmentId: String, bounds: ImageSourceBounds?) -> Unit = { _, _ -> }
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
@@ -627,60 +676,63 @@ fun ImageAttachment(
|
|||||||
if (isDarkTheme) Color.White.copy(alpha = 0.1f) else Color.Black.copy(alpha = 0.08f)
|
if (isDarkTheme) Color.White.copy(alpha = 0.1f) else Color.Black.copy(alpha = 0.08f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 📐 Вычисляем размер пузырька на основе соотношения сторон изображения (как в Telegram)
|
// 📐 TELEGRAM-STYLE sizing (из ChatMessageCell.java)
|
||||||
// Используем размеры из attachment или из загруженного bitmap
|
// maxWidth = min(displaySize.x, displaySize.y) * 0.5f (~65% в портрете)
|
||||||
|
// Сохраняем aspect ratio, ограничиваем max/min размеры
|
||||||
|
val configuration = LocalConfiguration.current
|
||||||
|
val screenWidthDp = configuration.screenWidthDp
|
||||||
|
|
||||||
val actualWidth = if (attachment.width > 0) attachment.width else imageBitmap?.width ?: 0
|
val actualWidth = if (attachment.width > 0) attachment.width else imageBitmap?.width ?: 0
|
||||||
val actualHeight = if (attachment.height > 0) attachment.height else imageBitmap?.height ?: 0
|
val actualHeight = if (attachment.height > 0) attachment.height else imageBitmap?.height ?: 0
|
||||||
|
|
||||||
val (imageWidth, imageHeight) =
|
val (imageWidth, imageHeight) =
|
||||||
remember(actualWidth, actualHeight, fillMaxSize, aspectRatio) {
|
remember(actualWidth, actualHeight, fillMaxSize, aspectRatio, screenWidthDp) {
|
||||||
// Если fillMaxSize - используем 100% контейнера
|
|
||||||
if (fillMaxSize) {
|
if (fillMaxSize) {
|
||||||
null to null
|
null to null
|
||||||
} else {
|
} else {
|
||||||
// Telegram-style размеры - больше и адаптивнее
|
// Telegram: maxWidth = screenWidth * 0.65
|
||||||
val maxWidth = 280.dp
|
val maxPhotoWidthPx = TelegramBubbleSpec.maxPhotoWidth(screenWidthDp)
|
||||||
val maxHeight = 400.dp // Увеличено для вертикальных фото
|
val maxPhotoWidth = maxPhotoWidthPx.dp
|
||||||
val minWidth = 160.dp
|
val maxPhotoHeight = TelegramBubbleSpec.maxPhotoHeight.dp
|
||||||
val minHeight = 120.dp
|
val minHeight = TelegramBubbleSpec.minPhotoHeight.dp
|
||||||
|
val minWidth = TelegramBubbleSpec.minPhotoWidth.dp
|
||||||
|
|
||||||
if (actualWidth > 0 && actualHeight > 0) {
|
if (actualWidth > 0 && actualHeight > 0) {
|
||||||
val ar = actualWidth.toFloat() / actualHeight.toFloat()
|
val ar = actualWidth.toFloat() / actualHeight.toFloat()
|
||||||
|
|
||||||
when {
|
// Telegram: scale = imageW / photoWidth, пропорционально масштабируем
|
||||||
// Очень широкое изображение (panorama)
|
var w: Float
|
||||||
ar > 2.0f -> {
|
var h: Float
|
||||||
val width = maxWidth
|
|
||||||
val height = (maxWidth.value / ar).dp.coerceIn(minHeight, maxHeight)
|
if (ar >= 1f) {
|
||||||
width to height
|
// Горизонтальное фото - по ширине
|
||||||
}
|
w = maxPhotoWidthPx.toFloat()
|
||||||
// Широкое изображение (landscape)
|
h = w / ar
|
||||||
ar > 1.2f -> {
|
} else {
|
||||||
val width = maxWidth
|
// Вертикальное фото - уменьшаем ширину
|
||||||
val height = (maxWidth.value / ar).dp.coerceIn(minHeight, maxHeight)
|
w = maxPhotoWidthPx * 0.75f
|
||||||
width to height
|
h = w / ar
|
||||||
}
|
|
||||||
// Очень высокое изображение (story-like)
|
|
||||||
ar < 0.5f -> {
|
|
||||||
val height = maxHeight
|
|
||||||
val width = (maxHeight.value * ar).dp.coerceIn(minWidth, maxWidth)
|
|
||||||
width to height
|
|
||||||
}
|
|
||||||
// Высокое изображение (portrait)
|
|
||||||
ar < 0.85f -> {
|
|
||||||
val height = maxHeight
|
|
||||||
val width = (maxHeight.value * ar).dp.coerceIn(minWidth, maxWidth)
|
|
||||||
width to height
|
|
||||||
}
|
|
||||||
// Квадратное или близкое к квадрату
|
|
||||||
else -> {
|
|
||||||
val size = 240.dp
|
|
||||||
size to size
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ограничение максимальной высоты
|
||||||
|
if (h > TelegramBubbleSpec.maxPhotoHeight) {
|
||||||
|
h = TelegramBubbleSpec.maxPhotoHeight.toFloat()
|
||||||
|
w = h * ar
|
||||||
|
}
|
||||||
|
|
||||||
|
// Минимальная высота
|
||||||
|
if (h < TelegramBubbleSpec.minPhotoHeight) {
|
||||||
|
h = TelegramBubbleSpec.minPhotoHeight.toFloat()
|
||||||
|
w = h * ar
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ограничения по ширине
|
||||||
|
w = w.coerceIn(TelegramBubbleSpec.minPhotoWidth.toFloat(), maxPhotoWidthPx.toFloat())
|
||||||
|
h = h.coerceIn(TelegramBubbleSpec.minPhotoHeight.toFloat(), TelegramBubbleSpec.maxPhotoHeight.toFloat())
|
||||||
|
|
||||||
|
w.dp to h.dp
|
||||||
} else {
|
} else {
|
||||||
// Fallback если размеры не указаны - квадрат средний
|
200.dp to 200.dp
|
||||||
240.dp to 240.dp
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -692,15 +744,28 @@ fun ImageAttachment(
|
|||||||
aspectRatio != null -> Modifier.fillMaxWidth().aspectRatio(aspectRatio)
|
aspectRatio != null -> Modifier.fillMaxWidth().aspectRatio(aspectRatio)
|
||||||
imageWidth != null && imageHeight != null ->
|
imageWidth != null && imageHeight != null ->
|
||||||
Modifier.width(imageWidth).height(imageHeight)
|
Modifier.width(imageWidth).height(imageHeight)
|
||||||
else -> Modifier.size(220.dp)
|
else -> Modifier.size(200.dp)
|
||||||
}
|
}
|
||||||
|
|
||||||
val cornerRadius = if (fillMaxSize) 0f else 12f
|
// Telegram: photoRadius = 15dp (bubbleRadius - 2)
|
||||||
|
// Если есть caption - нижние углы прямые
|
||||||
|
val photoRadius = TelegramBubbleSpec.photoRadius
|
||||||
|
val imageShape = when {
|
||||||
|
fillMaxSize -> RoundedCornerShape(0.dp)
|
||||||
|
hasCaption -> RoundedCornerShape(
|
||||||
|
topStart = photoRadius,
|
||||||
|
topEnd = photoRadius,
|
||||||
|
bottomStart = 0.dp,
|
||||||
|
bottomEnd = 0.dp
|
||||||
|
)
|
||||||
|
else -> RoundedCornerShape(photoRadius)
|
||||||
|
}
|
||||||
|
val cornerRadius = if (fillMaxSize || hasCaption) 0f else 15f
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier =
|
modifier =
|
||||||
sizeModifier
|
sizeModifier
|
||||||
.clip(RoundedCornerShape(cornerRadius.dp))
|
.clip(imageShape)
|
||||||
.background(Color.Transparent)
|
.background(Color.Transparent)
|
||||||
.onGloballyPositioned { coordinates ->
|
.onGloballyPositioned { coordinates ->
|
||||||
// Capture bounds for shared element transition
|
// Capture bounds for shared element transition
|
||||||
@@ -778,10 +843,10 @@ fun ImageAttachment(
|
|||||||
Box(
|
Box(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier.align(Alignment.BottomEnd)
|
Modifier.align(Alignment.BottomEnd)
|
||||||
.padding(8.dp)
|
.padding(TelegramBubbleSpec.timeOverlayPadding)
|
||||||
.background(
|
.background(
|
||||||
Color.Black.copy(alpha = 0.5f),
|
Color.Black.copy(alpha = TelegramBubbleSpec.timeOverlayAlpha),
|
||||||
shape = RoundedCornerShape(10.dp)
|
shape = RoundedCornerShape(TelegramBubbleSpec.timeOverlayRadius)
|
||||||
)
|
)
|
||||||
.padding(horizontal = 6.dp, vertical = 3.dp)
|
.padding(horizontal = 6.dp, vertical = 3.dp)
|
||||||
) {
|
) {
|
||||||
@@ -792,7 +857,7 @@ fun ImageAttachment(
|
|||||||
Text(
|
Text(
|
||||||
text = timeFormat.format(timestamp),
|
text = timeFormat.format(timestamp),
|
||||||
color = Color.White,
|
color = Color.White,
|
||||||
fontSize = 11.sp,
|
fontSize = TelegramBubbleSpec.timeTextSize,
|
||||||
fontWeight = FontWeight.Medium
|
fontWeight = FontWeight.Medium
|
||||||
)
|
)
|
||||||
if (isOutgoing) {
|
if (isOutgoing) {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import androidx.compose.ui.layout.Layout
|
|||||||
import androidx.compose.ui.layout.Measurable
|
import androidx.compose.ui.layout.Measurable
|
||||||
import androidx.compose.ui.layout.Placeable
|
import androidx.compose.ui.layout.Placeable
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.IntOffset
|
import androidx.compose.ui.unit.IntOffset
|
||||||
@@ -283,15 +284,18 @@ fun MessageBubble(
|
|||||||
else if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
|
else if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Telegram: bubbleRadius = 17dp, smallRad (хвостик) = 6dp
|
||||||
val bubbleShape =
|
val bubbleShape =
|
||||||
remember(message.isOutgoing, showTail) {
|
remember(message.isOutgoing, showTail) {
|
||||||
RoundedCornerShape(
|
RoundedCornerShape(
|
||||||
topStart = 18.dp,
|
topStart = TelegramBubbleSpec.bubbleRadius,
|
||||||
topEnd = 18.dp,
|
topEnd = TelegramBubbleSpec.bubbleRadius,
|
||||||
bottomStart =
|
bottomStart =
|
||||||
if (message.isOutgoing) 18.dp else (if (showTail) 4.dp else 18.dp),
|
if (message.isOutgoing) TelegramBubbleSpec.bubbleRadius
|
||||||
|
else (if (showTail) TelegramBubbleSpec.nearRadius else TelegramBubbleSpec.bubbleRadius),
|
||||||
bottomEnd =
|
bottomEnd =
|
||||||
if (message.isOutgoing) (if (showTail) 4.dp else 18.dp) else 18.dp
|
if (message.isOutgoing) (if (showTail) TelegramBubbleSpec.nearRadius else TelegramBubbleSpec.bubbleRadius)
|
||||||
|
else TelegramBubbleSpec.bubbleRadius
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,11 +451,49 @@ fun MessageBubble(
|
|||||||
}
|
}
|
||||||
val bubbleBorderWidth = if (hasOnlyMedia) 1.dp else 0.dp
|
val bubbleBorderWidth = if (hasOnlyMedia) 1.dp else 0.dp
|
||||||
|
|
||||||
|
// Telegram-style: ширина пузырька = ширина фото
|
||||||
|
// Caption переносится на новые строки, не расширяя пузырёк
|
||||||
|
val configuration = LocalConfiguration.current
|
||||||
|
val screenWidthDp = configuration.screenWidthDp
|
||||||
|
val maxPhotoWidth = TelegramBubbleSpec.maxPhotoWidth(screenWidthDp).dp
|
||||||
|
|
||||||
|
// Вычисляем ширину фото для ограничения пузырька
|
||||||
|
val photoWidth = if (hasImageWithCaption || hasOnlyMedia) {
|
||||||
|
val firstImage = message.attachments.firstOrNull {
|
||||||
|
it.type == com.rosetta.messenger.network.AttachmentType.IMAGE
|
||||||
|
}
|
||||||
|
if (firstImage != null && firstImage.width > 0 && firstImage.height > 0) {
|
||||||
|
val ar = firstImage.width.toFloat() / firstImage.height.toFloat()
|
||||||
|
val maxW = TelegramBubbleSpec.maxPhotoWidth(screenWidthDp).toFloat()
|
||||||
|
var w = if (ar >= 1f) maxW else maxW * 0.75f
|
||||||
|
var h = w / ar
|
||||||
|
|
||||||
|
if (h > TelegramBubbleSpec.maxPhotoHeight) {
|
||||||
|
h = TelegramBubbleSpec.maxPhotoHeight.toFloat()
|
||||||
|
w = h * ar
|
||||||
|
}
|
||||||
|
if (h < TelegramBubbleSpec.minPhotoHeight) {
|
||||||
|
h = TelegramBubbleSpec.minPhotoHeight.toFloat()
|
||||||
|
w = h * ar
|
||||||
|
}
|
||||||
|
w.coerceIn(TelegramBubbleSpec.minPhotoWidth.toFloat(), maxW).dp
|
||||||
|
} else {
|
||||||
|
maxPhotoWidth
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
280.dp
|
||||||
|
}
|
||||||
|
|
||||||
|
val bubbleWidthModifier = if (hasImageWithCaption || hasOnlyMedia) {
|
||||||
|
Modifier.widthIn(max = photoWidth) // Жёстко ограничиваем ширину размером фото
|
||||||
|
} else {
|
||||||
|
Modifier.widthIn(min = 60.dp, max = 280.dp)
|
||||||
|
}
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier.padding(end = 12.dp)
|
Modifier.padding(end = 12.dp)
|
||||||
.widthIn(min = 60.dp, max = 280.dp)
|
.then(bubbleWidthModifier)
|
||||||
.wrapContentWidth(unbounded = false)
|
|
||||||
.graphicsLayer {
|
.graphicsLayer {
|
||||||
this.alpha = selectionAlpha
|
this.alpha = selectionAlpha
|
||||||
this.scaleX = selectionScale
|
this.scaleX = selectionScale
|
||||||
@@ -516,19 +558,29 @@ fun MessageBubble(
|
|||||||
messageStatus = attachmentDisplayStatus,
|
messageStatus = attachmentDisplayStatus,
|
||||||
avatarRepository = avatarRepository,
|
avatarRepository = avatarRepository,
|
||||||
currentUserPublicKey = currentUserPublicKey,
|
currentUserPublicKey = currentUserPublicKey,
|
||||||
hasCaption = hasImageWithCaption, // Если есть caption - время на
|
hasCaption = hasImageWithCaption,
|
||||||
// пузырьке, не на фото
|
showTail = showTail, // Передаём для формы пузырька
|
||||||
onImageClick = onImageClick
|
onImageClick = onImageClick
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🖼️ Caption под фото (как в Telegram) - Telegram-style layout
|
// 🖼️ Caption под фото (Telegram-style)
|
||||||
|
// Отступ от фото = 9dp, слева = 11dp (исходящие) / 13dp (входящие)
|
||||||
if (hasImageWithCaption) {
|
if (hasImageWithCaption) {
|
||||||
|
val captionPaddingStart = if (message.isOutgoing)
|
||||||
|
TelegramBubbleSpec.captionPaddingStartOutgoing
|
||||||
|
else
|
||||||
|
TelegramBubbleSpec.captionPaddingStartIncoming
|
||||||
Box(
|
Box(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier.fillMaxWidth()
|
Modifier
|
||||||
.background(bubbleColor)
|
.background(bubbleColor)
|
||||||
.padding(horizontal = 10.dp, vertical = 6.dp)
|
.padding(
|
||||||
|
start = captionPaddingStart,
|
||||||
|
end = TelegramBubbleSpec.captionPaddingEnd,
|
||||||
|
top = TelegramBubbleSpec.captionPaddingTop,
|
||||||
|
bottom = TelegramBubbleSpec.captionPaddingBottom
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
TelegramStyleMessageContent(
|
TelegramStyleMessageContent(
|
||||||
textContent = {
|
textContent = {
|
||||||
|
|||||||
Reference in New Issue
Block a user