Localize ChatViewModel runtime messages
Some checks failed
Android CI / android (push) Failing after 4m52s
Android Release / release (push) Has started running
CI / test (push) Has been cancelled

This commit is contained in:
2026-03-11 06:35:24 +03:00
parent 3f9aa83110
commit 43c3fd0169
3 changed files with 66 additions and 25 deletions

View File

@@ -1,8 +1,10 @@
package ru.daemonlord.messenger.ui.chat package ru.daemonlord.messenger.ui.chat
import android.content.Context
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@@ -16,6 +18,7 @@ import kotlinx.coroutines.launch
import ru.daemonlord.messenger.core.notifications.ActiveChatTracker import ru.daemonlord.messenger.core.notifications.ActiveChatTracker
import ru.daemonlord.messenger.core.notifications.NotificationDispatcher import ru.daemonlord.messenger.core.notifications.NotificationDispatcher
import ru.daemonlord.messenger.core.token.TokenRepository import ru.daemonlord.messenger.core.token.TokenRepository
import ru.daemonlord.messenger.R
import ru.daemonlord.messenger.domain.chat.repository.ChatRepository import ru.daemonlord.messenger.domain.chat.repository.ChatRepository
import ru.daemonlord.messenger.domain.chat.usecase.ObserveChatUseCase import ru.daemonlord.messenger.domain.chat.usecase.ObserveChatUseCase
import ru.daemonlord.messenger.domain.chat.usecase.ObserveChatsUseCase import ru.daemonlord.messenger.domain.chat.usecase.ObserveChatsUseCase
@@ -66,6 +69,7 @@ class ChatViewModel @Inject constructor(
private val activeChatTracker: ActiveChatTracker, private val activeChatTracker: ActiveChatTracker,
private val notificationDispatcher: NotificationDispatcher, private val notificationDispatcher: NotificationDispatcher,
private val tokenRepository: TokenRepository, private val tokenRepository: TokenRepository,
@ApplicationContext private val context: Context,
) : ViewModel() { ) : ViewModel() {
private val chatId: Long = checkNotNull(savedStateHandle["chatId"]) private val chatId: Long = checkNotNull(savedStateHandle["chatId"])
@@ -133,7 +137,7 @@ class ChatViewModel @Inject constructor(
isRecordingVoice = true, isRecordingVoice = true,
isVoiceLocked = false, isVoiceLocked = false,
voiceRecordingDurationMs = 0L, voiceRecordingDurationMs = 0L,
voiceRecordingHint = "Slide up to lock, slide left to cancel", voiceRecordingHint = context.getString(R.string.chat_voice_hint_slide),
errorMessage = null, errorMessage = null,
) )
} }
@@ -150,7 +154,7 @@ class ChatViewModel @Inject constructor(
if (!it.isRecordingVoice) it else { if (!it.isRecordingVoice) it else {
it.copy( it.copy(
isVoiceLocked = true, isVoiceLocked = true,
voiceRecordingHint = "Recording locked", voiceRecordingHint = context.getString(R.string.chat_voice_hint_locked),
) )
} }
} }
@@ -180,7 +184,7 @@ class ChatViewModel @Inject constructor(
isVoiceLocked = false, isVoiceLocked = false,
voiceRecordingDurationMs = 0L, voiceRecordingDurationMs = 0L,
voiceRecordingHint = null, voiceRecordingHint = null,
errorMessage = "Voice message is too short.", errorMessage = context.getString(R.string.chat_error_voice_too_short),
) )
} }
return return
@@ -326,7 +330,7 @@ class ChatViewModel @Inject constructor(
val actionState = uiState.value.actionState val actionState = uiState.value.actionState
if (actionState.mode == MessageSelectionMode.MULTI) { if (actionState.mode == MessageSelectionMode.MULTI) {
if (forAll) { if (forAll) {
_uiState.update { it.copy(errorMessage = "Delete for all is available only for single message selection.") } _uiState.update { it.copy(errorMessage = context.getString(R.string.chat_error_delete_for_all_single)) }
return return
} }
val selectedIds = actionState.selectedMessageIds.toList().sorted() val selectedIds = actionState.selectedMessageIds.toList().sorted()
@@ -354,7 +358,7 @@ class ChatViewModel @Inject constructor(
} }
val selected = getFocusedSelectedMessage() ?: return val selected = getFocusedSelectedMessage() ?: return
if (forAll && !canDeleteForAll(selected)) { if (forAll && !canDeleteForAll(selected)) {
_uiState.update { it.copy(errorMessage = "Delete for all is available only for your own messages.") } _uiState.update { it.copy(errorMessage = context.getString(R.string.chat_error_delete_for_all_own)) }
return return
} }
viewModelScope.launch { viewModelScope.launch {
@@ -474,7 +478,7 @@ class ChatViewModel @Inject constructor(
selectedCanEdit = false, selectedCanEdit = false,
selectedCanDeleteForAll = false, selectedCanDeleteForAll = false,
actionState = it.actionState.clearSelection(), actionState = it.actionState.clearSelection(),
errorMessage = "Chat history cleared.", errorMessage = context.getString(R.string.chat_info_history_cleared),
) )
} }
is AppResult.Error -> _uiState.update { it.copy(errorMessage = result.reason.toUiMessage()) } is AppResult.Error -> _uiState.update { it.copy(errorMessage = result.reason.toUiMessage()) }
@@ -560,7 +564,7 @@ class ChatViewModel @Inject constructor(
_uiState.update { _uiState.update {
it.copy( it.copy(
isSending = false, isSending = false,
errorMessage = "This message can no longer be edited.", errorMessage = context.getString(R.string.chat_error_edit_expired),
) )
} }
return@launch return@launch
@@ -572,7 +576,8 @@ class ChatViewModel @Inject constructor(
_uiState.update { _uiState.update {
it.copy( it.copy(
isSending = false, isSending = false,
errorMessage = uiState.value.sendRestrictionText ?: "Sending is restricted in this chat.", errorMessage = uiState.value.sendRestrictionText
?: context.getString(R.string.chat_error_send_restricted),
) )
} }
return@launch return@launch
@@ -741,14 +746,18 @@ class ChatViewModel @Inject constructor(
val restriction = if (canSend) { val restriction = if (canSend) {
null null
} else { } else {
"Only channel owner/admin can send messages." context.getString(R.string.chat_restriction_owner_admin)
}
val chatTitle = chat.displayTitle.ifBlank {
context.getString(R.string.chat_title_fallback, chatId)
} }
val chatTitle = chat.displayTitle.ifBlank { "Chat #$chatId" }
val chatSubtitle = when { val chatSubtitle = when {
chat.type.equals("private", ignoreCase = true) && chat.counterpartIsOnline == true -> "online" chat.type.equals("private", ignoreCase = true) && chat.counterpartIsOnline == true ->
chat.type.equals("private", ignoreCase = true) && !chat.counterpartLastSeenAt.isNullOrBlank() -> "last seen recently" context.getString(R.string.chat_status_online)
chat.type.equals("group", ignoreCase = true) -> "group" chat.type.equals("private", ignoreCase = true) && !chat.counterpartLastSeenAt.isNullOrBlank() ->
chat.type.equals("channel", ignoreCase = true) -> "channel" context.getString(R.string.chat_status_last_seen_recently)
chat.type.equals("group", ignoreCase = true) -> context.getString(R.string.chat_type_group)
chat.type.equals("channel", ignoreCase = true) -> context.getString(R.string.chat_type_channel)
else -> "" else -> ""
} }
_uiState.update { _uiState.update {
@@ -932,32 +941,32 @@ class ChatViewModel @Inject constructor(
val state = uiState.value val state = uiState.value
val selfId = state.selfUserId val selfId = state.selfUserId
if (selfId != null && userId == selfId) { if (selfId != null && userId == selfId) {
_uiState.update { it.copy(errorMessage = "You cannot apply this action to yourself.") } _uiState.update { it.copy(errorMessage = context.getString(R.string.chat_error_action_self)) }
return false return false
} }
val actorRole = state.chatRole?.lowercase() val actorRole = state.chatRole?.lowercase()
if (actorRole != "owner" && actorRole != "admin") { if (actorRole != "owner" && actorRole != "admin") {
_uiState.update { it.copy(errorMessage = "You don't have enough permissions.") } _uiState.update { it.copy(errorMessage = context.getString(R.string.chat_error_permissions)) }
return false return false
} }
if (ownerOnly && actorRole != "owner") { if (ownerOnly && actorRole != "owner") {
_uiState.update { it.copy(errorMessage = "Only owner can perform this action.") } _uiState.update { it.copy(errorMessage = context.getString(R.string.chat_error_owner_only)) }
return false return false
} }
val targetRole = state.chatMembers.firstOrNull { it.userId == userId }?.role?.lowercase() val targetRole = state.chatMembers.firstOrNull { it.userId == userId }?.role?.lowercase()
if (targetRole == "owner") { if (targetRole == "owner") {
_uiState.update { it.copy(errorMessage = "You cannot manage owner account.") } _uiState.update { it.copy(errorMessage = context.getString(R.string.chat_error_manage_owner)) }
return false return false
} }
if (actorRole == "admin" && (targetRole == "admin" || targetRole == "owner")) { if (actorRole == "admin" && (targetRole == "admin" || targetRole == "owner")) {
_uiState.update { it.copy(errorMessage = "Admin cannot manage admins or owner.") } _uiState.update { it.copy(errorMessage = context.getString(R.string.chat_error_admin_manage_admin_owner)) }
return false return false
} }
if (action == "transfer_ownership" && targetRole == "owner") { if (action == "transfer_ownership" && targetRole == "owner") {
_uiState.update { it.copy(errorMessage = "Choose another member for ownership transfer.") } _uiState.update { it.copy(errorMessage = context.getString(R.string.chat_error_transfer_choose_another)) }
return false return false
} }
return true return true
@@ -965,11 +974,11 @@ class ChatViewModel @Inject constructor(
private fun AppError.toUiMessage(): String { private fun AppError.toUiMessage(): String {
return when (this) { return when (this) {
AppError.Network -> "Network error." AppError.Network -> context.getString(R.string.error_network)
AppError.Unauthorized -> "Session expired." AppError.Unauthorized -> context.getString(R.string.error_session_expired)
AppError.InvalidCredentials -> "Authorization error." AppError.InvalidCredentials -> context.getString(R.string.error_authorization)
is AppError.Server -> "Server error." is AppError.Server -> context.getString(R.string.error_server)
is AppError.Unknown -> "Unknown error." is AppError.Unknown -> context.getString(R.string.error_unknown)
} }
} }

View File

@@ -114,6 +114,22 @@
<string name="chat_member_dialog_ban_body">Забанить %1$s?</string> <string name="chat_member_dialog_ban_body">Забанить %1$s?</string>
<string name="chat_member_dialog_kick_title">Исключить участника</string> <string name="chat_member_dialog_kick_title">Исключить участника</string>
<string name="chat_member_dialog_kick_body">Исключить %1$s из чата?</string> <string name="chat_member_dialog_kick_body">Исключить %1$s из чата?</string>
<string name="chat_error_voice_too_short">Голосовое сообщение слишком короткое.</string>
<string name="chat_error_delete_for_all_single">Удаление для всех доступно только при выборе одного сообщения.</string>
<string name="chat_error_delete_for_all_own">Удаление для всех доступно только для ваших сообщений.</string>
<string name="chat_info_history_cleared">История чата очищена.</string>
<string name="chat_error_edit_expired">Это сообщение уже нельзя редактировать.</string>
<string name="chat_error_send_restricted">Отправка сообщений в этом чате ограничена.</string>
<string name="chat_restriction_owner_admin">Только owner/admin канала может отправлять сообщения.</string>
<string name="chat_error_action_self">Это действие нельзя применить к себе.</string>
<string name="chat_error_permissions">Недостаточно прав.</string>
<string name="chat_error_owner_only">Только owner может выполнить это действие.</string>
<string name="chat_error_manage_owner">Нельзя управлять аккаунтом owner.</string>
<string name="chat_error_admin_manage_admin_owner">Админ не может управлять админами и owner.</string>
<string name="chat_error_transfer_choose_another">Выберите другого участника для передачи owner.</string>
<string name="chat_voice_hint_slide">Проведите вверх, чтобы закрепить, и влево, чтобы отменить</string>
<string name="chat_voice_hint_locked">Запись закреплена</string>
<string name="chat_title_fallback">Чат #%1$d</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>

View File

@@ -114,6 +114,22 @@
<string name="chat_member_dialog_ban_body">Ban %1$s?</string> <string name="chat_member_dialog_ban_body">Ban %1$s?</string>
<string name="chat_member_dialog_kick_title">Kick member</string> <string name="chat_member_dialog_kick_title">Kick member</string>
<string name="chat_member_dialog_kick_body">Kick %1$s from chat?</string> <string name="chat_member_dialog_kick_body">Kick %1$s from chat?</string>
<string name="chat_error_voice_too_short">Voice message is too short.</string>
<string name="chat_error_delete_for_all_single">Delete for all is available only for single message selection.</string>
<string name="chat_error_delete_for_all_own">Delete for all is available only for your own messages.</string>
<string name="chat_info_history_cleared">Chat history cleared.</string>
<string name="chat_error_edit_expired">This message can no longer be edited.</string>
<string name="chat_error_send_restricted">Sending is restricted in this chat.</string>
<string name="chat_restriction_owner_admin">Only channel owner/admin can send messages.</string>
<string name="chat_error_action_self">You cannot apply this action to yourself.</string>
<string name="chat_error_permissions">You don\'t have enough permissions.</string>
<string name="chat_error_owner_only">Only owner can perform this action.</string>
<string name="chat_error_manage_owner">You cannot manage owner account.</string>
<string name="chat_error_admin_manage_admin_owner">Admin cannot manage admins or owner.</string>
<string name="chat_error_transfer_choose_another">Choose another member for ownership transfer.</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_title_fallback">Chat #%1$d</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>