Updated on 2026-08-14

This commit is contained in:
Tangem 2024-06-03 17:26:18 +05:00
commit c9526b3cc8
6 changed files with 94 additions and 84 deletions

View file

@ -40,25 +40,4 @@ fun BigDecimal.toFormattedCurrencyString(
)
val formattedCurrency = currency?.let { " $it" } ?: ""
return "$formattedAmount$formattedCurrency"
}
fun BigDecimal.toFiatString(
rateValue: BigDecimal,
fiatCurrencyName: String,
formatWithSpaces: Boolean = false,
): String {
val fiatValue = rateValue.multiply(this)
val formatter = NumberFormat.getInstance(Locale.getDefault()) as? DecimalFormat
val df = formatter?.apply {
maximumFractionDigits = 2
minimumFractionDigits = 2
isGroupingUsed = true
this.roundingMode = roundingMode
}
val formatted = if (formatWithSpaces) {
"${df?.format(fiatValue)} $fiatCurrencyName"
} else {
"${df?.format(fiatValue)}$fiatCurrencyName"
}
return formatted
}

View file

@ -22,6 +22,7 @@ class GetCryptoCurrencyStatusSyncUseCase(
suspend operator fun invoke(
userWalletId: UserWalletId,
cryptoCurrencyId: CryptoCurrency.ID,
isSingleWalletWithTokens: Boolean = false,
): Either<CurrencyStatusError, CryptoCurrencyStatus> {
val operations = CurrenciesStatusesOperations(
userWalletId = userWalletId,
@ -30,7 +31,7 @@ class GetCryptoCurrencyStatusSyncUseCase(
networksRepository = networksRepository,
)
return operations.getCurrencyStatusSync(cryptoCurrencyId)
return operations.getCurrencyStatusSync(cryptoCurrencyId, isSingleWalletWithTokens)
.mapLeft { error -> error.mapToCurrencyError() }
}

View file

@ -39,6 +39,26 @@ class GetNetworkCoinStatusUseCase(
.flowOn(dispatchers.io)
}
suspend fun invokeSync(
userWalletId: UserWalletId,
networkId: Network.ID,
derivationPath: Network.DerivationPath,
isSingleWalletWithTokens: Boolean,
): Either<CurrencyStatusError, CryptoCurrencyStatus> {
val operations = CurrenciesStatusesOperations(
currenciesRepository = currenciesRepository,
quotesRepository = quotesRepository,
networksRepository = networksRepository,
userWalletId = userWalletId,
)
val maybeCurrency = if (isSingleWalletWithTokens) {
operations.getNetworkCoinForSingleWalletWithTokenSync(networkId)
} else {
operations.getNetworkCoinSync(networkId, derivationPath)
}
return maybeCurrency.mapLeft(CurrenciesStatusesOperations.Error::mapToCurrencyError)
}
private suspend fun getCurrency(
userWalletId: UserWalletId,
networkId: Network.ID,

View file

@ -77,12 +77,18 @@ internal class CurrenciesStatusesOperations(
}
}
suspend fun getCurrencyStatusSync(cryptoCurrencyId: CryptoCurrency.ID): Either<Error, CryptoCurrencyStatus> {
suspend fun getCurrencyStatusSync(
cryptoCurrencyId: CryptoCurrency.ID,
isSingleWalletWithTokens: Boolean = false,
): Either<Error, CryptoCurrencyStatus> {
return either {
catch(
block = {
val currency =
val currency = if (isSingleWalletWithTokens) {
currenciesRepository.getSingleCurrencyWalletWithCardCurrency(userWalletId, cryptoCurrencyId)
} else {
currenciesRepository.getMultiCurrencyWalletCurrency(userWalletId, cryptoCurrencyId)
}
val quotes = quotesRepository.getQuoteSync(cryptoCurrencyId).right()
val networkStatuses =
networksRepository.getNetworkStatusesSync(
@ -111,6 +117,14 @@ internal class CurrenciesStatusesOperations(
return getCurrencyStatusSync(currency.id)
}
suspend fun getNetworkCoinForSingleWalletWithTokenSync(
networkId: Network.ID,
): Either<Error, CryptoCurrencyStatus> = either {
val currency = getNetworkCoinForSingleWalletWithToken(networkId)
return getCurrencyStatusSync(currency.id)
}
suspend fun getPrimaryCurrencyStatusSync(): Either<Error, CryptoCurrencyStatus> = either {
val currency = catch(
block = { currenciesRepository.getSingleCurrencyWalletPrimaryCurrency(userWalletId) },

View file

@ -70,11 +70,10 @@ import kotlin.properties.Delegates
internal class SendViewModel @Inject constructor(
private val dispatchers: CoroutineDispatcherProvider,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val getCurrencyStatusUpdatesUseCase: GetCurrencyStatusUpdatesUseCase,
private val getCryptoCurrencyStatusSyncUseCase: GetCryptoCurrencyStatusSyncUseCase,
private val getNetworkCoinStatusUseCase: GetNetworkCoinStatusUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getWalletsUseCase: GetWalletsUseCase,
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
private val getCryptoCurrencyUseCase: GetCryptoCurrencyUseCase,
private val getNetworkAddressesUseCase: GetNetworkAddressesUseCase,
@ -263,9 +262,7 @@ internal class SendViewModel @Inject constructor(
)
},
ifLeft = {
uiState = eventStateFactory.getGenericErrorState(
onConsume = { uiState = eventStateFactory.onConsumeEventState() },
)
showErrorAlert()
return@launch
},
)
@ -283,45 +280,33 @@ internal class SendViewModel @Inject constructor(
.saveIn(balanceHidingJobHolder)
}
// TODO [REDACTED_JIRA]
private fun getCurrenciesStatusUpdates(isSingleWalletWithToken: Boolean, isMultiCurrency: Boolean) {
if (cryptoCurrency is CryptoCurrency.Coin) {
getCurrencyStatusUpdates(
isSingleWalletWithToken = isSingleWalletWithToken,
isMultiCurrency = isMultiCurrency,
).onEach { currencyStatus ->
currencyStatus.onRight {
onDataLoaded(
currencyStatus = it,
coinCurrencyStatus = it,
feeCurrencyStatus = getFeeCurrencyStatusSync(it, isMultiCurrency),
)
}
}
.flowOn(dispatchers.main)
.launchIn(viewModelScope)
.saveIn(balanceJobHolder)
private suspend fun getCurrenciesStatusUpdates(isSingleWalletWithToken: Boolean, isMultiCurrency: Boolean) {
val maybeCurrencyStatus = getCurrencyStatus(
isSingleWalletWithToken = isSingleWalletWithToken,
isMultiCurrency = isMultiCurrency,
)
val maybeCoinStatus = if (cryptoCurrency is CryptoCurrency.Coin) {
maybeCurrencyStatus
} else {
combine(
flow = getCoinCurrencyStatusUpdates(isSingleWalletWithToken),
flow2 = getCurrencyStatusUpdates(
isSingleWalletWithToken = isSingleWalletWithToken,
isMultiCurrency = isMultiCurrency,
),
) { maybeCoinStatus, maybeCurrencyStatus ->
if (maybeCoinStatus.isRight() && maybeCurrencyStatus.isRight()) {
val currencyStatus = maybeCurrencyStatus.getOrElse { error("Currency status is unreachable") }
val coinStatus = maybeCoinStatus.getOrElse { error("Coin status is unreachable") }
onDataLoaded(
currencyStatus = currencyStatus,
coinCurrencyStatus = coinStatus,
feeCurrencyStatus = getFeeCurrencyStatusSync(currencyStatus, isMultiCurrency),
)
}
getCoinCurrencyStatusUpdates(isSingleWalletWithToken)
}
if (maybeCoinStatus.isRight() && maybeCurrencyStatus.isRight()) {
val currencyStatus = maybeCurrencyStatus.getOrElse {
showErrorAlert()
return Timber.e("Currency status is unreachable")
}
.flowOn(dispatchers.main)
.launchIn(viewModelScope)
.saveIn(balanceJobHolder)
val coinStatus = maybeCoinStatus.getOrElse {
showErrorAlert()
return Timber.e("Coin status is unreachable")
}
onDataLoaded(
currencyStatus = currencyStatus,
coinCurrencyStatus = coinStatus,
feeCurrencyStatus = getFeeCurrencyStatusSync(currencyStatus, isMultiCurrency),
)
} else {
showErrorAlert()
}
}
@ -331,25 +316,26 @@ internal class SendViewModel @Inject constructor(
}
}
private fun getCoinCurrencyStatusUpdates(isSingleWalletWithToken: Boolean) = getNetworkCoinStatusUseCase(
userWalletId = userWalletId,
networkId = cryptoCurrency.network.id,
derivationPath = cryptoCurrency.network.derivationPath,
isSingleWalletWithTokens = isSingleWalletWithToken,
).conflate().distinctUntilChanged()
private suspend fun getCoinCurrencyStatusUpdates(isSingleWalletWithToken: Boolean) = getNetworkCoinStatusUseCase
.invokeSync(
userWalletId = userWalletId,
networkId = cryptoCurrency.network.id,
derivationPath = cryptoCurrency.network.derivationPath,
isSingleWalletWithTokens = isSingleWalletWithToken,
)
private fun getCurrencyStatusUpdates(
private suspend fun getCurrencyStatus(
isSingleWalletWithToken: Boolean,
isMultiCurrency: Boolean,
): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
): Either<CurrencyStatusError, CryptoCurrencyStatus> {
return if (isMultiCurrency) {
getCurrencyStatusUpdatesUseCase(
getCryptoCurrencyStatusSyncUseCase(
userWalletId = userWalletId,
currencyId = cryptoCurrency.id,
cryptoCurrencyId = cryptoCurrency.id,
isSingleWalletWithTokens = isSingleWalletWithToken,
).conflate().distinctUntilChanged()
)
} else {
getPrimaryCurrencyStatusUpdatesUseCase(userWalletId = userWalletId)
getCryptoCurrencyStatusSyncUseCase(userWalletId = userWalletId)
}
}
@ -986,6 +972,12 @@ internal class SendViewModel @Inject constructor(
}
uiState = stateFactory.getHiddenTapHelpState()
}
private fun showErrorAlert() {
uiState = eventStateFactory.getGenericErrorState(
onConsume = { uiState = eventStateFactory.onConsumeEventState() },
)
}
// endregion
private companion object {

View file

@ -9,6 +9,7 @@ import com.tangem.blockchain.common.BlockchainSdkError
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.blockchainsdk.utils.minimalAmount
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.extenstions.unwrap
@ -46,7 +47,6 @@ import com.tangem.lib.crypto.UserWalletManager
import com.tangem.lib.crypto.models.*
import com.tangem.lib.crypto.models.transactions.SendTxResult
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.toFiatString
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.firstOrNull
import timber.log.Timber
@ -905,10 +905,10 @@ internal class SwapInteractorImpl @Inject constructor(
private suspend fun createEmptyAmountState(): SwapState {
val appCurrency = getSelectedAppCurrencyUseCase.unwrap()
return SwapState.EmptyAmountState(
zeroAmountEquivalent = BigDecimal.ZERO.toFiatString(
rateValue = BigDecimal.ONE,
fiatCurrencyName = appCurrency.symbol,
formatWithSpaces = true,
zeroAmountEquivalent = BigDecimalFormatter.formatFiatAmount(
fiatAmount = BigDecimal.ZERO,
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
),
)
}
@ -1155,7 +1155,11 @@ internal class SwapInteractorImpl @Inject constructor(
val rates = getQuotes(feeCurrencyId)
return rates[feeCurrencyId]?.fiatRate?.let { rate ->
fees.map { fee ->
fee.toFiatString(rate, appCurrency.symbol, true)
BigDecimalFormatter.formatFiatAmount(
fiatAmount = rate.multiply(fee),
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
)
}
}.orEmpty()
}