Updated on 2026-08-14

This commit is contained in:
Tangem 2024-04-04 13:07:18 +03:00
parent bc00198797
commit ddb8de4964
20 changed files with 447 additions and 84 deletions

View file

@ -55,9 +55,8 @@ internal object TokensDomainModule {
currenciesRepository: CurrenciesRepository,
quotesRepository: QuotesRepository,
networksRepository: NetworksRepository,
dispatchers: CoroutineDispatcherProvider,
): GetTokenListUseCase {
return GetTokenListUseCase(currenciesRepository, quotesRepository, networksRepository, dispatchers)
return GetTokenListUseCase(currenciesRepository, quotesRepository, networksRepository)
}
@Provides
@ -66,9 +65,8 @@ internal object TokensDomainModule {
currenciesRepository: CurrenciesRepository,
quotesRepository: QuotesRepository,
networksRepository: NetworksRepository,
dispatchers: CoroutineDispatcherProvider,
): GetCardTokensListUseCase {
return GetCardTokensListUseCase(currenciesRepository, quotesRepository, networksRepository, dispatchers)
return GetCardTokensListUseCase(currenciesRepository, quotesRepository, networksRepository)
}
@Provides

View file

@ -0,0 +1,96 @@
package com.tangem.domain.core.lce
import arrow.core.identity
import com.tangem.domain.core.utils.flatMap
import com.tangem.domain.core.utils.lceContent
import com.tangem.domain.core.utils.lceError
import com.tangem.domain.core.utils.lceLoading
/**
* A sealed class representing the three states of a data load operation: Loading, Content, and Error.
*
* @param E The type of the error object.
* @param C The type of the content object.
*/
sealed class Lce<out E : Any, out C : Any> {
/**
* Represents the loading state, which may contain partial content.
*
* @param partialContent The partial content that has been loaded so far, if any.
*/
data class Loading<C : Any>(val partialContent: C?) : Lce<Nothing, C>()
/**
* Represents the content state, which contains the loaded content.
*
* @param content The loaded content.
*/
data class Content<C : Any>(val content: C) : Lce<Nothing, C>()
/**
* Represents the error state, which contains an error object.
*
* @param error The error that occurred during loading.
*/
data class Error<E : Any>(val error: E) : Lce<E, Nothing>()
/**
* Applies the given functions to the content, error, or partial content of this Lce, depending on its state.
*
* @param ifLoading The function to apply if this is a [Loading] state.
* @param ifContent The function to apply if this is a [Content] state.
* @param ifError The function to apply if this is an [Error] state.
* @return The result of applying the corresponding function.
*/
inline fun <T> fold(
ifLoading: (partialContent: C?) -> T,
ifContent: (content: C) -> T,
ifError: (error: E) -> T,
): T = when (this) {
is Loading -> ifLoading(partialContent)
is Error -> ifError(error)
is Content -> ifContent(content)
}
/**
* Transforms the content of this [Lce] by applying the given function.
* If this is a [Content] state, the function is applied to the [Content.content].
* If this is a [Loading] state and partialContent is present,
* the function is applied to the [Loading.partialContent].
*
* @param ifContent The function to apply to the content or partial content.
* @return A new [Lce] instance containing the result of applying the function.
*/
inline fun <T : Any> map(ifContent: (C) -> T): Lce<E, T> = flatMap { content, isLoading ->
if (isLoading) {
lceLoading(ifContent(content))
} else {
ifContent(content).lceContent()
}
}
/**
* Transforms the error of this [Lce] by applying the given function, if this is an [Error] state.
*
* @param ifError The function to apply to the error.
* @return A new [Lce] with the transformed error, or this [Lce] unchanged if it is not an [Error] state.
*/
inline fun <T : Any> mapError(ifError: (E) -> T): Lce<T, C> = when (this) {
is Loading -> this
is Content -> this
is Error -> ifError(error).lceError()
}
/**
* Returns the content of this [Lce] if it's a [Lce.Content] or partial content if it's a [Lce.Loading],
* `null` if it's a [Lce.Error].
*
* @return The content of this [Lce] or `null`.
*/
fun getOrNull(): C? = fold(
ifLoading = ::identity,
ifContent = ::identity,
ifError = { null },
)
}

View file

@ -0,0 +1,99 @@
package com.tangem.domain.core.lce
import arrow.core.raise.Raise
import com.tangem.domain.core.utils.lceContent
import com.tangem.domain.core.utils.lceLoading
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.channels.ProducerScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.launch
import kotlin.experimental.ExperimentalTypeInference
/**
* A [Flow] of [Lce]
*
* @param E The type of the error object.
* @param C The type of the content object.
* */
typealias LceFlow<E, C> = Flow<Lce<E, C>>
/**
* A class that wraps a [LceRaise] instance for [Lce] type within a [ProducerScope].
* It provides methods to handle [Lce] instances and raise errors within a [Flow].
*
* @property raise The [LceRaise] instance that this class wraps.
* @property scope The [ProducerScope] that this class operates within.
* @property ifLoading The function to call if a loading state is raised.
*/
class LceFlowScope<E : Any, C : Any> @PublishedApi internal constructor(
private val raise: LceRaise<E>,
private val scope: ProducerScope<Lce<E, C>>,
private val ifLoading: LceRaise<E>.(C) -> Lce<E, C>,
) : Raise<Lce<E, Nothing>> by raise,
CoroutineScope by scope {
/**
* Raises an [Lce] instance within the [ProducerScope].
* It closes the [ProducerScope] after raise.
*
* @param r The [Lce] instance to raise.
*/
override fun raise(r: Lce<E, Nothing>): Nothing {
scope.launch(NonCancellable) {
scope.send(r)
scope.close()
}
raise.raise(r)
}
/**
* Sends a content value within the [ProducerScope].
* If the content is still loading, it calls [ifLoading] lambda to retrieve a state.
* Otherwise, it wraps the content in a [Lce.Content] state.
*
* @param content The content value to send.
* @param isStillLoading A flag indicating whether the content is still loading.
*/
suspend fun send(content: C, isStillLoading: Boolean = false) {
val value = if (isStillLoading) {
ifLoading(raise, content)
} else {
content.lceContent()
}
scope.send(value)
}
}
/**
* Creates a [LceFlow] by executing the given [block] within a [LceFlowScope] context.
*
* Flow starts with a [Lce.Loading] state.
*
* @param ifLoading The function to call if the [block] raises a [Lce.Loading] state.
* By default, it creates a new [Lce.Loading] state with the value returned by the [block].
* @param block The block to execute within a [LceFlowScope] context.
* @return A [LceFlow] representing the result of the [block].
*/
@OptIn(ExperimentalTypeInference::class)
fun <E : Any, C : Any> lceFlow(
ifLoading: LceRaise<E>.(C) -> Lce<E, C> = { lceLoading(partialContent = it) },
@BuilderInference block: suspend LceFlowScope<E, C>.() -> Unit,
): LceFlow<E, C> {
return channelFlow {
trySend(lceLoading())
lce {
val scope = LceFlowScope(
raise = this@lce,
scope = this@channelFlow,
ifLoading = ifLoading,
)
block(scope)
}
}
}

View file

@ -0,0 +1,86 @@
package com.tangem.domain.core.lce
import arrow.atomic.Atomic
import arrow.core.raise.Raise
import arrow.core.raise.recover
import com.tangem.domain.core.utils.lceContent
import com.tangem.domain.core.utils.lceLoading
import kotlin.experimental.ExperimentalTypeInference
/**
* A class that wraps a [Raise] instance for [Lce] type.
* It provides methods to handle [Lce] instances and raise errors.
*
* @property raise The [Raise] instance that this class wraps.
* @property isLoading An [Atomic] boolean flag indicating whether a loading operation is in progress.
*/
class LceRaise<E : Any> @PublishedApi internal constructor(
private val raise: Raise<Lce<E, Nothing>>,
) : Raise<Lce<E, Nothing>> by raise {
val isLoading: Atomic<Boolean> = Atomic(false)
/**
* Binds the content of this [Lce] instance and handles its state.
* If this is a [Lce.Loading] state, sets the [isLoading] flag to true and calls the [ifLoading] function.
* If this is a [Lce.Content] state, returns the content.
* If this is a [Lce.Error] state, raises the error.
*
* @param ifLoading The function to call if this is a [Lce.Loading] state.
* By default, it raises a new [Lce.Loading] state.
* @return The content of this [Lce] instance.
*/
fun <C : Any> Lce<E, C>.bind(ifLoading: (partialContent: C?) -> C = { raise(lceLoading()) }): C = when (this) {
is Lce.Loading -> {
isLoading.set(true)
ifLoading(partialContent)
}
is Lce.Content -> content
is Lce.Error -> raise(r = this)
}
/**
* Binds the content of this [Lce] instance and handles its state.
* If this is a [Lce.Loading] state, sets the [isLoading] flag to true and returns the partial content.
* If this is a [Lce.Content] state, returns the content.
* If this is a [Lce.Error] state, raises the error.
*
* @return The content of this [Lce] instance.
*/
fun <C : Any> Lce<E, C>.bindOrNull(): C? = when (this) {
is Lce.Loading -> {
isLoading.set(true)
partialContent
}
is Lce.Content -> content
is Lce.Error -> raise(r = this)
}
}
/**
* Creates a [Lce] instance by executing the given [block] within a [LceRaise] context.
*
* @param ifLoading The function to call if the [block] raises a [Lce.Loading] state.
* By default, it creates a new [Lce.Loading] state with the value returned by the [block].
* @param block The block to execute within a [LceRaise] context.
* @return A [Lce] instance representing the result of the [block].
*/
@OptIn(ExperimentalTypeInference::class)
inline fun <E : Any, C : Any> lce(
ifLoading: LceRaise<E>.(C) -> Lce<E, C> = { lceLoading(partialContent = it) },
@BuilderInference block: LceRaise<E>.() -> C,
): Lce<E, C> = recover(
block = {
val raise = LceRaise(raise = this)
val value = block(raise)
if (raise.isLoading.get()) {
ifLoading(raise, value)
} else {
value.lceContent()
}
},
recover = { e: Lce<E, Nothing> -> e },
)

View file

@ -0,0 +1,37 @@
package com.tangem.domain.core.utils
import arrow.core.Either
import com.tangem.domain.core.lce.Lce
import kotlinx.coroutines.flow.Flow
/**
* [Flow] of [Either]
*
* @param E type of left value
* @param A type of right value
* */
typealias EitherFlow<E, A> = Flow<Either<E, A>>
/**
* Converts an [Either] instance to a [Lce] instance.
* If this is a [Either.Left], it is converted to a [Lce.Error] with the same error.
* If this is a [Either.Right], it is converted to a [Lce.Content] or [Lce.Loading] with the same content,
* depending on the [isStillLoading] parameter.
*
* @param isStillLoading A flag indicating whether the content is still loading.
* If true, the [Either.Right] is converted to a [Lce.Loading].
* @return A [Lce] instance containing the same content or error as this [Either],
* and possibly indicating a loading state.
*/
inline fun <reified E : Any, reified T : Any> Either<E, T>.toLce(isStillLoading: Boolean = false): Lce<E, T> {
return when (this) {
is Either.Left -> Lce.Error(value)
is Either.Right -> {
if (isStillLoading) {
Lce.Loading(value)
} else {
Lce.Content(value)
}
}
}
}

View file

@ -0,0 +1,75 @@
package com.tangem.domain.core.utils
import arrow.core.Either
import arrow.core.identity
import arrow.core.left
import arrow.core.right
import com.tangem.domain.core.lce.Lce
/**
* Creates a [Lce.Loading] instance with optional partial content.
*
* @param partialContent The partial content that has been loaded so far, if any.
* @return A [Lce.Loading] instance.
*/
fun <C : Any> lceLoading(partialContent: C? = null): Lce<Nothing, C> = Lce.Loading(partialContent)
/**
* Wraps the receiver object in a [Lce.Content] instance.
*
* @return A [Lce.Content] instance containing the receiver object.
*/
fun <C : Any> C.lceContent(): Lce<Nothing, C> = Lce.Content(content = this)
/**
* Wraps the receiver object in a [Lce.Error] instance.
*
* @return A [Lce.Error] instance containing the receiver object.
*/
fun <E : Any> E.lceError(): Lce<E, Nothing> = Lce.Error(error = this)
/**
* Transforms this [Lce] instance by applying the given function into a new [Lce] instance.
*
* @param block The function to apply to the content of this [Lce].
* @return A new [Lce] instance containing the result of applying the function.
* */
inline fun <E : Any, C : Any, T : Any> Lce<E, C>.flatMap(
block: (content: C, isLoading: Boolean) -> Lce<E, T>,
): Lce<E, T> = when (this) {
is Lce.Loading -> partialContent?.let { block(it, true) } ?: lceLoading()
is Lce.Content -> block(content, false)
is Lce.Error -> this
}
/**
* Returns the content of this [Lce] if it's a [Lce.Content] or applies the given functions if it's a [Lce.Loading] or [Lce.Error].
*
* @param ifLoading The function to apply if this is a [Lce.Loading] state.
* @param ifError The function to apply if this is a [Lce.Error] state.
* @return The content of this [Lce] or the result of applying the corresponding function.
*/
inline fun <E : Any, C : Any> Lce<E, C>.getOrElse(ifLoading: (maybeContent: C?) -> C, ifError: (error: E) -> C): C {
return fold(
ifLoading = ifLoading,
ifContent = ::identity,
ifError = ifError,
)
}
/**
* Transforms this [Lce] into an [Either] instance.
* If this is a [Lce.Content], the content is wrapped in a [Either.Right].
* If this is a [Lce.Error], the error is wrapped in a [Either.Left].
* If this is a [Lce.Loading], the [ifLoading] function is applied to the partial content and the result is wrapped in a
* [Either.Right].
*
* @param ifLoading The function to apply if this is a [Lce.Loading] state.
* @return An [Either] instance containing the content or error of this [Lce],
* or the result of applying the [ifLoading] function to the partial content.
*/
inline fun <E : Any, C : Any> Lce<E, C>.toEither(ifLoading: (maybeContent: C?) -> C): Either<E, C> = fold(
ifLoading = { ifLoading(it).right() },
ifContent = { it.right() },
ifError = { it.left() },
)

View file

@ -15,7 +15,7 @@ import java.math.BigDecimal
*/
data class CryptoCurrencyStatus(
val currency: CryptoCurrency,
val value: Status,
val value: Value,
) {
/**
@ -23,7 +23,7 @@ data class CryptoCurrencyStatus(
*
* @property isError Indicates whether this status represents an error status.
*/
sealed class Status(val isError: Boolean) {
sealed class Value(val isError: Boolean) {
/** The amount of the cryptocurrency. */
open val amount: BigDecimal? = null
@ -48,7 +48,7 @@ data class CryptoCurrencyStatus(
}
/** Represents the Loading state of a cryptocurrency, typically while fetching its details. */
object Loading : Status(isError = false)
data object Loading : Value(isError = false)
/**
* Represents a state where the cryptocurrency is not reachable.
@ -61,19 +61,19 @@ data class CryptoCurrencyStatus(
override val priceChange: BigDecimal?,
override val fiatRate: BigDecimal?,
override val networkAddress: NetworkAddress?,
) : Status(isError = true)
) : Value(isError = true)
/** Represents a state where the cryptocurrency's network amount not found. */
data class NoAmount(
override val priceChange: BigDecimal?,
override val fiatRate: BigDecimal?,
) : Status(isError = true)
) : Value(isError = true)
/** Represents a state where the cryptocurrency's derivation is missed. */
data class MissedDerivation(
override val priceChange: BigDecimal?,
override val fiatRate: BigDecimal?,
) : Status(isError = true)
) : Value(isError = true)
/**
* Represents a state where there is no account associated with the cryptocurrency
@ -86,7 +86,7 @@ data class CryptoCurrencyStatus(
override val priceChange: BigDecimal?,
override val fiatRate: BigDecimal?,
override val networkAddress: NetworkAddress,
) : Status(isError = false) {
) : Value(isError = false) {
override val amount: BigDecimal = BigDecimal.ZERO
}
@ -110,7 +110,7 @@ data class CryptoCurrencyStatus(
override val hasCurrentNetworkTransactions: Boolean,
override val pendingTransactions: Set<TxHistoryItem>,
override val networkAddress: NetworkAddress,
) : Status(isError = false)
) : Value(isError = false)
/**
* Represents a Custom state of a cryptocurrency, typically used for user-defined tokens.
@ -131,7 +131,7 @@ data class CryptoCurrencyStatus(
override val hasCurrentNetworkTransactions: Boolean,
override val pendingTransactions: Set<TxHistoryItem>,
override val networkAddress: NetworkAddress,
) : Status(isError = false)
) : Value(isError = false)
/**
* Represents a state where the cryptocurrency is available, but there is no current quote available for it.
@ -146,5 +146,5 @@ data class CryptoCurrencyStatus(
override val hasCurrentNetworkTransactions: Boolean,
override val pendingTransactions: Set<TxHistoryItem>,
override val networkAddress: NetworkAddress,
) : Status(isError = false)
) : Value(isError = false)
}

View file

@ -11,7 +11,7 @@ import java.math.BigDecimal
*/
data class NetworkStatus(
val network: Network,
val value: Status,
val value: Value,
) {
/**
@ -19,19 +19,19 @@ data class NetworkStatus(
*
* This sealed class includes different states like unreachable, missed derivation, verified, and no account.
*/
sealed class Status
sealed class Value
/**
* Represents the state where the network is unreachable.
*
* @property address Network addresses.
*/
data class Unreachable(val address: NetworkAddress?) : Status()
data class Unreachable(val address: NetworkAddress?) : Value()
/**
* Represents the state where a derivation has been missed.
*/
object MissedDerivation : Status()
data object MissedDerivation : Value()
/**
* Represents the verified state of the network, including the amounts associated with different cryptocurrencies
@ -46,7 +46,7 @@ data class NetworkStatus(
val address: NetworkAddress,
val amounts: Map<CryptoCurrency.ID, CryptoCurrencyAmountStatus>,
val pendingTransactions: Map<CryptoCurrency.ID, Set<TxHistoryItem>>,
) : Status()
) : Value()
/**
* Represents the state where there is no account, and an amount is required to create one.
@ -59,5 +59,5 @@ data class NetworkStatus(
val address: NetworkAddress,
val amountToCreateAccount: BigDecimal,
val errorMessage: String,
) : Status()
) : Value()
}

View file

@ -1,7 +1,7 @@
package com.tangem.domain.tokens
import arrow.core.Either
import arrow.core.left
import com.tangem.domain.core.utils.EitherFlow
import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.tokens.error.mapper.mapToTokenListError
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
@ -12,19 +12,19 @@ import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.transformLatest
class GetCardTokensListUseCase(
internal val currenciesRepository: CurrenciesRepository,
internal val quotesRepository: QuotesRepository,
internal val networksRepository: NetworksRepository,
internal val dispatchers: CoroutineDispatcherProvider,
private val currenciesRepository: CurrenciesRepository,
private val quotesRepository: QuotesRepository,
private val networksRepository: NetworksRepository,
) {
@OptIn(ExperimentalCoroutinesApi::class)
operator fun invoke(userWalletId: UserWalletId): Flow<Either<TokenListError, TokenList>> {
operator fun invoke(userWalletId: UserWalletId): EitherFlow<TokenListError, TokenList> {
return getTokensStatuses(userWalletId).transformLatest { maybeTokens ->
maybeTokens.fold(
ifLeft = { error ->
@ -37,9 +37,7 @@ class GetCardTokensListUseCase(
}
}
private fun getTokensStatuses(
userWalletId: UserWalletId,
): Flow<Either<TokenListError, List<CryptoCurrencyStatus>>> {
private fun getTokensStatuses(userWalletId: UserWalletId): EitherFlow<TokenListError, List<CryptoCurrencyStatus>> {
val operations = CurrenciesStatusesOperations(
userWalletId = userWalletId,
currenciesRepository = currenciesRepository,
@ -56,7 +54,7 @@ class GetCardTokensListUseCase(
private fun createTokenList(
userWalletId: UserWalletId,
tokens: List<CryptoCurrencyStatus>,
): Flow<Either<TokenListError, TokenList>> {
): EitherFlow<TokenListError, TokenList> {
val operations = TokenListOperations(
userWalletId = userWalletId,
tokens = tokens,

View file

@ -12,7 +12,6 @@ import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emitAll
@ -20,10 +19,9 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.transformLatest
class GetTokenListUseCase(
internal val currenciesRepository: CurrenciesRepository,
internal val quotesRepository: QuotesRepository,
internal val networksRepository: NetworksRepository,
internal val dispatchers: CoroutineDispatcherProvider,
private val currenciesRepository: CurrenciesRepository,
private val quotesRepository: QuotesRepository,
private val networksRepository: NetworksRepository,
) {
@OptIn(ExperimentalCoroutinesApi::class)
@ -45,7 +43,9 @@ class GetTokenListUseCase(
): Flow<Either<TokenListError, List<CryptoCurrencyStatus>>> {
val operations = CurrenciesStatusesOperations(
userWalletId = userWalletId,
useCase = this@GetTokenListUseCase,
currenciesRepository = currenciesRepository,
quotesRepository = quotesRepository,
networksRepository = networksRepository,
)
return operations.getCurrenciesStatusesFlow()
@ -61,7 +61,7 @@ class GetTokenListUseCase(
val operations = TokenListOperations(
userWalletId = userWalletId,
tokens = tokens,
useCase = this@GetTokenListUseCase,
currenciesRepository = currenciesRepository,
)
return operations.getTokenListFlow().map { maybeTokenList ->

View file

@ -2,7 +2,7 @@ package com.tangem.domain.tokens.operations
import arrow.core.*
import arrow.core.raise.*
import com.tangem.domain.tokens.GetTokenListUseCase
import com.tangem.domain.core.utils.EitherFlow
import com.tangem.domain.tokens.model.*
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository
@ -20,18 +20,8 @@ internal class CurrenciesStatusesOperations(
private val userWalletId: UserWalletId,
) {
constructor(
userWalletId: UserWalletId,
useCase: GetTokenListUseCase,
) : this(
currenciesRepository = useCase.currenciesRepository,
quotesRepository = useCase.quotesRepository,
networksRepository = useCase.networksRepository,
userWalletId = userWalletId,
)
@OptIn(ExperimentalCoroutinesApi::class)
fun getCurrenciesStatusesFlow(): Flow<Either<Error, List<CryptoCurrencyStatus>>> {
fun getCurrenciesStatusesFlow(): EitherFlow<Error, List<CryptoCurrencyStatus>> {
return getMultiCurrencyWalletCurrencies().transformLatest { maybeCurrencies ->
val nonEmptyCurrencies = maybeCurrencies.fold(
ifLeft = { error ->

View file

@ -12,7 +12,7 @@ internal class CurrencyStatusOperations(
fun createTokenStatus(): CryptoCurrencyStatus = CryptoCurrencyStatus(currency, createStatus())
private fun createStatus(): CryptoCurrencyStatus.Status {
private fun createStatus(): CryptoCurrencyStatus.Value {
return when (val status = networkStatus?.value) {
null -> CryptoCurrencyStatus.Loading
is NetworkStatus.MissedDerivation -> createMissedDerivationStatus()
@ -42,7 +42,7 @@ internal class CurrencyStatusOperations(
networkAddress = status.address,
)
private fun createStatus(status: NetworkStatus.Verified): CryptoCurrencyStatus.Status {
private fun createStatus(status: NetworkStatus.Verified): CryptoCurrencyStatus.Value {
val amount = when (val amount = status.amounts[currency.id]) {
null -> {
return CryptoCurrencyStatus.Loading

View file

@ -4,7 +4,6 @@ import arrow.core.*
import arrow.core.raise.Raise
import arrow.core.raise.either
import arrow.core.raise.withError
import com.tangem.domain.tokens.GetTokenListUseCase
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.TokenList
import com.tangem.domain.tokens.repository.CurrenciesRepository
@ -18,16 +17,6 @@ internal class TokenListOperations(
private val tokens: List<CryptoCurrencyStatus>,
) {
constructor(
userWalletId: UserWalletId,
tokens: List<CryptoCurrencyStatus>,
useCase: GetTokenListUseCase,
) : this(
currenciesRepository = useCase.currenciesRepository,
userWalletId = userWalletId,
tokens = tokens,
)
fun getTokenListFlow(): Flow<Either<Error, TokenList>> {
return combine(
getIsGrouped(),

View file

@ -17,7 +17,6 @@ import com.tangem.domain.tokens.repository.MockCurrenciesRepository
import com.tangem.domain.tokens.repository.MockNetworksRepository
import com.tangem.domain.tokens.repository.MockQuotesRepository
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import junit.framework.TestCase.assertEquals
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
@ -27,7 +26,6 @@ import org.junit.Test
internal class GetTokenListUseCaseTest {
private val dispatchers = TestingCoroutineDispatcherProvider()
private val userWalletId = UserWalletId(value = null)
@Ignore
@ -308,7 +306,6 @@ internal class GetTokenListUseCaseTest {
isGrouped: Flow<Either<DataError, Boolean>> = flowOf(MockTokenLists.isGrouped.right()),
isSortedByBalance: Flow<Either<DataError, Boolean>> = flowOf(MockTokenLists.isSortedByBalance.right()),
) = GetTokenListUseCase(
dispatchers = dispatchers,
currenciesRepository = MockCurrenciesRepository(
sortTokensResult = Unit.right(),
removeCurrencyResult = Unit.right(),

View file

@ -106,13 +106,11 @@ class SwapDomainModule {
currenciesRepository: CurrenciesRepository,
quotesRepository: QuotesRepository,
networksRepository: NetworksRepository,
dispatchers: CoroutineDispatcherProvider,
): GetCardTokensListUseCase {
return GetCardTokensListUseCase(
currenciesRepository = currenciesRepository,
quotesRepository = quotesRepository,
networksRepository = networksRepository,
dispatchers = dispatchers,
)
}

View file

@ -84,7 +84,7 @@ internal class TokenDetailsLoadedBalanceConverter(
}
private fun getMarketPriceState(
status: CryptoCurrencyStatus.Status,
status: CryptoCurrencyStatus.Value,
currencySymbol: String,
): MarketPriceBlockState {
return when (status) {
@ -106,7 +106,7 @@ internal class TokenDetailsLoadedBalanceConverter(
}
}
private fun CryptoCurrencyStatus.Status.toContentConfig(currencySymbol: String): MarketPriceBlockState.Content {
private fun CryptoCurrencyStatus.Value.toContentConfig(currencySymbol: String): MarketPriceBlockState.Content {
return MarketPriceBlockState.Content(
currencySymbol = currencySymbol,
price = formatPrice(status = this, appCurrency = appCurrencyProvider()),
@ -117,11 +117,11 @@ internal class TokenDetailsLoadedBalanceConverter(
)
}
private fun getPriceChangeType(status: CryptoCurrencyStatus.Status): PriceChangeType {
private fun getPriceChangeType(status: CryptoCurrencyStatus.Value): PriceChangeType {
return PriceChangeConverter.fromBigDecimal(status.priceChange)
}
private fun formatPriceChange(status: CryptoCurrencyStatus.Status): String {
private fun formatPriceChange(status: CryptoCurrencyStatus.Value): String {
val priceChange = status.priceChange ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN
return BigDecimalFormatter.formatPercent(
@ -130,7 +130,7 @@ internal class TokenDetailsLoadedBalanceConverter(
)
}
private fun formatPrice(status: CryptoCurrencyStatus.Status, appCurrency: AppCurrency): String {
private fun formatPrice(status: CryptoCurrencyStatus.Value, appCurrency: AppCurrency): String {
val fiatRate = status.fiatRate ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN
return BigDecimalFormatter.formatFiatAmount(
@ -140,7 +140,7 @@ internal class TokenDetailsLoadedBalanceConverter(
)
}
private fun formatFiatAmount(status: CryptoCurrencyStatus.Status, appCurrency: AppCurrency): String {
private fun formatFiatAmount(status: CryptoCurrencyStatus.Value, appCurrency: AppCurrency): String {
val fiatAmount = status.fiatAmount ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN
return BigDecimalFormatter.formatFiatAmount(

View file

@ -10,7 +10,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
import com.tangem.utils.converter.Converter
internal class SingleWalletCardStateConverter(
private val status: CryptoCurrencyStatus.Status,
private val status: CryptoCurrencyStatus.Value,
private val selectedWallet: UserWallet,
private val appCurrency: AppCurrency,
) : Converter<WalletCardState, WalletCardState> {
@ -50,7 +50,7 @@ internal class SingleWalletCardStateConverter(
)
}
private fun WalletCardState.toContentState(status: CryptoCurrencyStatus.Status): WalletCardState {
private fun WalletCardState.toContentState(status: CryptoCurrencyStatus.Value): WalletCardState {
return WalletCardState.Content(
id = id,
title = title,
@ -66,7 +66,7 @@ internal class SingleWalletCardStateConverter(
)
}
private fun formatFiatAmount(status: CryptoCurrencyStatus.Status, appCurrency: AppCurrency): String {
private fun formatFiatAmount(status: CryptoCurrencyStatus.Value, appCurrency: AppCurrency): String {
val fiatAmount = status.fiatAmount ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN
return BigDecimalFormatter.formatFiatAmount(

View file

@ -10,7 +10,7 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.utils.converter.Converter
internal class SingleWalletMarketPriceConverter(
private val status: CryptoCurrencyStatus.Status,
private val status: CryptoCurrencyStatus.Value,
private val appCurrency: AppCurrency,
) : Converter<MarketPriceBlockState, MarketPriceBlockState> {
@ -44,7 +44,7 @@ internal class SingleWalletMarketPriceConverter(
)
}
private fun formatPrice(status: CryptoCurrencyStatus.Status, appCurrency: AppCurrency): String {
private fun formatPrice(status: CryptoCurrencyStatus.Value, appCurrency: AppCurrency): String {
val fiatRate = status.fiatRate ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN
return BigDecimalFormatter.formatFiatAmount(
@ -54,13 +54,13 @@ internal class SingleWalletMarketPriceConverter(
)
}
private fun formatPriceChange(status: CryptoCurrencyStatus.Status): String {
private fun formatPriceChange(status: CryptoCurrencyStatus.Value): String {
val priceChange = status.priceChange ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN
return BigDecimalFormatter.formatPercent(percent = priceChange, useAbsoluteValue = true)
}
private fun getPriceChangeType(status: CryptoCurrencyStatus.Status): PriceChangeType {
private fun getPriceChangeType(status: CryptoCurrencyStatus.Value): PriceChangeType {
return PriceChangeConverter.fromBigDecimal(status.priceChange)
}
}

View file

@ -121,7 +121,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
private fun sendToken(
cryptoCurrency: CryptoCurrency.Token,
cryptoCurrencyStatus: CryptoCurrencyStatus.Status,
cryptoCurrencyStatus: CryptoCurrencyStatus.Value,
feeCurrencyStatus: CryptoCurrencyStatus?,
userWallet: UserWallet,
) {

View file

@ -68,7 +68,7 @@ xmlShimmer = "1.1.3"
zxingQrCode = "3.5.1"
mviCore = "1.3.1"
kotlinSerialization = "1.4.1"
arrow = "1.2.0"
arrow = "1.2.3"
reactiveNetwork = "3.0.8"
walletConnectCore = "1.18.0"
walletConnectWeb3 = "1.11.0"