diff --git a/core/utils/src/main/java/com/tangem/utils/CryptoCurrencyFormatExtensions.kt b/core/utils/src/main/java/com/tangem/utils/CryptoCurrencyFormatExtensions.kt index a2533147f7..ce1a6d2470 100644 --- a/core/utils/src/main/java/com/tangem/utils/CryptoCurrencyFormatExtensions.kt +++ b/core/utils/src/main/java/com/tangem/utils/CryptoCurrencyFormatExtensions.kt @@ -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 } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyStatusSyncUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyStatusSyncUseCase.kt index ed3cbfad6c..8150cfc734 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyStatusSyncUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyStatusSyncUseCase.kt @@ -22,6 +22,7 @@ class GetCryptoCurrencyStatusSyncUseCase( suspend operator fun invoke( userWalletId: UserWalletId, cryptoCurrencyId: CryptoCurrency.ID, + isSingleWalletWithTokens: Boolean = false, ): Either { 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() } } diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkCoinStatusUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkCoinStatusUseCase.kt index f33b882c8e..8b03aef79d 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkCoinStatusUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkCoinStatusUseCase.kt @@ -39,6 +39,26 @@ class GetNetworkCoinStatusUseCase( .flowOn(dispatchers.io) } + suspend fun invokeSync( + userWalletId: UserWalletId, + networkId: Network.ID, + derivationPath: Network.DerivationPath, + isSingleWalletWithTokens: Boolean, + ): Either { + 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, diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt index 4ae1a1f01c..de1768de08 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt @@ -77,12 +77,18 @@ internal class CurrenciesStatusesOperations( } } - suspend fun getCurrencyStatusSync(cryptoCurrencyId: CryptoCurrency.ID): Either { + suspend fun getCurrencyStatusSync( + cryptoCurrencyId: CryptoCurrency.ID, + isSingleWalletWithTokens: Boolean = false, + ): Either { 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 = either { + val currency = getNetworkCoinForSingleWalletWithToken(networkId) + + return getCurrencyStatusSync(currency.id) + } + suspend fun getPrimaryCurrencyStatusSync(): Either = either { val currency = catch( block = { currenciesRepository.getSingleCurrencyWalletPrimaryCurrency(userWalletId) }, diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt index bcf4954db8..f580eaa140 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt @@ -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 { 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 { diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index ab1e044b30..9babfe5a73 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -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() }