Localize contacts screen and contact errors/messages (EN/RU)
Some checks failed
Android CI / android (push) Failing after 5m28s
Android Release / release (push) Failing after 5m34s
CI / test (push) Failing after 2m37s

This commit is contained in:
2026-03-11 06:16:37 +03:00
parent 2ffc4cce09
commit 3f9aa83110
4 changed files with 61 additions and 19 deletions

View File

@@ -30,12 +30,14 @@ import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import kotlinx.coroutines.flow.collectLatest
import ru.daemonlord.messenger.R
@Composable
fun ContactsRoute(
@@ -104,7 +106,7 @@ private fun ContactsScreen(
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
TopAppBar(
title = { Text("Contacts") },
title = { Text(stringResource(id = R.string.contacts_title)) },
)
PullToRefreshBox(
isRefreshing = state.isRefreshing,
@@ -122,7 +124,7 @@ private fun ContactsScreen(
onValueChange = onQueryChanged,
modifier = Modifier.fillMaxWidth(),
singleLine = true,
label = { Text("Search contacts/users") },
label = { Text(stringResource(id = R.string.contacts_search_label)) },
)
Row(
modifier = Modifier.fillMaxWidth(),
@@ -134,10 +136,10 @@ private fun ContactsScreen(
onValueChange = onAddByEmailChanged,
modifier = Modifier.weight(1f),
singleLine = true,
label = { Text("Add by email") },
label = { Text(stringResource(id = R.string.contacts_add_by_email_label)) },
)
Button(onClick = onAddContactByEmail) {
Text("Add")
Text(stringResource(id = R.string.common_create))
}
}
@@ -172,7 +174,7 @@ private fun ContactsScreen(
if (state.isSearchingUsers || state.query.trim().length >= 2) {
item(key = "search_header") {
Text(
text = "Search results",
text = stringResource(id = R.string.contacts_search_results),
style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.SemiBold,
)
@@ -197,7 +199,7 @@ private fun ContactsScreen(
)
}
OutlinedButton(onClick = { onAddContact(user.id) }) {
Text("Add")
Text(stringResource(id = R.string.common_create))
}
}
}
@@ -205,7 +207,7 @@ private fun ContactsScreen(
item(key = "contacts_header") {
Text(
text = "My contacts",
text = stringResource(id = R.string.contacts_my_contacts),
style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.SemiBold,
modifier = Modifier.padding(top = 4.dp),
@@ -226,13 +228,13 @@ private fun ContactsScreen(
overflow = TextOverflow.Ellipsis,
)
Text(
text = contact.username?.let { "@$it" } ?: "last seen recently",
text = contact.username?.let { "@$it" } ?: stringResource(id = R.string.contacts_last_seen_recently),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
OutlinedButton(onClick = { onRemoveContact(contact.id) }) {
Text("Remove")
Text(stringResource(id = R.string.contacts_remove))
}
}
}
@@ -240,7 +242,7 @@ private fun ContactsScreen(
if (state.contacts.isEmpty()) {
item(key = "empty_contacts") {
Text(
text = "No contacts yet.",
text = stringResource(id = R.string.contacts_empty),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)

View File

@@ -2,7 +2,9 @@ package ru.daemonlord.messenger.ui.contacts
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.android.lifecycle.HiltViewModel
import android.content.Context
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
@@ -10,6 +12,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import ru.daemonlord.messenger.R
import ru.daemonlord.messenger.domain.account.repository.AccountRepository
import ru.daemonlord.messenger.domain.common.AppError
import ru.daemonlord.messenger.domain.common.AppResult
@@ -18,6 +21,7 @@ import javax.inject.Inject
@HiltViewModel
class ContactsViewModel @Inject constructor(
private val accountRepository: AccountRepository,
@ApplicationContext private val context: Context,
) : ViewModel() {
private val _uiState = MutableStateFlow(ContactsUiState())
@@ -76,7 +80,7 @@ class ContactsViewModel @Inject constructor(
viewModelScope.launch {
when (val result = accountRepository.addContact(userId = userId)) {
is AppResult.Success -> {
_uiState.update { it.copy(infoMessage = "Contact added.") }
_uiState.update { it.copy(infoMessage = context.getString(R.string.contacts_info_added)) }
loadContacts(forceRefresh = true)
}
is AppResult.Error -> _uiState.update { it.copy(errorMessage = result.reason.toUiMessage()) }
@@ -87,7 +91,7 @@ class ContactsViewModel @Inject constructor(
fun addContactByEmail() {
val email = uiState.value.addByEmail.trim()
if (email.isBlank()) {
_uiState.update { it.copy(errorMessage = "Email is required.") }
_uiState.update { it.copy(errorMessage = context.getString(R.string.contacts_error_email_required)) }
return
}
viewModelScope.launch {
@@ -96,7 +100,7 @@ class ContactsViewModel @Inject constructor(
_uiState.update {
it.copy(
addByEmail = "",
infoMessage = "Contact added by email.",
infoMessage = context.getString(R.string.contacts_info_added_by_email),
)
}
loadContacts(forceRefresh = true)
@@ -110,7 +114,7 @@ class ContactsViewModel @Inject constructor(
viewModelScope.launch {
when (val result = accountRepository.removeContact(userId = userId)) {
is AppResult.Success -> {
_uiState.update { it.copy(infoMessage = "Contact removed.") }
_uiState.update { it.copy(infoMessage = context.getString(R.string.contacts_info_removed)) }
loadContacts(forceRefresh = true)
}
is AppResult.Error -> _uiState.update { it.copy(errorMessage = result.reason.toUiMessage()) }
@@ -148,11 +152,11 @@ class ContactsViewModel @Inject constructor(
private fun AppError.toUiMessage(): String {
return when (this) {
AppError.Network -> "Network error."
AppError.Unauthorized -> "Session expired."
AppError.InvalidCredentials -> "Authorization error."
is AppError.Server -> this.message ?: "Server error."
is AppError.Unknown -> this.cause?.message ?: "Unknown error."
AppError.Network -> context.getString(R.string.error_network)
AppError.Unauthorized -> context.getString(R.string.error_session_expired)
AppError.InvalidCredentials -> context.getString(R.string.error_authorization)
is AppError.Server -> this.message ?: context.getString(R.string.error_server)
is AppError.Unknown -> this.cause?.message ?: context.getString(R.string.error_unknown)
}
}
}

View File

@@ -208,4 +208,22 @@
<string name="auth_send_reset_link">Отправить ссылку сброса</string>
<string name="auth_new_password">Новый пароль</string>
<string name="auth_reset_with_token">Сбросить по токену</string>
<string name="contacts_title">Контакты</string>
<string name="contacts_search_label">Поиск контактов/пользователей</string>
<string name="contacts_add_by_email_label">Добавить по email</string>
<string name="contacts_search_results">Результаты поиска</string>
<string name="contacts_my_contacts">Мои контакты</string>
<string name="contacts_last_seen_recently">был(а) недавно</string>
<string name="contacts_remove">Удалить</string>
<string name="contacts_empty">Контактов пока нет.</string>
<string name="contacts_info_added">Контакт добавлен.</string>
<string name="contacts_info_added_by_email">Контакт добавлен по email.</string>
<string name="contacts_info_removed">Контакт удален.</string>
<string name="contacts_error_email_required">Требуется email.</string>
<string name="error_network">Ошибка сети.</string>
<string name="error_session_expired">Сессия истекла.</string>
<string name="error_authorization">Ошибка авторизации.</string>
<string name="error_server">Ошибка сервера.</string>
<string name="error_unknown">Неизвестная ошибка.</string>
</resources>

View File

@@ -208,4 +208,22 @@
<string name="auth_send_reset_link">Send reset link</string>
<string name="auth_new_password">New password</string>
<string name="auth_reset_with_token">Reset with token</string>
<string name="contacts_title">Contacts</string>
<string name="contacts_search_label">Search contacts/users</string>
<string name="contacts_add_by_email_label">Add by email</string>
<string name="contacts_search_results">Search results</string>
<string name="contacts_my_contacts">My contacts</string>
<string name="contacts_last_seen_recently">last seen recently</string>
<string name="contacts_remove">Remove</string>
<string name="contacts_empty">No contacts yet.</string>
<string name="contacts_info_added">Contact added.</string>
<string name="contacts_info_added_by_email">Contact added by email.</string>
<string name="contacts_info_removed">Contact removed.</string>
<string name="contacts_error_email_required">Email is required.</string>
<string name="error_network">Network error.</string>
<string name="error_session_expired">Session expired.</string>
<string name="error_authorization">Authorization error.</string>
<string name="error_server">Server error.</string>
<string name="error_unknown">Unknown error.</string>
</resources>