Updated on 2026-08-14

This commit is contained in:
Tangem 2025-08-01 15:46:06 +04:00
parent 71003c43db
commit 3cd5c708c9
7 changed files with 564 additions and 145 deletions

View file

@ -0,0 +1,127 @@
package com.tangem.domain.models.account
import arrow.core.Either
import arrow.core.raise.either
import arrow.core.raise.ensure
import com.tangem.domain.models.TokensGroupType
import com.tangem.domain.models.TokensSortType
import com.tangem.domain.models.account.Account.CryptoPortfolio.Error.AccountNameError
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId
/**
* Represents an account
*
[REDACTED_AUTHOR]
*/
sealed interface Account {
/** Unique identifier of the account */
val accountId: AccountId
/** Name of the account */
val name: AccountName
/** The identifier of the user wallet associated with the account */
val userWalletId: UserWalletId
get() = accountId.userWalletId
/**
* Represents a crypto portfolio account
*
* @property accountId unique identifier of the account
* @property name name of the account
* @property icon icon representing the account
* @property derivationIndex index used for derivation of the account
* @property isArchived indicates whether the account is archived
* @property cryptoCurrencyList list of tokens associated with the account
*/
data class CryptoPortfolio private constructor(
override val accountId: AccountId,
override val name: AccountName,
val icon: CryptoPortfolioIcon,
val derivationIndex: Int,
val isArchived: Boolean,
val cryptoCurrencyList: CryptoCurrencyList,
) : Account {
/** Indicates if the account is the main account */
val isMainAccount: Boolean
get() = derivationIndex == 0
/** Number of tokens in the account */
val tokensCount: Int
get() = cryptoCurrencyList.currencies.size
/** Number of distinct networks in the account */
val networksCount: Int
get() = cryptoCurrencyList.currencies.map(CryptoCurrency::network).distinct().size
/**
* Represents a list of tokens in the account
*
* @property currencies set of cryptocurrencies in the account
* @property sortType sorting type for the tokens
* @property groupType grouping type for the tokens
*/
data class CryptoCurrencyList(
val currencies: Set<CryptoCurrency>,
val sortType: TokensSortType,
val groupType: TokensGroupType,
)
/**
* Represents possible errors when creating a crypto portfolio account
*/
sealed interface Error {
/** Error indicating that the account name is blank */
data class AccountNameError(val cause: AccountName.Error) : Error {
override fun toString(): String = cause.toString()
}
/** Error indicating that the derivation index is negative */
data object NegativeDerivationIndex : Error {
override fun toString(): String = "${this::class.simpleName}: Derivation index must be non-negative"
}
}
companion object {
/**
* Constructor for creating a [CryptoPortfolio] instance
*
* @param accountId unique identifier of the account
* @param name name of the account
* @param accountIcon icon representing the account
* @param derivationIndex index used for derivation of the account
* @param isArchived indicates whether the account is archived
* @param cryptoCurrencyList list of tokens associated with the account
*/
@Suppress("LongParameterList")
operator fun invoke(
accountId: AccountId,
name: String,
accountIcon: CryptoPortfolioIcon,
derivationIndex: Int,
isArchived: Boolean,
cryptoCurrencyList: CryptoCurrencyList,
): Either<Error, CryptoPortfolio> {
return either {
val accountName = AccountName(name).mapLeft(::AccountNameError).bind()
ensure(derivationIndex >= 0) { Error.NegativeDerivationIndex }
CryptoPortfolio(
accountId = accountId,
name = accountName,
icon = accountIcon,
derivationIndex = derivationIndex,
isArchived = isArchived,
cryptoCurrencyList = cryptoCurrencyList,
)
}
}
}
}
}

View file

@ -0,0 +1,14 @@
package com.tangem.domain.models.account
import com.tangem.domain.models.wallet.UserWalletId
/**
* Represents a unique identifier for an account
*
* @property value a unique string value that distinguishes this account
* @property userWalletId the identifier of the user wallet associated with the account
*/
data class AccountId(
val value: String,
val userWalletId: UserWalletId,
)

View file

@ -0,0 +1,59 @@
package com.tangem.domain.models.account
import arrow.core.Either
import arrow.core.raise.either
import arrow.core.raise.ensure
/**
* Represents an account name
*
* @property value the validated account name as a string
*
[REDACTED_AUTHOR]
*/
data class AccountName private constructor(
val value: String,
) {
/**
* Represents possible validation errors
*/
sealed interface Error {
/**
* Error indicating that the account name is blank
*/
data object Empty : Error {
override fun toString(): String = "${Empty::class.simpleName}: Account name cannot be blank"
}
/**
* Error indicating that the account name exceeds the maximum allowed length
*/
data object ExceedsMaxLength : Error {
override fun toString(): String {
return "${ExceedsMaxLength::class.simpleName}: Account name cannot exceed $MAX_LENGTH characters"
}
}
}
companion object {
private const val MAX_LENGTH = 20
/**
* 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)
}
}
}

View file

@ -1,48 +1,26 @@
package com.tangem.domain.models.account
import com.tangem.domain.models.account.CryptoPortfolioIcon.Companion.ofCustomAccount
import com.tangem.domain.models.account.CryptoPortfolioIcon.Companion.ofDefaultCustomAccount
import com.tangem.domain.models.account.CryptoPortfolioIcon.Companion.ofMainAccount
import com.tangem.domain.models.wallet.UserWalletId
import kotlinx.serialization.Serializable
/**
* Represents an icon for an [Account.CryptoPortfolio] account
*
* @property type the type of the account icon
* @property value the type of the account icon
* @property color the color of the account icon
*
* @constructor [ofMainAccount], [ofCustomAccount]
* @constructor [ofMainAccount], [ofDefaultCustomAccount]
*
[REDACTED_AUTHOR]
*/
@Serializable
data class CryptoPortfolioIcon private constructor(
val type: Type,
val value: Icon,
val color: Color,
) {
/**
* Represents the type of an account icon. Can either be a specific [Icon] or a [Symbol]
*/
@Serializable
sealed interface Type {
/**
* Represents a specific predefined icon type
*
* @property value the predefined [Icon] of the icon
*/
@Serializable
data class Icon(val value: CryptoPortfolioIcon.Icon) : Type
/**
* Represents an icon with a letter
*
* @property value the letter used as the icon
*/
@Serializable
data class Symbol(val value: Char) : Type
}
/**
* Enum class representing the icons of accounts
*/
@ -91,59 +69,44 @@ data class CryptoPortfolioIcon private constructor(
companion object {
private val defaultMainAccountType: Icon = Icon.Star
private val defaultMainAccountColor: Color = Color.Azure
private val defaultMainAccountIcon: Icon = Icon.Star
private val excludedCustomAccountIcons: Set<Icon> = setOf(Icon.Letter, Icon.Star)
private const val HASH_MULTIPLIER = 31
/**
* Creates an [CryptoPortfolioIcon] for the Main account, ensuring the color is not in the excluded set.
* Creating a [CryptoPortfolioIcon] for the Main account with default values.
* The color is derived from the [UserWalletId].
*
* @param exclude excluded colors that are already used for main accounts
* @param userWalletId the ID of the user wallet
*/
fun ofMainAccount(exclude: Set<Color>): CryptoPortfolioIcon {
val isDefaultColorBusy = defaultMainAccountColor in exclude
fun ofMainAccount(userWalletId: UserWalletId): CryptoPortfolioIcon {
val colors = Color.entries
val hash = userWalletId.value.fold(0) { acc, byte -> acc * HASH_MULTIPLIER + byte }
val color = if (isDefaultColorBusy) {
val colorsWithExcluded = Color.entries - exclude
val index = (hash and Int.MAX_VALUE) % colors.size
val color = colors[index]
val availableColors = if (colorsWithExcluded.isNotEmpty()) {
colorsWithExcluded
} else {
Color.entries
}
availableColors.random()
} else {
defaultMainAccountColor
}
return CryptoPortfolioIcon(
type = Type.Icon(value = defaultMainAccountType),
color = color,
)
return CryptoPortfolioIcon(value = defaultMainAccountIcon, color = color)
}
/**
* Creates an [CryptoPortfolioIcon] for a user account based on the account name
*
* @param accountName the name of the account, used to determine the letter for the icon
*/
fun ofCustomAccount(accountName: String): CryptoPortfolioIcon {
fun ofDefaultCustomAccount(): CryptoPortfolioIcon {
val icon = (Icon.entries - excludedCustomAccountIcons).random()
val color = Color.entries.random()
return CryptoPortfolioIcon(
type = Type.Symbol(value = accountName.first()),
color = color,
)
return CryptoPortfolioIcon(value = icon, color = color)
}
/**
* Creates a [CryptoPortfolioIcon] for a user account with a specific type and color
*
* @param type the type of the account icon
* @param value the icon of the account
* @param color the color of the account icon
*/
fun ofCustomAccount(type: Type, color: Color): CryptoPortfolioIcon {
return CryptoPortfolioIcon(type = type, color = color)
fun ofCustomAccount(value: Icon, color: Color): CryptoPortfolioIcon {
return CryptoPortfolioIcon(value = value, color = color)
}
}
}

View file

@ -0,0 +1,67 @@
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.TestInstance
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource
/**
[REDACTED_AUTHOR]
*/
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class AccountNameTest {
@ParameterizedTest
@MethodSource("provideTestModels")
fun invoke(model: InvokeTestModel) {
// Act
val actual = AccountName(value = model.value)
// Assert
actual
.onRight {
val expected = model.expected.getOrNull()!!
Truth.assertThat(it).isEqualTo(expected)
}
.onLeft {
val expected = model.expected.leftOrNull()!!
Truth.assertThat(it).isEqualTo(expected)
}
}
private fun provideTestModels() = listOf(
InvokeTestModel(
value = "",
expected = AccountName.Error.Empty.left(),
),
InvokeTestModel(
value = " ",
expected = AccountName.Error.Empty.left(),
),
InvokeTestModel(
value = "a".repeat(21),
expected = AccountName.Error.ExceedsMaxLength.left(),
),
"a".repeat(20).let { value ->
InvokeTestModel(
value = value,
expected = AccountName(value = value),
)
},
InvokeTestModel(
value = " name ",
expected = AccountName(value = "name"),
),
InvokeTestModel(
value = "Main Account",
expected = AccountName(value = "Main Account"),
),
)
data class InvokeTestModel(
val value: String,
val expected: Either<AccountName.Error, AccountName>,
)
}

View file

@ -0,0 +1,187 @@
package com.tangem.domain.models.account
import com.google.common.truth.Truth
import com.tangem.domain.models.TokensGroupType
import com.tangem.domain.models.TokensSortType
import com.tangem.domain.models.account.Account.CryptoPortfolio.CryptoCurrencyList
import com.tangem.domain.models.account.Account.CryptoPortfolio.Error.AccountNameError
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId
import io.mockk.every
import io.mockk.mockk
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
/**
[REDACTED_AUTHOR]
*/
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class AccountTest {
@Test
fun `Account userWalletId`() {
// Arrange
val userWalletId = UserWalletId("011")
// Act
val actual = createCryptoPortfolioStub(userWalletId = userWalletId).userWalletId
// Assert
Truth.assertThat(actual).isEqualTo(userWalletId)
}
@Test
fun `CryptoPortfolio isMainAccount`() {
// Arrange
val derivationIndex0 = 0
val derivationIndex1 = 1
// Act
val actual1 = createCryptoPortfolioStub(derivationIndex = derivationIndex0)
.isMainAccount
val actual2 = createCryptoPortfolioStub(derivationIndex = derivationIndex1)
.isMainAccount
// Assert
Truth.assertThat(actual1).isTrue()
Truth.assertThat(actual2).isFalse()
}
@Test
fun `CryptoPortfolio tokensCount`() {
// Arrange
val emptyCurrencies = emptySet<CryptoCurrency>()
val filledCurrencies = setOf(mockk<CryptoCurrency>())
// Act
val actual1 = createCryptoPortfolioStub(currencies = emptyCurrencies)
.tokensCount
val actual2 = createCryptoPortfolioStub(currencies = filledCurrencies)
.tokensCount
// Assert
Truth.assertThat(actual1).isEqualTo(0)
Truth.assertThat(actual2).isEqualTo(1)
}
@Test
fun `CryptoPortfolio networksCount`() {
// Arrange
val emptyCurrencies = emptySet<CryptoCurrency>()
val filledCurrencies = setOf(
mockk<CryptoCurrency> {
every { network } returns mockk()
},
)
// Act
val actual1 = createCryptoPortfolioStub(currencies = emptyCurrencies)
.networksCount
val actual2 = createCryptoPortfolioStub(currencies = filledCurrencies)
.networksCount
// Assert
Truth.assertThat(actual1).isEqualTo(0)
Truth.assertThat(actual2).isEqualTo(1)
}
@Nested
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
inner class CreateCryptoPortfolio {
@Test
fun `invoke returns AccountNameError`() {
// Arrange
val name = ""
// Act
val actual = Account.CryptoPortfolio(
accountId = mockk(),
name = name,
accountIcon = mockk(),
derivationIndex = 0,
isArchived = false,
cryptoCurrencyList = mockk(),
)
.leftOrNull()!!
// Assert
val expected = AccountNameError(cause = AccountName.Error.Empty)
Truth.assertThat(actual).isEqualTo(expected)
}
@Test
fun `invoke returns NegativeDerivationIndex`() {
// Arrange
val derivationIndex = -1
// Act
val actual = Account.CryptoPortfolio(
accountId = mockk(),
name = "Test Account",
accountIcon = mockk(),
derivationIndex = derivationIndex,
isArchived = false,
cryptoCurrencyList = mockk(),
)
.leftOrNull()!!
// Assert
val expected = Account.CryptoPortfolio.Error.NegativeDerivationIndex
Truth.assertThat(actual).isEqualTo(expected)
}
@Test
fun `invoke returns CryptoPortfolio`() {
// Act
val actual = Account.CryptoPortfolio(
accountId = AccountId(
value = "value",
userWalletId = UserWalletId("011"),
),
name = "Test Account",
accountIcon = CryptoPortfolioIcon.ofMainAccount(userWalletId = UserWalletId("011")),
derivationIndex = 0,
isArchived = false,
cryptoCurrencyList = CryptoCurrencyList(
currencies = emptySet(),
sortType = TokensSortType.NONE,
groupType = TokensGroupType.NONE,
),
)
.getOrNull()!!
// Assert
val expected = createCryptoPortfolioStub()
Truth.assertThat(actual).isEqualTo(expected)
}
}
private fun createCryptoPortfolioStub(
userWalletId: UserWalletId = UserWalletId("011"),
name: String = "Test Account",
derivationIndex: Int = 0,
currencies: Set<CryptoCurrency> = emptySet(),
): Account.CryptoPortfolio {
return Account.CryptoPortfolio.invoke(
accountId = AccountId(
value = "value",
userWalletId = userWalletId,
),
name = name,
accountIcon = CryptoPortfolioIcon.ofMainAccount(userWalletId),
derivationIndex = derivationIndex,
isArchived = false,
cryptoCurrencyList = CryptoCurrencyList(
currencies = currencies,
sortType = TokensSortType.NONE,
groupType = TokensGroupType.NONE,
),
)
.getOrNull()!!
}
}

View file

@ -1,13 +1,14 @@
package com.tangem.domain.models.account
import com.google.common.truth.Truth
import com.tangem.domain.models.account.CryptoPortfolioIcon.*
import com.tangem.domain.models.account.CryptoPortfolioIcon.Color
import com.tangem.domain.models.account.CryptoPortfolioIcon.Icon
import com.tangem.domain.models.wallet.UserWalletId
import io.mockk.every
import io.mockk.mockkObject
import io.mockk.unmockkObject
import io.mockk.verify
import io.mockk.verifyOrder
import org.junit.jupiter.api.Nested
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
@ -23,159 +24,160 @@ class CryptoPortfolioIconTest {
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
inner class OfMainAccount {
@Test
fun `ofMainAccount with empty exclude`() {
// Act
val actual = CryptoPortfolioIcon.ofMainAccount(exclude = emptySet())
// Assert
val expectedColor = Color.Azure
Truth.assertThat(actual.color).isEqualTo(expectedColor)
val expectedType = Type.Icon(value = Icon.Star)
Truth.assertThat(actual.type).isEqualTo(expectedType)
}
@ParameterizedTest
@MethodSource("provideTestModels")
fun ofMainAccount(model: OfMainAccountModel) {
// Arrange
mockkObject(Random.Default)
val size = (Color.entries.size - model.exclude.size).takeIf { it > 0 } ?: Color.entries.size
every { Random.nextInt(size) } returns model.randomNextInt
// Act
val actual = CryptoPortfolioIcon.ofMainAccount(exclude = model.exclude)
val actual = CryptoPortfolioIcon.ofMainAccount(userWalletId = model.userWalletId)
// Assert
val expectedColor = model.expectedColor
Truth.assertThat(actual.color).isEqualTo(expectedColor)
val expectedType = Type.Icon(value = Icon.Star)
Truth.assertThat(actual.type).isEqualTo(expectedType)
verify(exactly = 1) { Random.nextInt(size) }
unmockkObject(Random.Default)
val expectedIcon = Icon.Star
Truth.assertThat(actual.value).isEqualTo(expectedIcon)
}
private fun provideTestModels() = listOf(
// If the default color is already occupied (present in the exclude set), a random color from the
// remaining available colors will be selected for the main account icon.
OfMainAccountModel(
exclude = setOf(Color.Azure),
randomNextInt = 0,
expectedColor = Color.entries[1],
userWalletId = UserWalletId("1234567890abcdef"),
expectedColor = Color.Pattypan,
),
OfMainAccountModel(
exclude = setOf(Color.Azure, Color.CaribbeanBlue),
randomNextInt = 0,
expectedColor = Color.entries[2],
userWalletId = UserWalletId("27163F47405CE73110837F24DF82607FF11C7AF9D78C93F409E4FEAFF3400C8F"),
expectedColor = Color.CandyGrapeFizz,
),
// If all colors are already occupied, a random one will be selected.
OfMainAccountModel(
exclude = Color.entries.toSet(),
randomNextInt = 1,
expectedColor = Color.entries[1],
userWalletId = UserWalletId("64A3791C180584C700EBECD6EAB36CBC34643BB449BC87761104C09F41DBCF3D"),
expectedColor = Color.PalatinateBlue,
),
OfMainAccountModel(
userWalletId = UserWalletId("01C061A99FCCEDA87933267EBAB3513592F83AD2E27BDA6EE5546BA96009D21F"),
expectedColor = Color.Pelati,
),
OfMainAccountModel(
userWalletId = UserWalletId("6D387A8FA5D2AF95F601EBCA8736D73D2ED53159835D8C407FBD4BBB10290C8B"),
expectedColor = Color.CaribbeanBlue,
),
OfMainAccountModel(
userWalletId = UserWalletId("33FCD9B9982C31648C235AE55A29212D567ECD3BA24BE4227D1A01897ADBC959"),
expectedColor = Color.SweetDesire,
),
OfMainAccountModel(
userWalletId = UserWalletId("197C8C5AA59270F3E9E1F30799A007D193DA596E6DC24C37D002C2EC203C2A0B"),
expectedColor = Color.VitalGreen,
),
OfMainAccountModel(
userWalletId = UserWalletId("ACF90C18393828958B5E795771F0692A00D3D7ADC092F726AB4A7E3116DD6E6E"),
expectedColor = Color.Pattypan,
),
)
}
data class OfMainAccountModel(
val exclude: Set<Color>,
val randomNextInt: Int,
val userWalletId: UserWalletId,
val expectedColor: Color,
)
@Nested
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
inner class OfCustomAccountBasedOnName {
inner class OfDefaultCustomAccount {
@ParameterizedTest
@MethodSource("provideTestModels")
fun ofCustomAccount(model: OfCustomAccountModel.BasedOnName) {
fun ofCustomAccount(model: OfDefaultCustomAccountModel) {
// Arrange
val availableIcons = Icon.entries - setOf(Icon.Letter, Icon.Star)
mockkObject(Random.Default)
every { Random.nextInt(until = Color.entries.size) } returns model.randomNextInt
every { Random.nextInt(until = availableIcons.size) } returns model.randomIconIndex
every { Random.nextInt(until = Color.entries.size) } returns model.randomColorIndex
// Act
val actual = CryptoPortfolioIcon.ofCustomAccount(accountName = model.accountName)
val actual = CryptoPortfolioIcon.ofDefaultCustomAccount()
// Assert
val expectedColor = model.expectedColor
Truth.assertThat(actual.color).isEqualTo(expectedColor)
val expected = model.expected
Truth.assertThat(actual).isEqualTo(expected)
val expectedType = Type.Symbol(value = model.accountName.first())
Truth.assertThat(actual.type).isEqualTo(expectedType)
verify(exactly = 1) { Random.nextInt(until = Color.entries.size) }
verifyOrder {
Random.nextInt(until = availableIcons.size)
Random.nextInt(until = Color.entries.size)
}
unmockkObject(Random.Default)
}
private fun provideTestModels() = listOf(
OfCustomAccountModel.BasedOnName(
accountName = "New account",
randomNextInt = 0,
expectedColor = Color.entries[0],
OfDefaultCustomAccountModel(
randomIconIndex = 0,
randomColorIndex = 0,
expected = CryptoPortfolioIcon.ofCustomAccount(value = Icon.User, color = Color.Azure),
),
OfCustomAccountModel.BasedOnName(
accountName = "Awesome",
randomNextInt = Color.entries.lastIndex,
expectedColor = Color.entries.last(),
OfDefaultCustomAccountModel(
randomIconIndex = 1,
randomColorIndex = 1,
expected = CryptoPortfolioIcon.ofCustomAccount(value = Icon.Family, color = Color.CaribbeanBlue),
),
OfDefaultCustomAccountModel(
randomIconIndex = Icon.entries.lastIndex - 2,
randomColorIndex = Color.entries.lastIndex,
expected = CryptoPortfolioIcon.ofCustomAccount(value = Icon.Gift, color = Color.VitalGreen),
),
)
}
data class OfDefaultCustomAccountModel(
val randomIconIndex: Int,
val randomColorIndex: Int,
val expected: CryptoPortfolioIcon,
)
@Nested
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
inner class OfCustomAccountWithTypeAndColor {
@ParameterizedTest
@MethodSource("provideTestModels")
fun ofCustomAccount(model: OfCustomAccountModel.WithTypeAndColor) {
fun ofCustomAccount(model: OfCustomAccountModel) {
// Act
val actual = CryptoPortfolioIcon.ofCustomAccount(type = model.type, color = model.color)
val actual = CryptoPortfolioIcon.ofCustomAccount(value = model.icon, color = model.color)
// Assert
val expectedColor = model.expectedColor
Truth.assertThat(actual.color).isEqualTo(expectedColor)
val expectedType = model.expectedType
Truth.assertThat(actual.type).isEqualTo(expectedType)
Truth.assertThat(actual.value).isEqualTo(expectedType)
}
private fun provideTestModels() = listOf(
OfCustomAccountModel.WithTypeAndColor(
type = Type.Icon(value = Icon.User),
OfCustomAccountModel(
icon = Icon.User,
color = Color.CaribbeanBlue,
expectedType = Type.Icon(value = Icon.User),
expectedType = Icon.User,
expectedColor = Color.CaribbeanBlue,
),
OfCustomAccountModel.WithTypeAndColor(
type = Type.Symbol(value = 'A'),
OfCustomAccountModel(
icon = Icon.Letter,
color = Color.DullLavender,
expectedType = Type.Symbol(value = 'A'),
expectedType = Icon.Letter,
expectedColor = Color.DullLavender,
),
OfCustomAccountModel(
icon = Icon.Star,
color = Color.DullLavender,
expectedType = Icon.Star,
expectedColor = Color.DullLavender,
),
)
}
sealed interface OfCustomAccountModel {
data class BasedOnName(
val accountName: String,
val randomNextInt: Int,
val expectedColor: Color,
) : OfCustomAccountModel
data class WithTypeAndColor(
val type: Type,
val color: Color,
val expectedType: Type,
val expectedColor: Color,
) : OfCustomAccountModel
}
data class OfCustomAccountModel(
val icon: Icon,
val color: Color,
val expectedType: Icon,
val expectedColor: Color,
)
}