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

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,
chatWallpaperId = chatWallpaperId,
avatarRepository = avatarRepository,
onImageViewerChanged = { isLocked -> isChatSwipeLocked = isLocked }
onImageViewerChanged = { isLocked -> isChatSwipeLocked = isLocked },
isCallActive = callUiState.isVisible
)
}
}

View File

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

View File

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

View File

@@ -1593,7 +1593,7 @@ private fun resolveDesktopCallUi(preview: String, isOutgoing: Boolean): DesktopC
}
val subtitle =
if (isError) {
"Call was not answered or was rejected"
if (isOutgoing) "Rejected" else "Missed"
} else {
formatDesktopCallDuration(durationSec)
}
@@ -1612,19 +1612,14 @@ fun CallAttachment(
val callUi = remember(attachment.preview, isOutgoing) {
resolveDesktopCallUi(attachment.preview, isOutgoing)
}
val containerShape = RoundedCornerShape(10.dp)
val containerShape = RoundedCornerShape(17.dp)
val containerBackground =
if (isOutgoing) {
Color.White.copy(alpha = 0.12f)
PrimaryBlue
} else {
if (isDarkTheme) Color(0xFF1F2733) else Color(0xFFF3F8FF)
}
val containerBorder =
if (isOutgoing) {
Color.White.copy(alpha = 0.2f)
} else {
if (isDarkTheme) Color(0xFF33435A) else Color(0xFFD8E5F4)
if (isDarkTheme) Color(0xFF212121) else Color(0xFFF5F5F5)
}
val containerBorder = Color.Transparent
val iconBackground = if (callUi.isError) Color(0xFFE55A5A) else PrimaryBlue
val iconVector =
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
}
val isCallMessage =
message.attachments.isNotEmpty() &&
message.text.isEmpty() &&
message.replyData == null &&
message.forwardedMessages.isEmpty() &&
message.attachments.all {
it.type == AttachmentType.CALL
}
val isStandaloneGroupInvite =
message.attachments.isEmpty() &&
message.replyData == null &&
@@ -794,7 +803,8 @@ fun MessageBubble(
onLongClick = onLongClick
)
.then(
if (false) {
if (isCallMessage) {
// Звонки без фонового пузырька — у них свой контейнер внутри CallAttachment
Modifier
} else {
Modifier.clip(bubbleShape)

View File

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