diff --git a/android/app/src/main/java/ru/daemonlord/messenger/ui/account/AccountViewModel.kt b/android/app/src/main/java/ru/daemonlord/messenger/ui/account/AccountViewModel.kt index 1175e62..32aceaf 100644 --- a/android/app/src/main/java/ru/daemonlord/messenger/ui/account/AccountViewModel.kt +++ b/android/app/src/main/java/ru/daemonlord/messenger/ui/account/AccountViewModel.kt @@ -1,9 +1,11 @@ package ru.daemonlord.messenger.ui.account +import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.appcompat.app.AppCompatDelegate import androidx.core.os.LocaleListCompat +import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow 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.repository.LanguageRepository import ru.daemonlord.messenger.domain.settings.repository.ThemeRepository +import ru.daemonlord.messenger.R import javax.inject.Inject @HiltViewModel @@ -36,6 +39,7 @@ class AccountViewModel @Inject constructor( private val languageRepository: LanguageRepository, private val themeRepository: ThemeRepository, private val pushTokenSyncManager: PushTokenSyncManager, + @ApplicationContext private val context: Context, ) : ViewModel() { private val _uiState = MutableStateFlow(AccountUiState()) val uiState: StateFlow = _uiState.asStateFlow() @@ -65,13 +69,14 @@ class AccountViewModel @Inject constructor( notificationsHistory = (notifications as? AppResult.Success)?.data ?: state.notificationsHistory, activeUserId = activeUserId, storedAccounts = storedAccounts.map { account -> + val fallbackUser = context.getString(R.string.account_user_fallback, account.userId) StoredAccountUi( userId = account.userId, - title = account.name.ifBlank { "User #${account.userId}" }, + title = account.name.ifBlank { fallbackUser }, subtitle = listOfNotNull( account.username?.takeIf { it.isNotBlank() }?.let { "@$it" }, account.email?.takeIf { it.isNotBlank() }, - ).joinToString(" • ").ifBlank { "User #${account.userId}" }, + ).joinToString(" • ").ifBlank { fallbackUser }, avatarUrl = account.avatarUrl, isActive = activeUserId == account.userId, ) @@ -131,7 +136,7 @@ class AccountViewModel @Inject constructor( fun addAccount(email: String, password: String, onDone: (Boolean) -> Unit = {}) { val normalizedEmail = email.trim() 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) return } @@ -142,7 +147,7 @@ class AccountViewModel @Inject constructor( _uiState.update { it.copy( isAddingAccount = false, - message = "Account added.", + message = context.getString(R.string.account_info_added), ) } refresh() @@ -165,7 +170,7 @@ class AccountViewModel @Inject constructor( viewModelScope.launch { val switched = tokenRepository.switchAccount(userId) 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) return@launch } @@ -180,7 +185,7 @@ class AccountViewModel @Inject constructor( if (syncFailed) { _uiState.update { 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( isSaving = false, profile = result.data, - message = "Profile updated.", + message = context.getString(R.string.account_info_profile_updated), ) } is AppResult.Error -> _uiState.update { @@ -231,7 +236,7 @@ class AccountViewModel @Inject constructor( _uiState.update { it.copy(isSaving = true, errorMessage = null, message = null) } when (val result = accountRepository.uploadAvatar(fileName, mimeType, bytes)) { 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) } is AppResult.Error -> _uiState.update { @@ -264,7 +269,7 @@ class AccountViewModel @Inject constructor( it.copy( isSaving = false, profile = result.data, - message = "Privacy settings updated.", + message = context.getString(R.string.account_info_privacy_updated), ) } is AppResult.Error -> _uiState.update { @@ -317,7 +322,7 @@ class AccountViewModel @Inject constructor( it.copy( twoFactorSecret = result.data.first, twoFactorOtpAuthUrl = result.data.second, - message = "2FA secret generated. Enter code to enable.", + message = context.getString(R.string.account_info_2fa_secret_generated), errorMessage = null, ) } @@ -366,7 +371,7 @@ class AccountViewModel @Inject constructor( is AppResult.Success -> _uiState.update { it.copy( recoveryCodes = result.data, - message = "Recovery codes regenerated.", + message = context.getString(R.string.account_info_recovery_codes_regenerated), errorMessage = null, ) } @@ -461,11 +466,11 @@ class AccountViewModel @Inject constructor( private fun AppError.toUiMessage(): String { return when (this) { - AppError.InvalidCredentials -> "Invalid credentials." - AppError.Unauthorized -> "Unauthorized." - AppError.Network -> "Network error." - is AppError.Server -> message ?: "Server error." - is AppError.Unknown -> cause?.message ?: "Unknown error." + AppError.InvalidCredentials -> context.getString(R.string.account_error_invalid_credentials) + AppError.Unauthorized -> context.getString(R.string.account_error_unauthorized) + AppError.Network -> context.getString(R.string.error_network) + is AppError.Server -> message ?: context.getString(R.string.error_server) + is AppError.Unknown -> cause?.message ?: context.getString(R.string.error_unknown) } } } diff --git a/android/app/src/main/res/values-ru/strings.xml b/android/app/src/main/res/values-ru/strings.xml index 98961d4..dad3a59 100644 --- a/android/app/src/main/res/values-ru/strings.xml +++ b/android/app/src/main/res/values-ru/strings.xml @@ -263,4 +263,16 @@ Ошибка авторизации. Ошибка сервера. Неизвестная ошибка. + Пользователь #%1$d + Нужны email и пароль. + Аккаунт добавлен. + Для этого аккаунта нет сохраненной сессии. + Аккаунт переключен, но синхронизация чатов не удалась. Потяните вниз для обновления. + Профиль обновлен. + Аватар загружен. + Настройки приватности обновлены. + Секрет 2FA сгенерирован. Введите код для включения. + Коды восстановления перегенерированы. + Неверные учетные данные. + Не авторизовано. diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 07e506f..ada95fb 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -263,4 +263,16 @@ Authorization error. Server error. Unknown error. + User #%1$d + Email and password are required. + Account added. + No saved session for this account. + Account switched, but chats sync failed. Pull to refresh. + Profile updated. + Avatar uploaded. + Privacy settings updated. + 2FA secret generated. Enter code to enable. + Recovery codes regenerated. + Invalid credentials. + Unauthorized.