Localize key ChatScreen labels and media badges
This commit is contained in:
@@ -386,7 +386,8 @@ fun ChatScreen(
|
||||
val senderNameByUserId = remember(state.messages, state.chatMembers) {
|
||||
val fromMembers = state.chatMembers.associate { member ->
|
||||
val resolved = member.name.ifBlank {
|
||||
member.username?.takeIf { it.isNotBlank() }?.let { "@$it" } ?: "User #${member.userId}"
|
||||
member.username?.takeIf { it.isNotBlank() }?.let { "@$it" }
|
||||
?: context.getString(R.string.chat_user_fallback_with_id, member.userId)
|
||||
}
|
||||
member.userId to resolved
|
||||
}
|
||||
@@ -694,7 +695,9 @@ fun ChatScreen(
|
||||
}
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = state.chatTitle.ifBlank { "Chat #${state.chatId}" },
|
||||
text = state.chatTitle.ifBlank {
|
||||
stringResource(id = R.string.chat_title_fallback, state.chatId)
|
||||
},
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
maxLines = 1,
|
||||
@@ -1287,7 +1290,9 @@ fun ChatScreen(
|
||||
}
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = state.chatTitle.ifBlank { "Chat #${state.chatId}" },
|
||||
text = state.chatTitle.ifBlank {
|
||||
stringResource(id = R.string.chat_title_fallback, state.chatId)
|
||||
},
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
)
|
||||
@@ -1977,7 +1982,7 @@ fun ChatScreen(
|
||||
.clickable { emojiPickerTab = tab },
|
||||
) {
|
||||
Text(
|
||||
text = tab.title,
|
||||
text = stringResource(id = tab.titleRes),
|
||||
modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
)
|
||||
@@ -2170,6 +2175,7 @@ private fun MessageBubble(
|
||||
onClick: () -> Unit,
|
||||
onLongPress: () -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val isOutgoing = message.isOutgoing
|
||||
val renderAsChannelPost = isChannelChat
|
||||
val alignAsOutgoing = isOutgoing && !renderAsChannelPost
|
||||
@@ -2360,7 +2366,11 @@ private fun MessageBubble(
|
||||
|
||||
if (isLegacyImageUrlMessage) {
|
||||
val isLegacyStickerMessage = isStickerAsset(fileUrl = textUrl, fileType = "image/url")
|
||||
val badgeLabel = if (isLegacyStickerMessage) null else mediaBadgeLabel(fileType = "image/url", url = textUrl)
|
||||
val badgeLabel = if (isLegacyStickerMessage) {
|
||||
null
|
||||
} else {
|
||||
mediaBadgeLabel(fileType = "image/url", url = textUrl, context = context)
|
||||
}
|
||||
val openable = !isLegacyStickerMessage && badgeLabel == null
|
||||
Box(
|
||||
modifier = Modifier
|
||||
@@ -2434,7 +2444,11 @@ private fun MessageBubble(
|
||||
if (imageAttachments.size == 1) {
|
||||
val single = imageAttachments.first()
|
||||
val isStickerImage = isStickerAsset(fileUrl = single.fileUrl, fileType = single.fileType)
|
||||
val badgeLabel = if (isStickerImage) null else mediaBadgeLabel(fileType = single.fileType, url = single.fileUrl)
|
||||
val badgeLabel = if (isStickerImage) {
|
||||
null
|
||||
} else {
|
||||
mediaBadgeLabel(fileType = single.fileType, url = single.fileUrl, context = context)
|
||||
}
|
||||
val openable = !isStickerImage && badgeLabel == null
|
||||
Box(
|
||||
modifier = Modifier
|
||||
@@ -2475,7 +2489,11 @@ private fun MessageBubble(
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
rowItems.forEach { image ->
|
||||
val badgeLabel = mediaBadgeLabel(fileType = image.fileType, url = image.fileUrl)
|
||||
val badgeLabel = mediaBadgeLabel(
|
||||
fileType = image.fileType,
|
||||
url = image.fileUrl,
|
||||
context = context,
|
||||
)
|
||||
val openable = badgeLabel == null
|
||||
Box(
|
||||
modifier = Modifier
|
||||
@@ -2533,9 +2551,15 @@ private fun MessageBubble(
|
||||
playbackTitle = message.senderDisplayName?.ifBlank { null }
|
||||
?: extractFileName(attachment.fileUrl),
|
||||
playbackSubtitle = if (message.type.contains("voice", ignoreCase = true)) {
|
||||
"Voice message • ${formatMessageTime(message.createdAt)}"
|
||||
stringResource(
|
||||
id = R.string.chat_playback_subtitle_voice,
|
||||
formatMessageTime(message.createdAt),
|
||||
)
|
||||
} else {
|
||||
"Audio • ${formatMessageTime(message.createdAt)}"
|
||||
stringResource(
|
||||
id = R.string.chat_playback_subtitle_audio,
|
||||
formatMessageTime(message.createdAt),
|
||||
)
|
||||
},
|
||||
messageId = message.id,
|
||||
onPlaybackChanged = onAudioPlaybackChanged,
|
||||
@@ -2741,10 +2765,10 @@ private sealed interface ChatTimelineItem {
|
||||
) : ChatTimelineItem
|
||||
}
|
||||
|
||||
private enum class ComposerPickerTab(val title: String) {
|
||||
Emoji("Эмодзи"),
|
||||
Gif("GIF"),
|
||||
Sticker("Стикеры"),
|
||||
private enum class ComposerPickerTab(@StringRes val titleRes: Int) {
|
||||
Emoji(R.string.chat_picker_tab_emoji),
|
||||
Gif(R.string.chat_picker_tab_gif),
|
||||
Sticker(R.string.chat_picker_tab_stickers),
|
||||
}
|
||||
|
||||
private data class RemotePickerItem(
|
||||
@@ -3882,12 +3906,19 @@ private fun ChatMembersTabContent(
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = member.name.ifBlank { member.username?.let { "@$it" } ?: "User #${member.userId}" },
|
||||
text = member.name.ifBlank {
|
||||
member.username?.let { "@$it" }
|
||||
?: stringResource(id = R.string.chat_user_fallback_with_id, member.userId)
|
||||
},
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
)
|
||||
Text(
|
||||
text = "${member.role} • id ${member.userId}",
|
||||
text = stringResource(
|
||||
id = R.string.chat_member_role_with_id,
|
||||
member.role,
|
||||
member.userId,
|
||||
),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
@@ -3993,12 +4024,15 @@ private fun ChatMembersTabContent(
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = ban.name.ifBlank { ban.username?.let { "@$it" } ?: "User #${ban.userId}" },
|
||||
text = ban.name.ifBlank {
|
||||
ban.username?.let { "@$it" }
|
||||
?: stringResource(id = R.string.chat_user_fallback_with_id, ban.userId)
|
||||
},
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
)
|
||||
Text(
|
||||
text = "id ${ban.userId}",
|
||||
text = stringResource(id = R.string.chat_member_id, ban.userId),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
@@ -4115,11 +4149,15 @@ private fun isStickerAsset(fileUrl: String, fileType: String): Boolean {
|
||||
return isStickerLikeUrl(fileUrl)
|
||||
}
|
||||
|
||||
private fun mediaBadgeLabel(fileType: String, url: String): String? {
|
||||
private fun mediaBadgeLabel(
|
||||
fileType: String,
|
||||
url: String,
|
||||
context: Context,
|
||||
): String? {
|
||||
val normalizedType = fileType.lowercase(Locale.getDefault())
|
||||
return when {
|
||||
normalizedType.contains("gif") || isGifLikeUrl(url) -> "GIF"
|
||||
normalizedType.contains("webp") || isStickerLikeUrl(url) -> "Sticker"
|
||||
normalizedType.contains("gif") || isGifLikeUrl(url) -> context.getString(R.string.chat_media_badge_gif)
|
||||
normalizedType.contains("webp") || isStickerLikeUrl(url) -> context.getString(R.string.chat_media_badge_sticker)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,6 +147,16 @@
|
||||
<string name="chat_member_action_kick">Кикнуть</string>
|
||||
<string name="chat_member_action_unban">Разбанить</string>
|
||||
<string name="chat_media_badge_video">Видео</string>
|
||||
<string name="chat_media_badge_gif">GIF</string>
|
||||
<string name="chat_media_badge_sticker">Стикер</string>
|
||||
<string name="chat_picker_tab_emoji">Эмодзи</string>
|
||||
<string name="chat_picker_tab_gif">GIF</string>
|
||||
<string name="chat_picker_tab_stickers">Стикеры</string>
|
||||
<string name="chat_playback_subtitle_voice">Голосовое сообщение • %1$s</string>
|
||||
<string name="chat_playback_subtitle_audio">Аудио • %1$s</string>
|
||||
<string name="chat_user_fallback_with_id">Пользователь #%1$d</string>
|
||||
<string name="chat_member_role_with_id">%1$s • id %2$d</string>
|
||||
<string name="chat_member_id">id %1$d</string>
|
||||
<string name="chat_member_dialog_demote_title">Понизить админа</string>
|
||||
<string name="chat_member_dialog_demote_body">Понизить %1$s до участника?</string>
|
||||
<string name="chat_member_dialog_transfer_title">Передача owner</string>
|
||||
|
||||
@@ -147,6 +147,16 @@
|
||||
<string name="chat_member_action_kick">Kick</string>
|
||||
<string name="chat_member_action_unban">Unban</string>
|
||||
<string name="chat_media_badge_video">Video</string>
|
||||
<string name="chat_media_badge_gif">GIF</string>
|
||||
<string name="chat_media_badge_sticker">Sticker</string>
|
||||
<string name="chat_picker_tab_emoji">Emoji</string>
|
||||
<string name="chat_picker_tab_gif">GIF</string>
|
||||
<string name="chat_picker_tab_stickers">Stickers</string>
|
||||
<string name="chat_playback_subtitle_voice">Voice message • %1$s</string>
|
||||
<string name="chat_playback_subtitle_audio">Audio • %1$s</string>
|
||||
<string name="chat_user_fallback_with_id">User #%1$d</string>
|
||||
<string name="chat_member_role_with_id">%1$s • id %2$d</string>
|
||||
<string name="chat_member_id">id %1$d</string>
|
||||
<string name="chat_member_dialog_demote_title">Demote admin</string>
|
||||
<string name="chat_member_dialog_demote_body">Demote %1$s to member?</string>
|
||||
<string name="chat_member_dialog_transfer_title">Transfer ownership</string>
|
||||
|
||||
Reference in New Issue
Block a user