diff --git a/client-mobile/.gradle/8.5/executionHistory/executionHistory.bin b/client-mobile/.gradle/8.5/executionHistory/executionHistory.bin index 16c344b..8099f3c 100644 Binary files a/client-mobile/.gradle/8.5/executionHistory/executionHistory.bin and b/client-mobile/.gradle/8.5/executionHistory/executionHistory.bin differ diff --git a/client-mobile/.gradle/8.5/executionHistory/executionHistory.lock b/client-mobile/.gradle/8.5/executionHistory/executionHistory.lock index 85cfe74..983dcba 100644 Binary files a/client-mobile/.gradle/8.5/executionHistory/executionHistory.lock and b/client-mobile/.gradle/8.5/executionHistory/executionHistory.lock differ diff --git a/client-mobile/.gradle/8.5/fileHashes/fileHashes.bin b/client-mobile/.gradle/8.5/fileHashes/fileHashes.bin index 973789f..7d9f990 100644 Binary files a/client-mobile/.gradle/8.5/fileHashes/fileHashes.bin and b/client-mobile/.gradle/8.5/fileHashes/fileHashes.bin differ diff --git a/client-mobile/.gradle/8.5/fileHashes/fileHashes.lock b/client-mobile/.gradle/8.5/fileHashes/fileHashes.lock index 09715ad..9f3cca0 100644 Binary files a/client-mobile/.gradle/8.5/fileHashes/fileHashes.lock and b/client-mobile/.gradle/8.5/fileHashes/fileHashes.lock differ diff --git a/client-mobile/.gradle/8.5/fileHashes/resourceHashesCache.bin b/client-mobile/.gradle/8.5/fileHashes/resourceHashesCache.bin index b1543db..338b816 100644 Binary files a/client-mobile/.gradle/8.5/fileHashes/resourceHashesCache.bin and b/client-mobile/.gradle/8.5/fileHashes/resourceHashesCache.bin differ diff --git a/client-mobile/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/client-mobile/.gradle/buildOutputCleanup/buildOutputCleanup.lock index 794f912..1262973 100644 Binary files a/client-mobile/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/client-mobile/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/client-mobile/.gradle/buildOutputCleanup/outputFiles.bin b/client-mobile/.gradle/buildOutputCleanup/outputFiles.bin index c4c8f46..beb3dcf 100644 Binary files a/client-mobile/.gradle/buildOutputCleanup/outputFiles.bin and b/client-mobile/.gradle/buildOutputCleanup/outputFiles.bin differ diff --git a/client-mobile/app/src/main/res/values-en/strings.xml b/client-mobile/app/src/main/res/values-en/strings.xml index ef63089..8654653 100644 --- a/client-mobile/app/src/main/res/values-en/strings.xml +++ b/client-mobile/app/src/main/res/values-en/strings.xml @@ -53,4 +53,11 @@ Message... Voice Message Send + Photo + Video + Audio + File + GIF + Reply to + yourself diff --git a/client-mobile/app/src/main/res/values/strings.xml b/client-mobile/app/src/main/res/values/strings.xml index 3962884..b3a476a 100644 --- a/client-mobile/app/src/main/res/values/strings.xml +++ b/client-mobile/app/src/main/res/values/strings.xml @@ -75,4 +75,11 @@ Контакты Профиль Сохранение... + Фото + Видео + Аудио + Файл + GIF + Ответ + самому себе diff --git a/client-mobile/chats/presentation/chat_detail/ChatDetailScreen.kt b/client-mobile/chats/presentation/chat_detail/ChatDetailScreen.kt index 981950e..225aea0 100644 --- a/client-mobile/chats/presentation/chat_detail/ChatDetailScreen.kt +++ b/client-mobile/chats/presentation/chat_detail/ChatDetailScreen.kt @@ -640,20 +640,63 @@ fun ChatDetailScreen( .weight(1f) .padding(horizontal = 8.dp, vertical = 2.dp) ) { + val replyPrefix = stringResource(R.string.reply_prefix) + val replySelf = stringResource(R.string.reply_self) + Text( - text = "Ответ " + (if (replyMsg.senderId == viewModel.getCurrentUserId()) "самом себе" else replyMsg.senderName), + text = replyPrefix + (if (replyMsg.senderId == viewModel.getCurrentUserId()) replySelf else replyMsg.senderName), style = MaterialTheme.typography.labelMedium, color = MaterialTheme.colorScheme.primary, fontWeight = FontWeight.Bold ) + + val photoText = stringResource(R.string.reply_photo) + val videoText = stringResource(R.string.reply_video) + val voiceText = stringResource(R.string.voice_message) + val audioText = stringResource(R.string.reply_audio) + val fileText = stringResource(R.string.reply_file) + val gifText = stringResource(R.string.reply_gif) + val mediaText = stringResource(R.string.media) + + val replyText = remember(replyMsg) { + if (!replyMsg.content.isNullOrEmpty()) { + replyMsg.content + } else { + when (replyMsg.mediaType) { + chats.domain.model.MediaType.IMAGE -> photoText + chats.domain.model.MediaType.VIDEO -> videoText + chats.domain.model.MediaType.AUDIO -> if (replyMsg.media.any { it.type == "voice" }) voiceText else audioText + chats.domain.model.MediaType.FILE -> fileText + chats.domain.model.MediaType.GIF -> gifText + else -> mediaText + } + } + } + Text( - text = replyMsg.content ?: if (replyMsg.media.isNotEmpty()) "Медиа" else "...", + text = replyText, style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant, maxLines = 1, overflow = TextOverflow.Ellipsis ) } + + if ((replyMsg.mediaType == chats.domain.model.MediaType.IMAGE || + replyMsg.mediaType == chats.domain.model.MediaType.VIDEO || + replyMsg.mediaType == chats.domain.model.MediaType.GIF) && + replyMsg.media.isNotEmpty()) { + androidx.compose.foundation.Image( + painter = rememberAsyncImagePainter(replyMsg.media.first().url), + contentDescription = null, + modifier = Modifier + .padding(end = 4.dp) + .size(36.dp) + .clip(RoundedCornerShape(4.dp)), + contentScale = androidx.compose.ui.layout.ContentScale.Crop + ) + } + IconButton(onClick = { viewModel.cancelReply() }) { Icon(Icons.Default.Close, contentDescription = "Cancel", modifier = Modifier.size(20.dp), tint = Color.Gray) } diff --git a/client-mobile/chats/presentation/components/MessageBubble.kt b/client-mobile/chats/presentation/components/MessageBubble.kt index 7b30a20..8baeea6 100644 --- a/client-mobile/chats/presentation/components/MessageBubble.kt +++ b/client-mobile/chats/presentation/components/MessageBubble.kt @@ -123,7 +123,22 @@ fun MessageBubble( .width(2.dp) .background(if (isCurrentUser) Color.White else Color(0xFF3096E5)) ) - Column(modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp)) { + val context = androidx.compose.ui.platform.LocalContext.current + val isReplyImage = reply.mediaType == MediaType.IMAGE || reply.mediaType == MediaType.VIDEO || reply.mediaType == MediaType.GIF + + if (isReplyImage && reply.media.isNotEmpty()) { + AsyncImage( + model = reply.media.first().url, + contentDescription = null, + modifier = Modifier + .fillMaxHeight() + .width(40.dp) + .clip(RoundedCornerShape(0.dp, 4.dp, 4.dp, 0.dp)), + contentScale = ContentScale.Crop + ) + } + + Column(modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp).weight(1f)) { val accentColor = if (isCurrentUser) Color.White else Color(0xFF3096E5) Text( text = reply.senderName, @@ -132,8 +147,32 @@ fun MessageBubble( maxLines = 1, fontWeight = androidx.compose.ui.text.font.FontWeight.Bold ) + + val photoText = androidx.compose.ui.res.stringResource(ru.knot.messager.R.string.reply_photo) + val videoText = androidx.compose.ui.res.stringResource(ru.knot.messager.R.string.reply_video) + val voiceText = androidx.compose.ui.res.stringResource(ru.knot.messager.R.string.voice_message) + val audioText = androidx.compose.ui.res.stringResource(ru.knot.messager.R.string.reply_audio) + val fileText = androidx.compose.ui.res.stringResource(ru.knot.messager.R.string.reply_file) + val gifText = androidx.compose.ui.res.stringResource(ru.knot.messager.R.string.reply_gif) + val mediaText = androidx.compose.ui.res.stringResource(ru.knot.messager.R.string.media) + + val replyText = remember(reply) { + if (!reply.content.isNullOrEmpty()) { + reply.content + } else { + when (reply.mediaType) { + MediaType.IMAGE -> photoText + MediaType.VIDEO -> videoText + MediaType.AUDIO -> if (reply.media.any { it.type == "voice" }) voiceText else audioText + MediaType.FILE -> fileText + MediaType.GIF -> gifText + else -> mediaText + } + } + } + Text( - text = reply.content ?: if (reply.media.isNotEmpty()) "\uD83D\uDCCE Media" else "", + text = replyText, style = MaterialTheme.typography.bodySmall, color = contentColor.copy(alpha = 0.8f), maxLines = 1,