Поправлен визуал пузырьков со звонками

This commit is contained in:
2026-03-27 17:22:51 +05:00
parent 51f76b5073
commit 39b0b0e107
6 changed files with 43 additions and 72 deletions

View File

@@ -1277,7 +1277,8 @@ fun MainScreen(
isDarkTheme = isDarkTheme, isDarkTheme = isDarkTheme,
chatWallpaperId = chatWallpaperId, chatWallpaperId = chatWallpaperId,
avatarRepository = avatarRepository, avatarRepository = avatarRepository,
onImageViewerChanged = { isLocked -> isChatSwipeLocked = isLocked } onImageViewerChanged = { isLocked -> isChatSwipeLocked = isLocked },
isCallActive = callUiState.isVisible
) )
} }
} }

View File

@@ -199,7 +199,7 @@ object CallManager {
updateState { updateState {
it.copy( it.copy(
phase = CallPhase.OUTGOING, phase = CallPhase.OUTGOING,
statusText = "Calling..." statusText = "Calling"
) )
} }

View File

@@ -295,7 +295,8 @@ fun ChatDetailScreen(
isDarkTheme: Boolean, isDarkTheme: Boolean,
chatWallpaperId: String = "", chatWallpaperId: String = "",
avatarRepository: AvatarRepository? = null, avatarRepository: AvatarRepository? = null,
onImageViewerChanged: (Boolean) -> Unit = {} onImageViewerChanged: (Boolean) -> Unit = {},
isCallActive: Boolean = false
) { ) {
val viewModel: ChatViewModel = viewModel(key = "chat_${user.publicKey}") val viewModel: ChatViewModel = viewModel(key = "chat_${user.publicKey}")
val context = LocalContext.current val context = LocalContext.current
@@ -381,6 +382,13 @@ fun ChatDetailScreen(
// Логирование изменений selection mode // Логирование изменений selection mode
LaunchedEffect(isSelectionMode, selectedMessages.size) {} LaunchedEffect(isSelectionMode, selectedMessages.size) {}
// Сброс выделения при начале звонка
LaunchedEffect(isCallActive) {
if (isCallActive) {
selectedMessages = emptySet()
}
}
// 🔥 Backup: если клавиатура ещё открыта когда selection mode активировался // 🔥 Backup: если клавиатура ещё открыта когда selection mode активировался
// (клавиатура уже должна быть закрыта в onLongClick, это только backup) // (клавиатура уже должна быть закрыта в onLongClick, это только backup)
LaunchedEffect(isSelectionMode) { LaunchedEffect(isSelectionMode) {
@@ -2974,7 +2982,7 @@ fun ChatDetailScreen(
avatarRepository = avatarRepository =
avatarRepository, avatarRepository,
onLongClick = { onLongClick = {
if (simplePickerPreviewUri != null) { if (simplePickerPreviewUri != null || isCallActive) {
return@MessageBubble return@MessageBubble
} }
// 📳 Haptic feedback при долгом нажатии // 📳 Haptic feedback при долгом нажатии
@@ -3017,7 +3025,7 @@ fun ChatDetailScreen(
) )
}, },
onClick = { onClick = {
if (simplePickerPreviewUri != null) { if (simplePickerPreviewUri != null || isCallActive) {
return@MessageBubble return@MessageBubble
} }
if (shouldIgnoreTapAfterLongPress( if (shouldIgnoreTapAfterLongPress(
@@ -3039,12 +3047,17 @@ fun ChatDetailScreen(
message.attachments.all { message.attachments.all {
it.type == AttachmentType.IMAGE it.type == AttachmentType.IMAGE
} }
val isCallMessage =
message.attachments.isNotEmpty() &&
message.attachments.all {
it.type == AttachmentType.CALL
}
if (isSelectionMode) { if (isSelectionMode) {
toggleMessageSelection( toggleMessageSelection(
selectionKey, selectionKey,
!hasAvatar !hasAvatar
) )
} else if (!hasAvatar && !isPhotoOnly) { } else if (!hasAvatar && (!isPhotoOnly || isCallMessage)) {
// 💬 Tap = context menu // 💬 Tap = context menu
contextMenuMessage = message contextMenuMessage = message
showContextMenu = true showContextMenu = true

View File

@@ -1593,7 +1593,7 @@ private fun resolveDesktopCallUi(preview: String, isOutgoing: Boolean): DesktopC
} }
val subtitle = val subtitle =
if (isError) { if (isError) {
"Call was not answered or was rejected" if (isOutgoing) "Rejected" else "Missed"
} else { } else {
formatDesktopCallDuration(durationSec) formatDesktopCallDuration(durationSec)
} }
@@ -1612,19 +1612,14 @@ fun CallAttachment(
val callUi = remember(attachment.preview, isOutgoing) { val callUi = remember(attachment.preview, isOutgoing) {
resolveDesktopCallUi(attachment.preview, isOutgoing) resolveDesktopCallUi(attachment.preview, isOutgoing)
} }
val containerShape = RoundedCornerShape(10.dp) val containerShape = RoundedCornerShape(17.dp)
val containerBackground = val containerBackground =
if (isOutgoing) { if (isOutgoing) {
Color.White.copy(alpha = 0.12f) PrimaryBlue
} else { } else {
if (isDarkTheme) Color(0xFF1F2733) else Color(0xFFF3F8FF) if (isDarkTheme) Color(0xFF212121) else Color(0xFFF5F5F5)
}
val containerBorder =
if (isOutgoing) {
Color.White.copy(alpha = 0.2f)
} else {
if (isDarkTheme) Color(0xFF33435A) else Color(0xFFD8E5F4)
} }
val containerBorder = Color.Transparent
val iconBackground = if (callUi.isError) Color(0xFFE55A5A) else PrimaryBlue val iconBackground = if (callUi.isError) Color(0xFFE55A5A) else PrimaryBlue
val iconVector = val iconVector =
when { when {
@@ -1690,59 +1685,6 @@ fun CallAttachment(
) )
} }
if (isOutgoing) {
Spacer(modifier = Modifier.width(8.dp))
Row(verticalAlignment = Alignment.CenterVertically) {
Text(
text = android.text.format.DateFormat.format("HH:mm", timestamp).toString(),
fontSize = 11.sp,
color = Color.White.copy(alpha = 0.7f)
)
Spacer(modifier = Modifier.width(4.dp))
when (messageStatus) {
MessageStatus.SENDING -> {
Icon(
painter = TelegramIcons.Clock,
contentDescription = null,
tint = Color.White.copy(alpha = 0.7f),
modifier = Modifier.size(14.dp)
)
}
MessageStatus.SENT, MessageStatus.DELIVERED -> {
Icon(
painter = TelegramIcons.Done,
contentDescription = null,
tint = Color.White.copy(alpha = 0.8f),
modifier = Modifier.size(14.dp)
)
}
MessageStatus.READ -> {
Box(modifier = Modifier.height(14.dp)) {
Icon(
painter = TelegramIcons.Done,
contentDescription = null,
tint = Color.White,
modifier = Modifier.size(14.dp)
)
Icon(
painter = TelegramIcons.Done,
contentDescription = null,
tint = Color.White,
modifier = Modifier.size(14.dp).offset(x = 4.dp)
)
}
}
MessageStatus.ERROR -> {
Icon(
imageVector = Icons.Default.Error,
contentDescription = null,
tint = Color(0xFFE53935),
modifier = Modifier.size(14.dp)
)
}
}
}
}
} }
} }
} }

View File

@@ -666,6 +666,15 @@ fun MessageBubble(
.IMAGE .IMAGE
} }
val isCallMessage =
message.attachments.isNotEmpty() &&
message.text.isEmpty() &&
message.replyData == null &&
message.forwardedMessages.isEmpty() &&
message.attachments.all {
it.type == AttachmentType.CALL
}
val isStandaloneGroupInvite = val isStandaloneGroupInvite =
message.attachments.isEmpty() && message.attachments.isEmpty() &&
message.replyData == null && message.replyData == null &&
@@ -794,7 +803,8 @@ fun MessageBubble(
onLongClick = onLongClick onLongClick = onLongClick
) )
.then( .then(
if (false) { if (isCallMessage) {
// Звонки без фонового пузырька — у них свой контейнер внутри CallAttachment
Modifier Modifier
} else { } else {
Modifier.clip(bubbleShape) Modifier.clip(bubbleShape)

View File

@@ -655,9 +655,14 @@ fun MessageInputBar(
val hasImageAttachment = msg.attachments.any { val hasImageAttachment = msg.attachments.any {
it.type == AttachmentType.IMAGE it.type == AttachmentType.IMAGE
} }
val hasCallAttachment = msg.attachments.any {
it.type == AttachmentType.CALL
}
AppleEmojiText( AppleEmojiText(
text = if (panelReplyMessages.size == 1) { text = if (panelReplyMessages.size == 1) {
if (msg.text.isEmpty() && hasImageAttachment) { if (msg.text.isEmpty() && hasCallAttachment) {
"Call"
} else if (msg.text.isEmpty() && hasImageAttachment) {
"Photo" "Photo"
} else { } else {
val shortText = msg.text.take(40) val shortText = msg.text.take(40)