Localize AccountViewModel status and error messages
Some checks failed
Android CI / android (push) Failing after 5m31s
Android Release / release (push) Has started running
CI / test (push) Has been cancelled

This commit is contained in:
2026-03-11 20:46:36 +03:00
parent d54dc9fe8b
commit 27f2ad8001
3 changed files with 45 additions and 16 deletions

View File

@@ -1,9 +1,11 @@
package ru.daemonlord.messenger.ui.account package ru.daemonlord.messenger.ui.account
import android.content.Context
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.LocaleListCompat import androidx.core.os.LocaleListCompat
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
@@ -23,6 +25,7 @@ import ru.daemonlord.messenger.domain.settings.model.AppLanguage
import ru.daemonlord.messenger.domain.settings.model.AppThemeMode import ru.daemonlord.messenger.domain.settings.model.AppThemeMode
import ru.daemonlord.messenger.domain.settings.repository.LanguageRepository import ru.daemonlord.messenger.domain.settings.repository.LanguageRepository
import ru.daemonlord.messenger.domain.settings.repository.ThemeRepository import ru.daemonlord.messenger.domain.settings.repository.ThemeRepository
import ru.daemonlord.messenger.R
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
@@ -36,6 +39,7 @@ class AccountViewModel @Inject constructor(
private val languageRepository: LanguageRepository, private val languageRepository: LanguageRepository,
private val themeRepository: ThemeRepository, private val themeRepository: ThemeRepository,
private val pushTokenSyncManager: PushTokenSyncManager, private val pushTokenSyncManager: PushTokenSyncManager,
@ApplicationContext private val context: Context,
) : ViewModel() { ) : ViewModel() {
private val _uiState = MutableStateFlow(AccountUiState()) private val _uiState = MutableStateFlow(AccountUiState())
val uiState: StateFlow<AccountUiState> = _uiState.asStateFlow() val uiState: StateFlow<AccountUiState> = _uiState.asStateFlow()
@@ -65,13 +69,14 @@ class AccountViewModel @Inject constructor(
notificationsHistory = (notifications as? AppResult.Success)?.data ?: state.notificationsHistory, notificationsHistory = (notifications as? AppResult.Success)?.data ?: state.notificationsHistory,
activeUserId = activeUserId, activeUserId = activeUserId,
storedAccounts = storedAccounts.map { account -> storedAccounts = storedAccounts.map { account ->
val fallbackUser = context.getString(R.string.account_user_fallback, account.userId)
StoredAccountUi( StoredAccountUi(
userId = account.userId, userId = account.userId,
title = account.name.ifBlank { "User #${account.userId}" }, title = account.name.ifBlank { fallbackUser },
subtitle = listOfNotNull( subtitle = listOfNotNull(
account.username?.takeIf { it.isNotBlank() }?.let { "@$it" }, account.username?.takeIf { it.isNotBlank() }?.let { "@$it" },
account.email?.takeIf { it.isNotBlank() }, account.email?.takeIf { it.isNotBlank() },
).joinToString("").ifBlank { "User #${account.userId}" }, ).joinToString("").ifBlank { fallbackUser },
avatarUrl = account.avatarUrl, avatarUrl = account.avatarUrl,
isActive = activeUserId == account.userId, isActive = activeUserId == account.userId,
) )
@@ -131,7 +136,7 @@ class AccountViewModel @Inject constructor(
fun addAccount(email: String, password: String, onDone: (Boolean) -> Unit = {}) { fun addAccount(email: String, password: String, onDone: (Boolean) -> Unit = {}) {
val normalizedEmail = email.trim() val normalizedEmail = email.trim()
if (normalizedEmail.isBlank() || password.isBlank()) { if (normalizedEmail.isBlank() || password.isBlank()) {
_uiState.update { it.copy(errorMessage = "Email and password are required.") } _uiState.update { it.copy(errorMessage = context.getString(R.string.account_error_email_password_required)) }
onDone(false) onDone(false)
return return
} }
@@ -142,7 +147,7 @@ class AccountViewModel @Inject constructor(
_uiState.update { _uiState.update {
it.copy( it.copy(
isAddingAccount = false, isAddingAccount = false,
message = "Account added.", message = context.getString(R.string.account_info_added),
) )
} }
refresh() refresh()
@@ -165,7 +170,7 @@ class AccountViewModel @Inject constructor(
viewModelScope.launch { viewModelScope.launch {
val switched = tokenRepository.switchAccount(userId) val switched = tokenRepository.switchAccount(userId)
if (!switched) { if (!switched) {
_uiState.update { it.copy(errorMessage = "No saved session for this account.") } _uiState.update { it.copy(errorMessage = context.getString(R.string.account_error_no_saved_session)) }
onSwitched(false) onSwitched(false)
return@launch return@launch
} }
@@ -180,7 +185,7 @@ class AccountViewModel @Inject constructor(
if (syncFailed) { if (syncFailed) {
_uiState.update { _uiState.update {
it.copy( it.copy(
errorMessage = "Account switched, but chats sync failed. Pull to refresh.", errorMessage = context.getString(R.string.account_error_switch_sync_failed),
) )
} }
} }
@@ -208,7 +213,7 @@ class AccountViewModel @Inject constructor(
it.copy( it.copy(
isSaving = false, isSaving = false,
profile = result.data, profile = result.data,
message = "Profile updated.", message = context.getString(R.string.account_info_profile_updated),
) )
} }
is AppResult.Error -> _uiState.update { is AppResult.Error -> _uiState.update {
@@ -231,7 +236,7 @@ class AccountViewModel @Inject constructor(
_uiState.update { it.copy(isSaving = true, errorMessage = null, message = null) } _uiState.update { it.copy(isSaving = true, errorMessage = null, message = null) }
when (val result = accountRepository.uploadAvatar(fileName, mimeType, bytes)) { when (val result = accountRepository.uploadAvatar(fileName, mimeType, bytes)) {
is AppResult.Success -> { is AppResult.Success -> {
_uiState.update { it.copy(isSaving = false, message = "Avatar uploaded.") } _uiState.update { it.copy(isSaving = false, message = context.getString(R.string.account_info_avatar_uploaded)) }
onUploaded(result.data) onUploaded(result.data)
} }
is AppResult.Error -> _uiState.update { is AppResult.Error -> _uiState.update {
@@ -264,7 +269,7 @@ class AccountViewModel @Inject constructor(
it.copy( it.copy(
isSaving = false, isSaving = false,
profile = result.data, profile = result.data,
message = "Privacy settings updated.", message = context.getString(R.string.account_info_privacy_updated),
) )
} }
is AppResult.Error -> _uiState.update { is AppResult.Error -> _uiState.update {
@@ -317,7 +322,7 @@ class AccountViewModel @Inject constructor(
it.copy( it.copy(
twoFactorSecret = result.data.first, twoFactorSecret = result.data.first,
twoFactorOtpAuthUrl = result.data.second, twoFactorOtpAuthUrl = result.data.second,
message = "2FA secret generated. Enter code to enable.", message = context.getString(R.string.account_info_2fa_secret_generated),
errorMessage = null, errorMessage = null,
) )
} }
@@ -366,7 +371,7 @@ class AccountViewModel @Inject constructor(
is AppResult.Success -> _uiState.update { is AppResult.Success -> _uiState.update {
it.copy( it.copy(
recoveryCodes = result.data, recoveryCodes = result.data,
message = "Recovery codes regenerated.", message = context.getString(R.string.account_info_recovery_codes_regenerated),
errorMessage = null, errorMessage = null,
) )
} }
@@ -461,11 +466,11 @@ class AccountViewModel @Inject constructor(
private fun AppError.toUiMessage(): String { private fun AppError.toUiMessage(): String {
return when (this) { return when (this) {
AppError.InvalidCredentials -> "Invalid credentials." AppError.InvalidCredentials -> context.getString(R.string.account_error_invalid_credentials)
AppError.Unauthorized -> "Unauthorized." AppError.Unauthorized -> context.getString(R.string.account_error_unauthorized)
AppError.Network -> "Network error." AppError.Network -> context.getString(R.string.error_network)
is AppError.Server -> message ?: "Server error." is AppError.Server -> message ?: context.getString(R.string.error_server)
is AppError.Unknown -> cause?.message ?: "Unknown error." is AppError.Unknown -> cause?.message ?: context.getString(R.string.error_unknown)
} }
} }
} }

View File

@@ -263,4 +263,16 @@
<string name="error_authorization">Ошибка авторизации.</string> <string name="error_authorization">Ошибка авторизации.</string>
<string name="error_server">Ошибка сервера.</string> <string name="error_server">Ошибка сервера.</string>
<string name="error_unknown">Неизвестная ошибка.</string> <string name="error_unknown">Неизвестная ошибка.</string>
<string name="account_user_fallback">Пользователь #%1$d</string>
<string name="account_error_email_password_required">Нужны email и пароль.</string>
<string name="account_info_added">Аккаунт добавлен.</string>
<string name="account_error_no_saved_session">Для этого аккаунта нет сохраненной сессии.</string>
<string name="account_error_switch_sync_failed">Аккаунт переключен, но синхронизация чатов не удалась. Потяните вниз для обновления.</string>
<string name="account_info_profile_updated">Профиль обновлен.</string>
<string name="account_info_avatar_uploaded">Аватар загружен.</string>
<string name="account_info_privacy_updated">Настройки приватности обновлены.</string>
<string name="account_info_2fa_secret_generated">Секрет 2FA сгенерирован. Введите код для включения.</string>
<string name="account_info_recovery_codes_regenerated">Коды восстановления перегенерированы.</string>
<string name="account_error_invalid_credentials">Неверные учетные данные.</string>
<string name="account_error_unauthorized">Не авторизовано.</string>
</resources> </resources>

View File

@@ -263,4 +263,16 @@
<string name="error_authorization">Authorization error.</string> <string name="error_authorization">Authorization error.</string>
<string name="error_server">Server error.</string> <string name="error_server">Server error.</string>
<string name="error_unknown">Unknown error.</string> <string name="error_unknown">Unknown error.</string>
<string name="account_user_fallback">User #%1$d</string>
<string name="account_error_email_password_required">Email and password are required.</string>
<string name="account_info_added">Account added.</string>
<string name="account_error_no_saved_session">No saved session for this account.</string>
<string name="account_error_switch_sync_failed">Account switched, but chats sync failed. Pull to refresh.</string>
<string name="account_info_profile_updated">Profile updated.</string>
<string name="account_info_avatar_uploaded">Avatar uploaded.</string>
<string name="account_info_privacy_updated">Privacy settings updated.</string>
<string name="account_info_2fa_secret_generated">2FA secret generated. Enter code to enable.</string>
<string name="account_info_recovery_codes_regenerated">Recovery codes regenerated.</string>
<string name="account_error_invalid_credentials">Invalid credentials.</string>
<string name="account_error_unauthorized">Unauthorized.</string>
</resources> </resources>