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