Updated on 2026-08-14

This commit is contained in:
Tangem 2025-09-10 16:15:18 +04:00
parent 11511d36dd
commit 28b4c29bef
27 changed files with 278 additions and 84 deletions

View file

@ -0,0 +1,56 @@
package com.tangem.common.ui.account
import androidx.compose.runtime.Immutable
import arrow.core.Either
import arrow.core.raise.either
import com.tangem.core.ui.R
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.models.account.AccountName
/**
* Represents a user model (UM) for an [AccountName] in the UI layer.
* This sealed interface provides a way to handle different types of account names.
*/
@Immutable
sealed interface AccountNameUM {
/** The textual representation of the account name */
val value: TextReference
/**
* Represents the default main account name.
* If the user renames the main account, it will be converted to a [Custom] account name.
*/
data object DefaultMain : AccountNameUM {
override val value: TextReference = resourceReference(R.string.account_main_account_title)
}
/**
* Represents a custom account name provided by the user
*
* @property raw the raw string value of the custom account name
*/
class Custom(internal val raw: String) : AccountNameUM {
override val value: TextReference = stringReference(value = raw)
}
}
/** Extension function to convert a domain model [AccountName] to its corresponding UI model [AccountNameUM] */
fun AccountName.toUM(): AccountNameUM {
return when (this) {
is AccountName.Custom -> AccountNameUM.Custom(raw = value)
AccountName.DefaultMain -> AccountNameUM.DefaultMain
}
}
/** Extension function to convert a UI model [AccountNameUM] to its corresponding domain model [AccountName] */
fun AccountNameUM.toDomain(): Either<AccountName.Error, AccountName> = either {
when (this@toDomain) {
is AccountNameUM.Custom -> AccountName(value = raw).bind()
AccountNameUM.DefaultMain -> AccountName.DefaultMain
}
}

View file

@ -7,7 +7,7 @@ import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
@JsonClass(generateAdapter = true)
data class WalletAccountDTO(
@Json(name = "id") val id: String,
@Json(name = "name") val name: String,
@Json(name = "name") val name: String?,
@Json(name = "derivation") val derivationIndex: Int,
@Json(name = "icon") val icon: String,
@Json(name = "iconColor") val iconColor: String,

View file

@ -3,7 +3,6 @@ package com.tangem.data.account.converter
import arrow.core.getOrElse
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.account.AccountName
import com.tangem.domain.models.account.CryptoPortfolioIcon
import com.tangem.domain.models.account.DerivationIndex
import com.tangem.domain.models.wallet.UserWalletId
@ -14,12 +13,6 @@ internal fun String.toAccountId(userWalletId: UserWalletId): AccountId {
}
}
internal fun String.toAccountName(): AccountName {
return AccountName(value = this).getOrElse {
error("Unable to create AccountName from value: $this. Cause: $it")
}
}
internal fun WalletAccountDTO.toIcon(): CryptoPortfolioIcon {
return CryptoPortfolioIconConverter.convert(
value = CryptoPortfolioIconConverter.DataModel(icon = icon, color = iconColor),

View file

@ -0,0 +1,31 @@
package com.tangem.data.account.converter
import arrow.core.getOrElse
import com.tangem.domain.models.account.AccountName
import com.tangem.utils.converter.TwoWayConverter
/**
* A converter for transforming [AccountName] domain models into their string representations.
* This is used to handle the conversion logic between the domain layer and other layers.
*
[REDACTED_AUTHOR]
*/
internal object AccountNameConverter : TwoWayConverter<AccountName, String?> {
override fun convert(value: AccountName): String? {
return when (value) {
is AccountName.Custom -> value.value
AccountName.DefaultMain -> null
}
}
override fun convertBack(value: String?): AccountName {
return if (value == null) {
AccountName.DefaultMain
} else {
AccountName(value = value).getOrElse {
error("Unable to create AccountName from value: $value. Cause: $it")
}
}
}
}

View file

@ -19,7 +19,7 @@ internal class ArchivedAccountConverter(
override fun convert(value: WalletAccountDTO): ArchivedAccount {
return ArchivedAccount(
accountId = value.id.toAccountId(userWalletId = userWalletId),
name = value.name.toAccountName(),
name = AccountNameConverter.convertBack(value = value.name),
icon = value.toIcon(),
derivationIndex = value.derivationIndex.toDerivationIndex(),
tokensCount = value.totalTokens ?: error("Total tokens should not be null"),

View file

@ -29,7 +29,7 @@ internal class CryptoPortfolioConverter @AssistedInject constructor(
return Account.CryptoPortfolio(
accountId = value.id.toAccountId(userWallet.walletId),
accountName = value.name.toAccountName(),
accountName = AccountNameConverter.convertBack(value = value.name),
icon = value.toIcon(),
derivationIndex = value.derivationIndex.toDerivationIndex(),
cryptoCurrencies = if (tokens.isNotEmpty()) {
@ -46,7 +46,7 @@ internal class CryptoPortfolioConverter @AssistedInject constructor(
override fun convertBack(value: Account.CryptoPortfolio): WalletAccountDTO {
return WalletAccountDTO(
id = value.accountId.value,
name = value.accountName.value,
name = AccountNameConverter.convert(value = value.accountName),
derivationIndex = value.derivationIndex.value,
icon = value.icon.value.name,
iconColor = value.icon.color.name,

View file

@ -24,7 +24,7 @@ internal object SaveWalletAccountsResponseConverter : Converter<AccountList, Sav
private fun toDTO(account: Account.CryptoPortfolio): WalletAccountDTO {
return WalletAccountDTO(
id = account.accountId.value,
name = account.accountName.value,
name = AccountNameConverter.convert(value = account.accountName),
derivationIndex = account.derivationIndex.value,
icon = account.icon.value.name,
iconColor = account.icon.color.name,

View file

@ -7,6 +7,7 @@ import com.tangem.domain.account.models.AccountList
import com.tangem.domain.models.TokensGroupType
import com.tangem.domain.models.TokensSortType
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountName
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
@ -23,7 +24,7 @@ internal fun createWalletAccountDTO(
return WalletAccountDTO(
id = accountId ?: mainAccount.accountId.value,
name = accountName ?: mainAccount.accountName.value,
name = accountName ?: (mainAccount.accountName as? AccountName.Custom)?.value,
derivationIndex = derivationIndex ?: mainAccount.derivationIndex.value,
icon = icon ?: mainAccount.icon.value.name,
iconColor = iconColor ?: mainAccount.icon.color.name,

View file

@ -0,0 +1,65 @@
package com.tangem.data.account.converter
import com.google.common.truth.Truth
import com.tangem.common.test.utils.ProvideTestModels
import com.tangem.domain.models.account.AccountName
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.params.ParameterizedTest
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
internal class AccountNameConverterTest {
@Nested
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
inner class Convert {
@ParameterizedTest
@ProvideTestModels
fun convert(model: ConvertModel) {
// Act
val actual = AccountNameConverter.convert(value = model.value)
// Assert
val expected = model.expected
Truth.assertThat(actual).isEqualTo(expected)
}
private fun provideTestModels(): List<ConvertModel> {
return listOf(
ConvertModel(
value = AccountName.Custom("MyAccount").getOrNull()!!,
expected = "MyAccount",
),
ConvertModel(value = AccountName.DefaultMain, expected = null),
)
}
}
@Nested
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
inner class ConvertBack {
@ParameterizedTest
@ProvideTestModels
fun convertBack(model: ConvertBackModel) {
// Act
val actual = AccountNameConverter.convertBack(value = model.value)
// Assert
val expected = model.expected
Truth.assertThat(actual).isEqualTo(expected)
}
private fun provideTestModels(): List<ConvertBackModel> {
return listOf(
ConvertBackModel(value = "MyAccount", expected = AccountName.Custom("MyAccount").getOrNull()!!),
ConvertBackModel(value = null, expected = AccountName.DefaultMain),
)
}
}
data class ConvertModel(val value: AccountName, val expected: String?)
data class ConvertBackModel(val value: String?, val expected: AccountName)
}

View file

@ -54,6 +54,12 @@ class ArchivedAccountConverterTest {
),
),
),
TestModel(
value = createDTO(name = null),
expected = Result.success(
createDomain().copy(name = AccountName.DefaultMain),
),
),
TestModel(
value = createDTO(name = ""),
expected = Result.failure(
@ -104,7 +110,7 @@ class ArchivedAccountConverterTest {
private fun createDTO(
accountId: String = "957B88B12730E646E0F33D3618B77DFA579E8231E3C59C7104BE7165611C8027",
name: String = "Test Account",
name: String? = "Test Account",
icon: String = "Letter",
iconColor: String = "Azure",
derivationIndex: Int = 0,
@ -126,7 +132,7 @@ class ArchivedAccountConverterTest {
private fun createDomain(): ArchivedAccount {
return ArchivedAccount(
accountId = AccountId.forCryptoPortfolio(userWalletId, DerivationIndex(0).getOrNull()!!),
name = "Test Account".toAccountName(),
name = AccountName("Test Account").getOrNull()!!,
derivationIndex = 0.toDerivationIndex(),
icon = CryptoPortfolioIcon.ofCustomAccount(
value = CryptoPortfolioIcon.Icon.Letter,

View file

@ -75,6 +75,14 @@ class CryptoPortfolioConverterTest {
),
),
),
ConvertModel(
value = createWalletAccountDTO(userWalletId = userWallet.walletId, accountName = null),
expected = Result.success(
createCryptoPortfolio(userWalletId = userWallet.walletId).copy(
accountName = AccountName.DefaultMain,
),
),
),
ConvertModel(
value = createWalletAccountDTO(userWalletId = userWallet.walletId, accountName = ""),
expected = Result.failure(

View file

@ -5,6 +5,7 @@ import com.tangem.datasource.api.tangemTech.models.account.SaveWalletAccountsRes
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
import com.tangem.domain.account.models.AccountList
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountName
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import io.mockk.every
@ -37,7 +38,7 @@ class SaveWalletAccountsResponseConverterTest {
accounts = listOf(
WalletAccountDTO(
id = accountList.mainAccount.accountId.value,
name = accountList.mainAccount.accountName.value,
name = (accountList.mainAccount.accountName as? AccountName.Custom)?.value,
derivationIndex = accountList.mainAccount.derivationIndex.value,
icon = accountList.mainAccount.icon.value.name,
iconColor = accountList.mainAccount.icon.color.name,

View file

@ -6,6 +6,7 @@ import arrow.core.raise.ensure
import com.tangem.domain.models.TokensGroupType
import com.tangem.domain.models.TokensSortType
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountName
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.utils.extensions.addOrReplace
@ -119,8 +120,8 @@ data class AccountList private constructor(
}
@Serializable
data object DuplicateAccountNames : Error {
override fun toString(): String = "$tag: Account list contains duplicate account names"
data class DuplicateAccountNames(val message: String) : Error {
override fun toString(): String = "$tag: Account list contains duplicate account names. $message"
}
}
@ -160,8 +161,18 @@ data class AccountList private constructor(
val uniqueAccountIdsCount = accounts.map { it.accountId.value }.distinct().size
ensure(accounts.size == uniqueAccountIdsCount) { Error.DuplicateAccountIds }
val uniqueAccountNameCount = accounts.map { it.accountName.value }.distinct().size
ensure(accounts.size == uniqueAccountNameCount) { Error.DuplicateAccountNames }
val defaultMainNameCount = accounts.count { it.accountName is AccountName.DefaultMain }
val customNames = accounts.mapNotNull { (it.accountName as? AccountName.Custom)?.value }
val uniqueCustomNameCount = customNames.distinct().size
ensure(defaultMainNameCount == 0 || defaultMainNameCount == 1) {
Error.DuplicateAccountNames("Only one account can have the default main name.")
}
ensure(customNames.size == uniqueCustomNameCount) {
Error.DuplicateAccountNames("Custom account names must be unique.")
}
AccountList(
userWallet = userWallet,

View file

@ -157,7 +157,7 @@ class AccountListTest {
createAccount(userWalletId = userWalletId, name = "Name", derivationIndex = 0),
createAccount(userWalletId = userWalletId, name = "Name", derivationIndex = 1),
),
expected = AccountList.Error.DuplicateAccountNames.left(),
expected = AccountList.Error.DuplicateAccountNames("Custom account names must be unique.").left(),
),
)
}

View file

@ -162,7 +162,7 @@ sealed interface Account {
userWalletId = userWalletId,
derivationIndex = derivationIndex,
),
accountName = AccountName.Main,
accountName = AccountName.DefaultMain,
icon = CryptoPortfolioIcon.ofMainAccount(userWalletId),
derivationIndex = derivationIndex,
cryptoCurrencies = cryptoCurrencies,

View file

@ -8,14 +8,44 @@ import kotlinx.serialization.Serializable
/**
* Represents an account name
*
* @property value the validated account name as a string
*
[REDACTED_AUTHOR]
*/
@Serializable
data class AccountName private constructor(
val value: String,
) {
sealed interface AccountName {
/**
* Represents the default main account name.
* If the user renames the main account, it will be converted to a [Custom] account name.
*/
@Serializable
data object DefaultMain : AccountName
/**
* Represents a custom account name provided by the user
*
* @property value the string value of the custom account name
*/
@Serializable
data class Custom private constructor(val value: String) : AccountName {
companion object {
/**
* Factory method to create an [AccountName.Custom] instance.
* Validates the input string to ensure it is not blank and does not exceed the maximum length.
*
* @param value the input string representing the account name
*/
operator fun invoke(value: String): Either<Error, Custom> = either {
val trimmedValue = value.trim()
ensure(trimmedValue.isNotBlank()) { Error.Empty }
ensure(trimmedValue.length <= MAX_LENGTH) { Error.ExceedsMaxLength }
Custom(value = trimmedValue)
}
}
}
/**
* Represents possible validation errors
@ -44,26 +74,14 @@ data class AccountName private constructor(
companion object {
private const val MAIN_ACCOUNT_NAME = "Main Account"
private const val MAX_LENGTH = 20
/** Default name for the main account */
val Main: AccountName
get() = AccountName(value = MAIN_ACCOUNT_NAME)
/**
* Factory method to create an `AccountName` instance.
* Factory method to create an [AccountName] instance.
* Validates the input string to ensure it is not blank and does not exceed the maximum length.
*
* @param value the input string representing the account name
*/
operator fun invoke(value: String): Either<Error, AccountName> = either {
val trimmedValue = value.trim()
ensure(trimmedValue.isNotBlank()) { Error.Empty }
ensure(trimmedValue.length <= MAX_LENGTH) { Error.ExceedsMaxLength }
AccountName(value = trimmedValue)
}
operator fun invoke(value: String): Either<Error, AccountName> = Custom(value)
}
}

View file

@ -3,7 +3,6 @@ package com.tangem.domain.models.account
import arrow.core.Either
import arrow.core.left
import com.google.common.truth.Truth
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource
@ -14,16 +13,6 @@ import org.junit.jupiter.params.provider.MethodSource
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class AccountNameTest {
@Test
fun main_returnsMainAccountName() {
// Act
val main = AccountName.Main.value
// Assert
val expected = "Main Account"
Truth.assertThat(main).isEqualTo(expected)
}
@ParameterizedTest
@MethodSource("provideTestModels")
fun invoke(model: InvokeTestModel) {

View file

@ -147,7 +147,7 @@ class AccountTest {
userWalletId = userWalletId,
derivationIndex = derivationIndex,
),
accountName = AccountName.Main,
accountName = AccountName.DefaultMain,
icon = CryptoPortfolioIcon.ofMainAccount(userWalletId),
derivationIndex = derivationIndex,
cryptoCurrencies = emptySet(),

View file

@ -1,5 +1,6 @@
package com.tangem.features.account.archived
import com.tangem.common.ui.account.toUM
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
@ -50,7 +51,7 @@ internal class ArchivedAccountListModel @Inject constructor(
title = resourceReference(R.string.account_archived_recover_dialog_title),
message = resourceReference(
id = R.string.account_archived_recover_dialog_description,
formatArgs = wrappedList(account.accountName.value),
formatArgs = wrappedList(account.accountName.toUM().value),
),
firstActionBuilder = { firstAction },
secondActionBuilder = { secondAction },

View file

@ -1,6 +1,8 @@
package com.tangem.features.account.createedit
import com.tangem.common.ui.account.AccountNameUM
import com.tangem.common.ui.account.toDomain
import com.tangem.common.ui.account.toUM
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
import com.tangem.core.decompose.di.ModelScoped
@ -16,7 +18,6 @@ import com.tangem.core.ui.utils.showErrorDialog
import com.tangem.domain.account.usecase.AddCryptoPortfolioUseCase
import com.tangem.domain.account.usecase.GetUnoccupiedAccountIndexUseCase
import com.tangem.domain.account.usecase.UpdateCryptoPortfolioUseCase
import com.tangem.domain.models.account.AccountName
import com.tangem.domain.models.account.CryptoPortfolioIcon
import com.tangem.domain.models.account.DerivationIndex
import com.tangem.domain.models.wallet.UserWalletId
@ -92,7 +93,7 @@ internal class AccountCreateEditModel @Inject constructor(
private suspend fun createNewCryptoPortfolio(params: AccountCreateEditComponent.Params.Create) {
val state = uiState.value
val name = AccountName(value = state.account.name).getOrNull() ?: return
val name = state.account.name.toDomain().getOrNull() ?: return
val icon = state.account.portfolioIcon.toDomain()
val index = state.account.derivationInfo.index ?: return
val derivationIndex = DerivationIndex(value = index).getOrNull() ?: return
@ -107,7 +108,7 @@ internal class AccountCreateEditModel @Inject constructor(
private suspend fun editCryptoPortfolio(params: AccountCreateEditComponent.Params.Edit) {
val state = uiState.value
val name = AccountName(state.account.name).getOrNull() ?: return
val name = state.account.name.toDomain().getOrNull() ?: return
val icon = state.account.portfolioIcon.toDomain()
val isNewName = name != params.account.accountName
val isNewIcon = icon != params.account.portfolioIcon
@ -132,18 +133,20 @@ internal class AccountCreateEditModel @Inject constructor(
.validateNewState()
}
private fun onNameChange(name: String) {
private fun onNameChange(name: AccountNameUM) {
uiState.value = uiState.value
.updateName(name)
.validateNewState()
}
private fun AccountCreateEditUM.validateNewState(): AccountCreateEditUM {
val isValidName = AccountName(this.account.name).isRight()
val isValidName = this.account.name.toDomain().isRight()
val isAvailableForConfirm = when (params) {
is AccountCreateEditComponent.Params.Create -> isValidName
is AccountCreateEditComponent.Params.Edit -> {
val isNewName = this.account.name != params.account.accountName.value
val oldName = params.account.accountName.toUM()
val isNewName = this.account.name != oldName
val isNewIcon = this.account.portfolioIcon != params.account.portfolioIcon
isValidName && (isNewName || isNewIcon)
}

View file

@ -1,5 +1,6 @@
package com.tangem.features.account.createedit.entity
import com.tangem.common.ui.account.AccountNameUM
import com.tangem.common.ui.account.CryptoPortfolioIconUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.models.account.CryptoPortfolioIcon
@ -15,11 +16,11 @@ internal data class AccountCreateEditUM(
) {
data class Account(
val name: String,
val name: AccountNameUM,
val portfolioIcon: CryptoPortfolioIconUM,
val derivationInfo: DerivationInfo,
val inputPlaceholder: TextReference,
val onNameChange: (String) -> Unit,
val onNameChange: (AccountNameUM) -> Unit,
)
sealed interface DerivationInfo {

View file

@ -1,5 +1,6 @@
package com.tangem.features.account.createedit.entity
import com.tangem.common.ui.account.AccountNameUM
import com.tangem.common.ui.account.toUM
import com.tangem.core.res.R
import com.tangem.core.ui.extensions.TextReference
@ -24,17 +25,17 @@ internal class AccountCreateEditUMBuilder(
is AccountCreateEditComponent.Params.Edit -> resourceReference(R.string.account_form_title_edit)
}
fun initAccountUM(onNameChange: (String) -> Unit): AccountCreateEditUM.Account {
fun initAccountUM(onNameChange: (AccountNameUM) -> Unit): AccountCreateEditUM.Account {
return when (params) {
is AccountCreateEditComponent.Params.Create -> AccountCreateEditUM.Account(
name = "",
name = AccountNameUM.Custom(raw = ""),
portfolioIcon = createIcon,
derivationInfo = AccountCreateEditUM.DerivationInfo.Empty,
inputPlaceholder = resourceReference(R.string.account_form_placeholder_new_account),
onNameChange = onNameChange,
)
is AccountCreateEditComponent.Params.Edit -> AccountCreateEditUM.Account(
name = params.account.accountName.value,
name = params.account.accountName.toUM(),
portfolioIcon = params.account.portfolioIcon.toUM(),
derivationInfo = createAccountDerivationInfo(
index = (params.account as Account.CryptoPortfolio).derivationIndex.value,
@ -108,7 +109,7 @@ internal class AccountCreateEditUMBuilder(
)
}
fun AccountCreateEditUM.updateName(name: String): AccountCreateEditUM {
fun AccountCreateEditUM.updateName(name: AccountNameUM): AccountCreateEditUM {
return this.copy(account = this.account.copy(name = name))
}

View file

@ -13,6 +13,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@ -24,11 +25,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import androidx.compose.ui.unit.dp
import com.tangem.common.ui.R
import com.tangem.common.ui.account.AccountIcon
import com.tangem.common.ui.account.AccountIconPreviewData
import com.tangem.common.ui.account.AccountIconSize
import com.tangem.common.ui.account.getResId
import com.tangem.common.ui.account.getUiColor
import com.tangem.common.ui.account.*
import com.tangem.core.ui.components.PrimaryButton
import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.components.SpacerH24
@ -43,7 +40,6 @@ import com.tangem.features.account.createedit.entity.AccountCreateEditUM
import com.tangem.features.account.createedit.entity.AccountCreateEditUM.Account
import kotlinx.collections.immutable.toImmutableList
@Suppress("LongMethod", "MagicNumber")
@Composable
internal fun AccountCreateEditContent(state: AccountCreateEditUM, modifier: Modifier = Modifier) {
Column(
@ -65,7 +61,6 @@ internal fun AccountCreateEditContent(state: AccountCreateEditUM, modifier: Modi
modifier = Modifier
.padding(horizontal = 16.dp)
.weight(1f),
) {
AccountSummary(state.account)
SpacerH24()
@ -103,7 +98,7 @@ private fun AccountSummary(account: Account) {
Spacer(modifier = Modifier.height(24.dp))
AccountIcon(
name = stringReference(account.name),
name = account.name.value,
icon = account.portfolioIcon,
size = AccountIconSize.Large,
)
@ -116,13 +111,27 @@ private fun AccountSummary(account: Account) {
)
Spacer(modifier = Modifier.height(2.dp))
val wasDefault = remember { account.name is AccountNameUM.DefaultMain }
val defaultAccountName = AccountNameUM.DefaultMain.value.resolveReference()
AutoSizeTextField(
centered = true,
textStyle = TangemTheme.typography.head,
placeholder = account.inputPlaceholder,
value = account.name,
value = account.name.value.resolveReference(),
singleLine = true,
onValueChange = account.onNameChange,
onValueChange = {
/*
* If the user had the default main account name and enters the same name during renaming,
* we should use the default value instead of custom to avoid breaking the name validation process.
*/
val newName = if (wasDefault && it == defaultAccountName) {
AccountNameUM.DefaultMain
} else {
AccountNameUM.Custom(raw = it)
}
account.onNameChange(newName)
},
)
SpacerH(20.dp)
}
@ -279,7 +288,7 @@ private class PreviewStateProvider : CollectionPreviewParameterProvider<AccountC
title = stringReference("Add account"),
onCloseClick = {},
account = Account(
name = "",
name = AccountNameUM.DefaultMain,
portfolioIcon = portfolioIcon,
inputPlaceholder = resourceReference(R.string.account_form_placeholder_new_account),
onNameChange = {},
@ -313,7 +322,7 @@ private class PreviewStateProvider : CollectionPreviewParameterProvider<AccountC
onCloseClick = {},
account = Account(
portfolioIcon = portfolioIcon,
name = accountName,
name = AccountNameUM.Custom(raw = accountName),
inputPlaceholder = resourceReference(R.string.account_form_placeholder_edit_account),
onNameChange = {},
derivationInfo = AccountCreateEditUM.DerivationInfo.Content(

View file

@ -74,7 +74,7 @@ internal class AccountDetailsModel @Inject constructor(
private fun getInitialState(): AccountDetailsUM {
return AccountDetailsUM(
accountName = params.account.accountName.value,
accountName = params.account.accountName.toUM().value,
accountIcon = params.account.portfolioIcon.toUM(),
onCloseClick = { router.pop() },
onAccountEditClick = ::onEditAccountClick,

View file

@ -1,9 +1,10 @@
package com.tangem.features.account.details.entity
import com.tangem.common.ui.account.CryptoPortfolioIconUM
import com.tangem.core.ui.extensions.TextReference
internal data class AccountDetailsUM(
val accountName: String,
val accountName: TextReference,
val accountIcon: CryptoPortfolioIconUM,
val onCloseClick: () -> Unit,
val onAccountEditClick: () -> Unit,

View file

@ -143,7 +143,7 @@ private fun AccountRow(state: AccountDetailsUM) {
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
) {
AccountRow(
title = stringReference(state.accountName),
title = state.accountName,
subtitle = resourceReference(R.string.account_form_name),
icon = state.accountIcon,
modifier = Modifier.weight(1f),
@ -177,7 +177,7 @@ private class PreviewStateProvider : CollectionPreviewParameterProvider<AccountD
onAccountEditClick = {},
onManageTokensClick = {},
onArchiveAccountClick = {},
accountName = accountName,
accountName = stringReference(value = accountName),
accountIcon = portfolioIcon,
)
add(first)

View file

@ -8,7 +8,6 @@ import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.components.block.model.BlockUM
import com.tangem.core.ui.extensions.pluralReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.message.DialogMessage
import com.tangem.core.ui.message.EventMessageAction
@ -59,7 +58,7 @@ internal class AccountItemsDelegate @Inject constructor(
private fun Account.CryptoPortfolio.mapCryptoPortfolio(): WalletSettingsAccountsUM {
return WalletSettingsAccountsUM.Account(
id = accountId.value,
accountName = stringReference(accountName.value),
accountName = accountName.toUM().value,
accountIconUM = icon.toUM(),
tokensInfo = pluralReference(
R.plurals.common_tokens_count,