Updated on 2026-08-14
This commit is contained in:
parent
1c8b3ff14b
commit
2d6ead6d92
4 changed files with 131 additions and 3 deletions
|
|
@ -2,6 +2,7 @@ package com.tangem.domain.account.status.usecase
|
|||
|
||||
import arrow.core.Either
|
||||
import arrow.core.getOrElse
|
||||
import arrow.core.merge
|
||||
import arrow.core.raise.Raise
|
||||
import arrow.core.raise.catch
|
||||
import arrow.core.raise.either
|
||||
|
|
@ -9,10 +10,12 @@ import arrow.core.raise.ensure
|
|||
import com.tangem.domain.account.models.AccountList
|
||||
import com.tangem.domain.account.models.ArchivedAccount
|
||||
import com.tangem.domain.account.repository.AccountsCRUDRepository
|
||||
import com.tangem.domain.account.status.utils.AccountNameIndexer
|
||||
import com.tangem.domain.account.status.utils.CryptoCurrencyBalanceFetcher
|
||||
import com.tangem.domain.account.tokens.MainAccountTokensMigration
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.account.AccountName
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
||||
/**
|
||||
|
|
@ -45,8 +48,7 @@ class RecoverCryptoPortfolioUseCase(
|
|||
|
||||
val recoveredAccount = archivedAccount.recover()
|
||||
|
||||
val updatedAccountList = (accountList + recoveredAccount)
|
||||
.getOrElse { raise(Error.AccountListRequirementsNotMet(cause = it)) }
|
||||
val updatedAccountList = add(accountList, recoveredAccount)
|
||||
|
||||
saveAccounts(updatedAccountList)
|
||||
|
||||
|
|
@ -97,6 +99,25 @@ class RecoverCryptoPortfolioUseCase(
|
|||
)
|
||||
}
|
||||
|
||||
private fun Raise<Error>.add(accountList: AccountList, recoveredAccount: Account.CryptoPortfolio): AccountList {
|
||||
val maybeAccountList = accountList + recoveredAccount
|
||||
|
||||
return maybeAccountList.mapLeft {
|
||||
val recoveredAccountName = recoveredAccount.accountName as? AccountName.Custom
|
||||
?: raise(Error.DataOperationFailed(message = "Recovered account should have a custom name"))
|
||||
|
||||
val indexedName = AccountNameIndexer.transform(from = recoveredAccountName.value)
|
||||
.let(AccountName::invoke).getOrNull()
|
||||
?: raise(Error.DataOperationFailed(message = "Failed to generate indexed account name"))
|
||||
|
||||
val renamedAccount = recoveredAccount.copy(accountName = indexedName)
|
||||
|
||||
// recursively try to add the account with the new name
|
||||
add(accountList = accountList, recoveredAccount = renamedAccount)
|
||||
}
|
||||
.merge()
|
||||
}
|
||||
|
||||
private suspend fun refreshBalances(accountId: AccountId): Either<Error, Unit> = either {
|
||||
val currencies = catch(
|
||||
block = { crudRepository.getAccountSync(accountId = accountId) },
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
package com.tangem.domain.account.status.utils
|
||||
|
||||
import com.tangem.domain.models.account.AccountName
|
||||
|
||||
/**
|
||||
* Utility object for indexing account names to ensure uniqueness.
|
||||
*
|
||||
* @see [iOS](https://github.com/tangem-developments/tangem-app-ios/blob/32c56f1a566ecec00364ffeaf6b3c9929419ad15/Tangem/Domain/Accounts/Archiving/Utils/UnarchivedCryptoAccountNameIndexer.swift#L11)
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal object AccountNameIndexer {
|
||||
|
||||
private const val INITIAL_INDEX = 1
|
||||
private const val INDEX_PREFIX = "("
|
||||
private const val INDEX_SUFFIX = ")"
|
||||
|
||||
/**
|
||||
* Transforms the given account name by appending or incrementing an index suffix to ensure uniqueness.
|
||||
*
|
||||
* @param from the original account name
|
||||
*
|
||||
* @return the transformed account name with an updated index suffix
|
||||
*/
|
||||
fun transform(from: String): String {
|
||||
val (newIndex, currentIndex) = extractIndices(from)
|
||||
val accountNameSuffix = makeStringFromIndex(newIndex)
|
||||
val accountNamePrefixLength = (AccountName.MAX_LENGTH - accountNameSuffix.length).coerceAtLeast(0)
|
||||
|
||||
var accountNamePrefix = from
|
||||
if (currentIndex != null) {
|
||||
val accountNameCurrentSuffix = makeStringFromIndex(currentIndex)
|
||||
val accountNameCurrentSuffixLength = accountNameCurrentSuffix.length
|
||||
accountNamePrefix = accountNamePrefix.dropLast(accountNameCurrentSuffixLength)
|
||||
}
|
||||
|
||||
accountNamePrefix = accountNamePrefix.take(accountNamePrefixLength)
|
||||
|
||||
return accountNamePrefix + accountNameSuffix
|
||||
}
|
||||
|
||||
private fun extractIndices(string: String): Pair<Int, Int?> {
|
||||
val pattern = """\${INDEX_PREFIX}(\d+)\${INDEX_SUFFIX}$"""
|
||||
val regex = Regex(pattern)
|
||||
val match = regex.find(string)
|
||||
|
||||
return if (match != null && match.groupValues.size > 1) {
|
||||
val currentIndex = match.groupValues[1].toIntOrNull()
|
||||
|
||||
if (currentIndex != null) {
|
||||
Pair(currentIndex + 1, currentIndex)
|
||||
} else {
|
||||
Pair(INITIAL_INDEX, null)
|
||||
}
|
||||
} else {
|
||||
Pair(INITIAL_INDEX, null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeStringFromIndex(index: Int): String {
|
||||
return "${INDEX_PREFIX}$index${INDEX_SUFFIX}"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package com.tangem.domain.account.status.utils
|
||||
|
||||
import com.google.common.truth.Truth
|
||||
import com.tangem.common.test.utils.ProvideTestModels
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class AccountNameIndexerTest {
|
||||
|
||||
@ParameterizedTest
|
||||
@ProvideTestModels
|
||||
fun transform(model: TestModel) {
|
||||
// Act
|
||||
val actual = AccountNameIndexer.transform(model.input)
|
||||
|
||||
// Assert
|
||||
Truth.assertThat(actual).isEqualTo(model.expected)
|
||||
}
|
||||
|
||||
private fun provideTestModels() = listOf(
|
||||
TestModel(input = "Bitcoin", expected = "Bitcoin(1)"),
|
||||
TestModel(input = "Bitcoin(5)", expected = "Bitcoin(6)"),
|
||||
TestModel(input = "Ethereum(123)", expected = "Ethereum(124)"),
|
||||
TestModel(input = "Account(0)", expected = "Account(1)"),
|
||||
TestModel(input = "Bitcoin(old)new", expected = "Bitcoin(old)new(1)"),
|
||||
TestModel(input = "Bitcoin(10)new", expected = "Bitcoin(10)new(1)"),
|
||||
TestModel(input = "", expected = "(1)"),
|
||||
TestModel(input = "(42)", expected = "(43)"),
|
||||
TestModel(input = "Account(999999)", expected = "Account(1000000)"),
|
||||
TestModel(input = "Wallet(9)", expected = "Wallet(10)"),
|
||||
TestModel(input = "My Account (5)", expected = "My Account (6)"),
|
||||
TestModel(input = "Bitcoin💰(2)", expected = "Bitcoin💰(3)"),
|
||||
TestModel(input = "Bitcoin(abc)", expected = "Bitcoin(abc)(1)"),
|
||||
TestModel(input = "Bitcoin()", expected = "Bitcoin()(1)"),
|
||||
TestModel(input = "Bitcoin(", expected = "Bitcoin((1)"),
|
||||
TestModel(input = "Bitcoin)", expected = "Bitcoin)(1)"),
|
||||
TestModel(input = "EthereumEthereumEthereum(999)", expected = "EthereumEthere(1000)"),
|
||||
TestModel(input = "a".repeat(100), expected = "a".repeat(17) + "(1)"),
|
||||
)
|
||||
|
||||
data class TestModel(val input: String, val expected: String)
|
||||
}
|
||||
|
|
@ -74,7 +74,8 @@ sealed interface AccountName {
|
|||
|
||||
companion object {
|
||||
|
||||
private const val MAX_LENGTH = 20
|
||||
/** Maximum allowed length for an account name */
|
||||
const val MAX_LENGTH = 20
|
||||
|
||||
/**
|
||||
* Factory method to create an [AccountName] instance.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue