Localize chat day labels and gif errors
This commit is contained in:
@@ -382,7 +382,7 @@ fun ChatScreen(
|
|||||||
val isPrivateChat = state.chatType.equals("private", ignoreCase = true)
|
val isPrivateChat = state.chatType.equals("private", ignoreCase = true)
|
||||||
val canShowMembersTab = state.chatType.equals("group", ignoreCase = true) ||
|
val canShowMembersTab = state.chatType.equals("group", ignoreCase = true) ||
|
||||||
(state.chatType.equals("channel", ignoreCase = true) && state.canManageMembers)
|
(state.chatType.equals("channel", ignoreCase = true) && state.canManageMembers)
|
||||||
val timelineItems = remember(state.messages) { buildChatTimelineItems(state.messages) }
|
val timelineItems = remember(state.messages, context) { buildChatTimelineItems(state.messages, context) }
|
||||||
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 {
|
||||||
@@ -442,7 +442,7 @@ fun ChatScreen(
|
|||||||
val chatInfoSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
val chatInfoSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||||
val isTabletLayout = LocalConfiguration.current.screenWidthDp >= 840
|
val isTabletLayout = LocalConfiguration.current.screenWidthDp >= 840
|
||||||
val adaptiveHorizontalPadding = if (isTabletLayout) 72.dp else 0.dp
|
val adaptiveHorizontalPadding = if (isTabletLayout) 72.dp else 0.dp
|
||||||
val chatInfoEntries = remember(state.messages) { buildChatInfoEntries(state.messages) }
|
val chatInfoEntries = remember(state.messages, context) { buildChatInfoEntries(state.messages, context) }
|
||||||
val giphyApiKey = remember { BuildConfig.GIPHY_API_KEY.trim() }
|
val giphyApiKey = remember { BuildConfig.GIPHY_API_KEY.trim() }
|
||||||
val firstUnreadIncomingMessageId = remember(state.messages, state.chatUnreadCount) {
|
val firstUnreadIncomingMessageId = remember(state.messages, state.chatUnreadCount) {
|
||||||
val unread = state.chatUnreadCount.coerceAtLeast(0)
|
val unread = state.chatUnreadCount.coerceAtLeast(0)
|
||||||
@@ -554,7 +554,7 @@ fun ChatScreen(
|
|||||||
}
|
}
|
||||||
if (giphyApiKey.isBlank()) {
|
if (giphyApiKey.isBlank()) {
|
||||||
giphySearchItems = emptyList()
|
giphySearchItems = emptyList()
|
||||||
giphyErrorMessage = "Set GIPHY_API_KEY in local.properties"
|
giphyErrorMessage = context.getString(R.string.chat_error_giphy_api_key_missing)
|
||||||
isGiphyLoading = false
|
isGiphyLoading = false
|
||||||
return@LaunchedEffect
|
return@LaunchedEffect
|
||||||
}
|
}
|
||||||
@@ -567,7 +567,11 @@ fun ChatScreen(
|
|||||||
limit = 24,
|
limit = 24,
|
||||||
)
|
)
|
||||||
giphySearchItems = found
|
giphySearchItems = found
|
||||||
giphyErrorMessage = if (found.isEmpty()) "No GIFs found" else null
|
giphyErrorMessage = if (found.isEmpty()) {
|
||||||
|
context.getString(R.string.chat_error_no_gifs_found)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
isGiphyLoading = false
|
isGiphyLoading = false
|
||||||
}
|
}
|
||||||
LaunchedEffect(listState, timelineItems) {
|
LaunchedEffect(listState, timelineItems) {
|
||||||
@@ -2193,6 +2197,8 @@ private fun MessageBubble(
|
|||||||
?.takeIf { it.isNotBlank() }
|
?.takeIf { it.isNotBlank() }
|
||||||
?: message.senderUsername?.takeIf { it.isNotBlank() }?.let { "@$it" }
|
?: message.senderUsername?.takeIf { it.isNotBlank() }?.let { "@$it" }
|
||||||
?: senderNameByUserId[message.senderId]
|
?: senderNameByUserId[message.senderId]
|
||||||
|
val unknownUserText = stringResource(id = R.string.chat_unknown_user)
|
||||||
|
val mediaPlaceholder = stringResource(id = R.string.chat_media_placeholder)
|
||||||
val legacyTextUrl = message.text?.trim()?.takeIf { it.startsWith("http", ignoreCase = true) }
|
val legacyTextUrl = message.text?.trim()?.takeIf { it.startsWith("http", ignoreCase = true) }
|
||||||
val hasLegacyStickerImage = message.attachments.isEmpty() &&
|
val hasLegacyStickerImage = message.attachments.isEmpty() &&
|
||||||
message.type.equals("image", ignoreCase = true) &&
|
message.type.equals("image", ignoreCase = true) &&
|
||||||
@@ -2264,7 +2270,11 @@ private fun MessageBubble(
|
|||||||
|
|
||||||
if (message.forwardedFromMessageId != null || !message.forwardedFromDisplayName.isNullOrBlank()) {
|
if (message.forwardedFromMessageId != null || !message.forwardedFromDisplayName.isNullOrBlank()) {
|
||||||
Text(
|
Text(
|
||||||
text = "Forwarded from ${message.forwardedFromDisplayName?.takeIf { it.isNotBlank() } ?: "#${message.forwardedFromMessageId ?: "?"}"}",
|
text = stringResource(
|
||||||
|
id = R.string.chat_forwarded_from,
|
||||||
|
message.forwardedFromDisplayName?.takeIf { it.isNotBlank() }
|
||||||
|
?: "#${message.forwardedFromMessageId ?: "?"}",
|
||||||
|
),
|
||||||
style = MaterialTheme.typography.labelSmall,
|
style = MaterialTheme.typography.labelSmall,
|
||||||
color = secondaryTextColor,
|
color = secondaryTextColor,
|
||||||
)
|
)
|
||||||
@@ -2287,8 +2297,8 @@ private fun MessageBubble(
|
|||||||
}
|
}
|
||||||
val replyAuthor = replyAuthorFromPreview
|
val replyAuthor = replyAuthorFromPreview
|
||||||
?: replyAuthorByMessageId[message.replyToMessageId]
|
?: replyAuthorByMessageId[message.replyToMessageId]
|
||||||
?: "Unknown user"
|
?: unknownUserText
|
||||||
val replySnippet = message.replyPreviewText?.takeIf { it.isNotBlank() } ?: "[media]"
|
val replySnippet = message.replyPreviewText?.takeIf { it.isNotBlank() } ?: mediaPlaceholder
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -2761,7 +2771,10 @@ private val defaultStickerItems = listOf(
|
|||||||
RemotePickerItem("Sticker 6", "https://raw.githubusercontent.com/twitter/twemoji/master/assets/72x72/1f602.png", "sticker_lol"),
|
RemotePickerItem("Sticker 6", "https://raw.githubusercontent.com/twitter/twemoji/master/assets/72x72/1f602.png", "sticker_lol"),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun buildChatTimelineItems(messages: List<MessageItem>): List<ChatTimelineItem> {
|
private fun buildChatTimelineItems(
|
||||||
|
messages: List<MessageItem>,
|
||||||
|
context: Context,
|
||||||
|
): List<ChatTimelineItem> {
|
||||||
if (messages.isEmpty()) return emptyList()
|
if (messages.isEmpty()) return emptyList()
|
||||||
val items = mutableListOf<ChatTimelineItem>()
|
val items = mutableListOf<ChatTimelineItem>()
|
||||||
var previousDate: LocalDate? = null
|
var previousDate: LocalDate? = null
|
||||||
@@ -2773,7 +2786,7 @@ private fun buildChatTimelineItems(messages: List<MessageItem>): List<ChatTimeli
|
|||||||
items += ChatTimelineItem.DayHeader(
|
items += ChatTimelineItem.DayHeader(
|
||||||
headerId = "${date}_$dayHeaderCounter",
|
headerId = "${date}_$dayHeaderCounter",
|
||||||
dateKey = date.toString(),
|
dateKey = date.toString(),
|
||||||
label = formatDateSeparatorLabel(date),
|
label = formatDateSeparatorLabel(date, context),
|
||||||
)
|
)
|
||||||
previousDate = date
|
previousDate = date
|
||||||
}
|
}
|
||||||
@@ -2885,11 +2898,14 @@ private fun parseMessageLocalDate(createdAt: String): LocalDate? {
|
|||||||
|
|
||||||
private val daySeparatorFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("d MMMM", Locale.getDefault())
|
private val daySeparatorFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("d MMMM", Locale.getDefault())
|
||||||
|
|
||||||
private fun formatDateSeparatorLabel(date: LocalDate): String {
|
private fun formatDateSeparatorLabel(
|
||||||
|
date: LocalDate,
|
||||||
|
context: Context,
|
||||||
|
): String {
|
||||||
val today = LocalDate.now()
|
val today = LocalDate.now()
|
||||||
return when {
|
return when {
|
||||||
date == today -> "Сегодня"
|
date == today -> context.getString(R.string.chat_day_today)
|
||||||
date == today.minusDays(1) -> "Вчера"
|
date == today.minusDays(1) -> context.getString(R.string.chat_day_yesterday)
|
||||||
else -> date.format(daySeparatorFormatter)
|
else -> date.format(daySeparatorFormatter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2914,7 +2930,10 @@ private fun VoiceRecordingStatusRow(
|
|||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "Voice ${formatDuration(durationMs.toInt())}",
|
text = stringResource(
|
||||||
|
id = R.string.chat_voice_recording_duration,
|
||||||
|
formatDuration(durationMs.toInt()),
|
||||||
|
),
|
||||||
style = MaterialTheme.typography.labelMedium,
|
style = MaterialTheme.typography.labelMedium,
|
||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.SemiBold,
|
||||||
)
|
)
|
||||||
@@ -4014,7 +4033,10 @@ private fun ChatMembersTabContent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildChatInfoEntries(messages: List<MessageItem>): List<ChatInfoEntry> {
|
private fun buildChatInfoEntries(
|
||||||
|
messages: List<MessageItem>,
|
||||||
|
context: Context,
|
||||||
|
): List<ChatInfoEntry> {
|
||||||
val entries = mutableListOf<ChatInfoEntry>()
|
val entries = mutableListOf<ChatInfoEntry>()
|
||||||
messages
|
messages
|
||||||
.sortedByDescending { it.id }
|
.sortedByDescending { it.id }
|
||||||
@@ -4063,7 +4085,7 @@ private fun buildChatInfoEntries(messages: List<MessageItem>): List<ChatInfoEntr
|
|||||||
entries += ChatInfoEntry(
|
entries += ChatInfoEntry(
|
||||||
type = ChatInfoEntryType.Link,
|
type = ChatInfoEntryType.Link,
|
||||||
title = match.value,
|
title = match.value,
|
||||||
subtitle = "${message.senderDisplayName?.takeIf { it.isNotBlank() } ?: message.senderUsername?.let { "@$it" } ?: "Unknown"} • $time",
|
subtitle = "${message.senderDisplayName?.takeIf { it.isNotBlank() } ?: message.senderUsername?.let { "@$it" } ?: context.getString(R.string.chat_unknown_user)} • $time",
|
||||||
resourceUrl = match.value,
|
resourceUrl = match.value,
|
||||||
sourceMessageId = message.id,
|
sourceMessageId = message.id,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -130,6 +130,14 @@
|
|||||||
<string name="chat_voice_hint_slide">Проведите вверх, чтобы закрепить, и влево, чтобы отменить</string>
|
<string name="chat_voice_hint_slide">Проведите вверх, чтобы закрепить, и влево, чтобы отменить</string>
|
||||||
<string name="chat_voice_hint_locked">Запись закреплена</string>
|
<string name="chat_voice_hint_locked">Запись закреплена</string>
|
||||||
<string name="chat_title_fallback">Чат #%1$d</string>
|
<string name="chat_title_fallback">Чат #%1$d</string>
|
||||||
|
<string name="chat_day_today">Сегодня</string>
|
||||||
|
<string name="chat_day_yesterday">Вчера</string>
|
||||||
|
<string name="chat_voice_recording_duration">Голос %1$s</string>
|
||||||
|
<string name="chat_unknown_user">Неизвестный пользователь</string>
|
||||||
|
<string name="chat_media_placeholder">[медиа]</string>
|
||||||
|
<string name="chat_forwarded_from">Переслано от %1$s</string>
|
||||||
|
<string name="chat_error_giphy_api_key_missing">Укажите GIPHY_API_KEY в local.properties</string>
|
||||||
|
<string name="chat_error_no_gifs_found">GIF не найдены</string>
|
||||||
|
|
||||||
<string name="settings_user_fallback">Пользователь</string>
|
<string name="settings_user_fallback">Пользователь</string>
|
||||||
<string name="settings_accounts_header">АККАУНТЫ</string>
|
<string name="settings_accounts_header">АККАУНТЫ</string>
|
||||||
|
|||||||
@@ -130,6 +130,14 @@
|
|||||||
<string name="chat_voice_hint_slide">Slide up to lock, slide left to cancel</string>
|
<string name="chat_voice_hint_slide">Slide up to lock, slide left to cancel</string>
|
||||||
<string name="chat_voice_hint_locked">Recording locked</string>
|
<string name="chat_voice_hint_locked">Recording locked</string>
|
||||||
<string name="chat_title_fallback">Chat #%1$d</string>
|
<string name="chat_title_fallback">Chat #%1$d</string>
|
||||||
|
<string name="chat_day_today">Today</string>
|
||||||
|
<string name="chat_day_yesterday">Yesterday</string>
|
||||||
|
<string name="chat_voice_recording_duration">Voice %1$s</string>
|
||||||
|
<string name="chat_unknown_user">Unknown user</string>
|
||||||
|
<string name="chat_media_placeholder">[media]</string>
|
||||||
|
<string name="chat_forwarded_from">Forwarded from %1$s</string>
|
||||||
|
<string name="chat_error_giphy_api_key_missing">Set GIPHY_API_KEY in local.properties</string>
|
||||||
|
<string name="chat_error_no_gifs_found">No GIFs found</string>
|
||||||
|
|
||||||
<string name="settings_user_fallback">User</string>
|
<string name="settings_user_fallback">User</string>
|
||||||
<string name="settings_accounts_header">ACCOUNTS</string>
|
<string name="settings_accounts_header">ACCOUNTS</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user