diff --git a/app/src/main/java/com/tangem/tap/domain/model/TotalFiatBalance.kt b/app/src/main/java/com/tangem/tap/domain/model/TotalFiatBalance.kt index 9fa5b24fd8..0180d85435 100644 --- a/app/src/main/java/com/tangem/tap/domain/model/TotalFiatBalance.kt +++ b/app/src/main/java/com/tangem/tap/domain/model/TotalFiatBalance.kt @@ -13,11 +13,12 @@ sealed interface TotalFiatBalance { override val amount: BigDecimal? = null } - data class Error( - override val amount: BigDecimal?, - ) : TotalFiatBalance + object Failed : TotalFiatBalance { + override val amount: BigDecimal? = null + } data class Loaded( override val amount: BigDecimal, + val isWarning: Boolean, ) : TotalFiatBalance } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/totalBalance/implementation/DefaultTotalFiatBalanceCalculator.kt b/app/src/main/java/com/tangem/tap/domain/totalBalance/implementation/DefaultTotalFiatBalanceCalculator.kt index fd5bf29aa9..330561f5af 100644 --- a/app/src/main/java/com/tangem/tap/domain/totalBalance/implementation/DefaultTotalFiatBalanceCalculator.kt +++ b/app/src/main/java/com/tangem/tap/domain/totalBalance/implementation/DefaultTotalFiatBalanceCalculator.kt @@ -22,13 +22,15 @@ internal class DefaultTotalFiatBalanceCalculator : TotalFiatBalanceCalculator { val walletsData = walletStores .asSequence() .flatMap { it.walletsData } - val calculateAmount = { walletsData.calculateTotalFiatAmount() } - when (walletsData.findStatus()) { + when (val status = walletsData.findStatus()) { TotalFiatBalanceStatus.Loading -> TotalFiatBalance.Loading - TotalFiatBalanceStatus.Error -> TotalFiatBalance.Error(calculateAmount()) - TotalFiatBalanceStatus.Loaded -> TotalFiatBalance.Loaded( - amount = calculateAmount() ?: BigDecimal.ZERO, + TotalFiatBalanceStatus.Failed -> TotalFiatBalance.Failed + TotalFiatBalanceStatus.Warning, + TotalFiatBalanceStatus.Loaded, + -> TotalFiatBalance.Loaded( + amount = walletsData.calculateTotalFiatAmount(), + isWarning = status == TotalFiatBalanceStatus.Warning, ) } } @@ -50,29 +52,30 @@ internal class DefaultTotalFiatBalanceCalculator : TotalFiatBalanceCalculator { is WalletDataModel.SameCurrencyTransactionInProgress, is WalletDataModel.TransactionInProgress, is WalletDataModel.NoAccount, - -> if (walletData.isCustom && walletData.fiatRate == null) { - TotalFiatBalanceStatus.Error + -> if (walletData.isWarningCase()) { + TotalFiatBalanceStatus.Warning } else { TotalFiatBalanceStatus.Loaded } is WalletDataModel.Unreachable, is WalletDataModel.MissedDerivation, - -> TotalFiatBalanceStatus.Error + -> TotalFiatBalanceStatus.Failed is WalletDataModel.Loading -> TotalFiatBalanceStatus.Loading } } } - private fun Sequence.calculateTotalFiatAmount(): BigDecimal? { + private fun Sequence.calculateTotalFiatAmount(): BigDecimal { return this - .filterNot { it.isCustom && it.fiatRate == null } + .filterNot { it.isWarningCase() } .map { walletData -> walletData.fiatRate ?.takeUnless { walletData.status.isErrorStatus } ?.let { walletData.status.amount.toFiatValue(it) } + ?: BigDecimal.ZERO } .reduce { acc, value -> - value?.let { acc?.plus(it) } + acc + value } } @@ -80,20 +83,22 @@ internal class DefaultTotalFiatBalanceCalculator : TotalFiatBalanceCalculator { prevStatus: TotalFiatBalanceStatus, newStatus: TotalFiatBalanceStatus, ): TotalFiatBalanceStatus { - return when (prevStatus) { - TotalFiatBalanceStatus.Loading -> prevStatus - TotalFiatBalanceStatus.Loaded, - TotalFiatBalanceStatus.Error, - -> when (newStatus) { - TotalFiatBalanceStatus.Loading, - TotalFiatBalanceStatus.Error, - -> newStatus - TotalFiatBalanceStatus.Loaded -> prevStatus - } - } + return TotalFiatBalanceStatus[minOf(prevStatus.ordinal, newStatus.ordinal)] } + private fun WalletDataModel.isWarningCase(): Boolean = isCustom && fiatRate == null + private enum class TotalFiatBalanceStatus { - Loading, Error, Loaded, + Loading, + Failed, + Warning, + Loaded, + ; + + companion object { + private val allValues = values() + + operator fun get(index: Int) = allValues[index] + } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/analytics/WalletAnalyticsEventsMapper.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/analytics/WalletAnalyticsEventsMapper.kt index 277ce8471c..184e85983c 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/analytics/WalletAnalyticsEventsMapper.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/analytics/WalletAnalyticsEventsMapper.kt @@ -10,21 +10,11 @@ class WalletAnalyticsEventsMapper : Converter { - if (value.amount == null) { - // if fiatAmount is null while ProgressState.Error it means error occurs when loading blockchain - AnalyticsParam.CardBalanceState.BlockchainError - } else { - // if fiatAmount is not null while ProgressState.Error it means couldn't load custom token amount - AnalyticsParam.CardBalanceState.CustomToken - } - } - is TotalFiatBalance.Loaded -> { - if (value.amount.isGreaterThan(BigDecimal.ZERO)) { - AnalyticsParam.CardBalanceState.Full - } else { - AnalyticsParam.CardBalanceState.Empty - } + is TotalFiatBalance.Failed -> AnalyticsParam.CardBalanceState.BlockchainError + is TotalFiatBalance.Loaded -> when { + value.isWarning -> AnalyticsParam.CardBalanceState.CustomToken + value.amount.isGreaterThan(BigDecimal.ZERO) -> AnalyticsParam.CardBalanceState.Full + else -> AnalyticsParam.CardBalanceState.Empty } is TotalFiatBalance.Loading -> null } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/view/TotalBalanceCard.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/view/TotalBalanceCard.kt index 4eab1313a5..605053c7e4 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/view/TotalBalanceCard.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/view/TotalBalanceCard.kt @@ -86,8 +86,7 @@ internal class TotalBalanceCard @JvmOverloads constructor( private fun updateState(status: TotalFiatBalance?, fiatCurrency: FiatCurrency, onChangeCurrencyClick: () -> Unit) { state = when (status) { null -> TotalBalanceCardState.Empty - is TotalFiatBalance.Error -> TotalBalanceCardState.Failure( - amount = status.amount, + is TotalFiatBalance.Failed -> TotalBalanceCardState.Failure( fiatCurrency = fiatCurrency, onChangeFiatCurrencyClick = onChangeCurrencyClick, ) @@ -97,8 +96,9 @@ internal class TotalBalanceCard @JvmOverloads constructor( ) is TotalFiatBalance.Loaded -> TotalBalanceCardState.Success( amount = status.amount, - fiatCurrency = fiatCurrency, + showWarning = status.isWarning, onChangeFiatCurrencyClick = onChangeCurrencyClick, + fiatCurrency = fiatCurrency, ) } } @@ -138,8 +138,8 @@ private fun TotalBalanceCardContent(state: TotalBalanceCardState, modifier: Modi ) } }, - failureText = { - AnimatedVisibility(visible = state is TotalBalanceCardState.Failure) { + warningText = { + AnimatedVisibility(visible = state.showWarning) { Text( modifier = Modifier.fillMaxWidth(), text = stringResource(id = R.string.main_processing_full_amount), @@ -156,7 +156,7 @@ private fun TotalBalanceCardScaffold( title: @Composable () -> Unit, amount: @Composable () -> Unit, currencySelector: @Composable () -> Unit, - failureText: @Composable () -> Unit, + warningText: @Composable () -> Unit, modifier: Modifier = Modifier, amountWeight: Float = 0.8f, ) { @@ -192,7 +192,7 @@ private fun TotalBalanceCardScaffold( horizontal = TangemTheme.dimens.spacing16, ), ) { - failureText() + warningText() } SpacerH12() } @@ -252,11 +252,13 @@ private fun buildAmountString(amount: BigDecimal?, fiatCurrencySymbol: String): private sealed interface TotalBalanceCardState { val amount: BigDecimal? - val onChangeFiatCurrencyClick: () -> Unit + val showWarning: Boolean val fiatCurrency: FiatCurrency + val onChangeFiatCurrencyClick: () -> Unit object Empty : TotalBalanceCardState { override val amount: BigDecimal? = null + override val showWarning: Boolean = false override val fiatCurrency: FiatCurrency = FiatCurrency.Default override val onChangeFiatCurrencyClick: () -> Unit = { /* no-op */ } } @@ -266,16 +268,20 @@ private sealed interface TotalBalanceCardState { override val onChangeFiatCurrencyClick: () -> Unit, ) : TotalBalanceCardState { override val amount: BigDecimal = BigDecimal.ZERO + override val showWarning: Boolean = false } data class Failure( - override val amount: BigDecimal?, override val fiatCurrency: FiatCurrency, override val onChangeFiatCurrencyClick: () -> Unit, - ) : TotalBalanceCardState + ) : TotalBalanceCardState { + override val amount: BigDecimal? = null + override val showWarning: Boolean = true + } data class Success( override val amount: BigDecimal, + override val showWarning: Boolean, override val fiatCurrency: FiatCurrency, override val onChangeFiatCurrencyClick: () -> Unit, ) : TotalBalanceCardState @@ -297,7 +303,6 @@ private fun TotalBalanceCardContentSample(modifier: Modifier = Modifier) { Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) TotalBalanceCardContent( state = TotalBalanceCardState.Failure( - amount = BigDecimal("9917.72"), onChangeFiatCurrencyClick = {}, fiatCurrency = FiatCurrency("USD", "USD", "$"), ), @@ -306,6 +311,7 @@ private fun TotalBalanceCardContentSample(modifier: Modifier = Modifier) { TotalBalanceCardContent( state = TotalBalanceCardState.Success( amount = BigDecimal("9917.72"), + showWarning = false, onChangeFiatCurrencyClick = {}, fiatCurrency = FiatCurrency("USD", "USD", "$"), ), diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorMiddleware.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorMiddleware.kt index 9191b57a00..aadd56356b 100644 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorMiddleware.kt @@ -108,7 +108,7 @@ internal class WalletSelectorMiddleware { ) { if (updatedWalletStores.isNotEmpty()) { scope.launch(Dispatchers.Default) { - val updatedWallets = state.wallets.updateWalletStoresAndCalculateFiatBalance(updatedWalletStores) + val updatedWallets = state.wallets.calculateBalanceAndUpdateWalletStores(updatedWalletStores) if (updatedWallets != state.wallets) { store.dispatchOnMain(WalletSelectorAction.BalancesLoaded(updatedWallets)) } @@ -362,17 +362,17 @@ internal class WalletSelectorMiddleware { } } - private suspend fun List.updateWalletStoresAndCalculateFiatBalance( + private suspend fun List.calculateBalanceAndUpdateWalletStores( walletStores: Map>, ): List { return this .associateWith { walletStores[it.id] } .map { (wallet, walletStores) -> - wallet.updateWalletStoresAndCalculateFiatBalance(walletStores) + wallet.calculateBalanceAndUpdateWalletStores(walletStores) } } - private suspend fun UserWalletModel.updateWalletStoresAndCalculateFiatBalance( + private suspend fun UserWalletModel.calculateBalanceAndUpdateWalletStores( walletStores: List?, ): UserWalletModel { return this.copy( diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/Mapper.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/Mapper.kt index 400c4e026e..0e4319a4dc 100644 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/Mapper.kt +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/Mapper.kt @@ -3,7 +3,6 @@ package com.tangem.tap.features.walletSelector.ui import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.common.extensions.toFormattedFiatValue import com.tangem.tap.domain.model.TotalFiatBalance -import com.tangem.tap.features.wallet.redux.utils.UNKNOWN_AMOUNT_SIGN import com.tangem.tap.features.walletSelector.redux.UserWalletModel import com.tangem.tap.features.walletSelector.ui.model.MultiCurrencyUserWalletItem import com.tangem.tap.features.walletSelector.ui.model.SingleCurrencyUserWalletItem @@ -13,13 +12,12 @@ internal fun List.toUiModels(appCurrency: FiatCurrency): Sequen return this.asSequence().map { userWalletModel -> with(userWalletModel) { val balance = when (fiatBalance) { - is TotalFiatBalance.Error -> UserWalletItem.Balance.Error( - amount = fiatBalance.amount?.toFormattedFiatValue(appCurrency.symbol) ?: UNKNOWN_AMOUNT_SIGN, - ) + is TotalFiatBalance.Failed -> UserWalletItem.Balance.Failed + is TotalFiatBalance.Loading -> UserWalletItem.Balance.Loading is TotalFiatBalance.Loaded -> UserWalletItem.Balance.Loaded( amount = fiatBalance.amount.toFormattedFiatValue(appCurrency.symbol), + showWarning = fiatBalance.isWarning, ) - is TotalFiatBalance.Loading -> UserWalletItem.Balance.Loading } when (type) { is UserWalletModel.Type.MultiCurrency -> MultiCurrencyUserWalletItem( diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/MockData.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/MockData.kt index db09868d03..c404c8890a 100644 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/MockData.kt +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/MockData.kt @@ -11,6 +11,7 @@ internal object MockData { id = UserWalletId("wallet_1"), balance = UserWalletItem.Balance.Loaded( amount = "6781.05 $", + showWarning = true, ), name = "Wallet", imageUrl = "https://app.tangem.com/cards/card_default.png", @@ -23,6 +24,7 @@ internal object MockData { id = UserWalletId("wallet_4"), balance = UserWalletItem.Balance.Loaded( amount = "6781.05 $", + showWarning = false, ), name = "Wallet", imageUrl = "https://app.tangem.com/cards/card_default.png", diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletItem.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletItem.kt index 70c46cb0ce..40f87ab080 100644 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletItem.kt +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletItem.kt @@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.Divider import androidx.compose.material.Icon import androidx.compose.material.Text import androidx.compose.runtime.Composable @@ -178,18 +179,13 @@ private fun RowScope.TokensInfo(isLocked: Boolean, balance: UserWalletItem.Balan LockedPlaceholder() } else { when (balance) { - is UserWalletItem.Balance.Error -> { + is UserWalletItem.Balance.Failed, + is UserWalletItem.Balance.Loaded, + -> { LoadedTokensInfo( balanceAmount = balance.amount, tokensCount = tokensCount, - showWarning = true, - ) - } - is UserWalletItem.Balance.Loaded -> { - LoadedTokensInfo( - balanceAmount = balance.amount, - tokensCount = tokensCount, - showWarning = false, + showWarning = balance.showWarning, ) } is UserWalletItem.Balance.Loading -> { @@ -360,16 +356,23 @@ private fun CheckedWalletMark(modifier: Modifier = Modifier) { } } -@Preview(widthDp = 360, heightDp = 72, showBackground = true) +// region Preview @Composable -private fun PreviewWalletItem() { - TangemTheme { +@Suppress("LongMethod") // preview +private fun WalletItemSample(modifier: Modifier = Modifier) { + Column( + modifier = modifier + .background(TangemTheme.colors.background.primary), + ) { WalletItem( wallet = MultiCurrencyUserWalletItem( id = UserWalletId(value = null), name = "Tangem Card", imageUrl = "", - balance = UserWalletItem.Balance.Loaded("141212121888 BTC"), + balance = UserWalletItem.Balance.Loaded( + amount = "1412121218 BTC", + showWarning = false, + ), isLocked = false, tokensCount = 2, cardsInWallet = 1, @@ -379,5 +382,91 @@ private fun PreviewWalletItem() { onWalletClick = {}, onWalletLongClick = {}, ) + Divider(modifier = Modifier.padding(vertical = 4.dp)) + WalletItem( + wallet = MultiCurrencyUserWalletItem( + id = UserWalletId(value = null), + name = "Tangem Card", + imageUrl = "", + balance = UserWalletItem.Balance.Loaded( + amount = "1412121218 BTC", + showWarning = true, + ), + isLocked = false, + tokensCount = 2, + cardsInWallet = 1, + ), + isSelected = false, + isChecked = false, + onWalletClick = {}, + onWalletLongClick = {}, + ) + Divider(modifier = Modifier.padding(vertical = 4.dp)) + WalletItem( + wallet = MultiCurrencyUserWalletItem( + id = UserWalletId(value = null), + name = "Tangem Card", + imageUrl = "", + balance = UserWalletItem.Balance.Loading, + isLocked = true, + tokensCount = 2, + cardsInWallet = 1, + ), + isSelected = false, + isChecked = false, + onWalletClick = {}, + onWalletLongClick = {}, + ) + Divider(modifier = Modifier.padding(vertical = 4.dp)) + WalletItem( + wallet = MultiCurrencyUserWalletItem( + id = UserWalletId(value = null), + name = "Tangem Card", + imageUrl = "", + balance = UserWalletItem.Balance.Failed, + isLocked = false, + tokensCount = 2, + cardsInWallet = 1, + ), + isSelected = true, + isChecked = false, + onWalletClick = {}, + onWalletLongClick = {}, + ) + Divider(modifier = Modifier.padding(vertical = 4.dp)) + WalletItem( + wallet = SingleCurrencyUserWalletItem( + id = UserWalletId(value = null), + name = "Tangem Card", + imageUrl = "", + balance = UserWalletItem.Balance.Loaded( + amount = "141212121888 BTC", + showWarning = false, + ), + isLocked = false, + tokenName = "Bitcoin", + ), + isSelected = false, + isChecked = true, + onWalletClick = {}, + onWalletLongClick = {}, + ) } -} \ No newline at end of file +} + +@Preview(showBackground = true, widthDp = 360) +@Composable +private fun WalletItemPreview_Light() { + TangemTheme { + WalletItemSample() + } +} + +@Preview(showBackground = true, widthDp = 360) +@Composable +private fun WalletItemPreview_Dark() { + TangemTheme(isDark = true) { + WalletItemSample() + } +} +// endregion Preview \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/UserWalletItem.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/UserWalletItem.kt index eeca9d7611..7013362085 100644 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/UserWalletItem.kt +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/UserWalletItem.kt @@ -1,6 +1,7 @@ package com.tangem.tap.features.walletSelector.ui.model import com.tangem.domain.common.util.UserWalletId +import com.tangem.tap.features.wallet.redux.utils.UNKNOWN_AMOUNT_SIGN internal sealed interface UserWalletItem { val id: UserWalletId @@ -9,12 +10,20 @@ internal sealed interface UserWalletItem { val balance: Balance val isLocked: Boolean - sealed interface Balance { - object Loading : Balance + sealed class Balance { + open val amount: String = UNKNOWN_AMOUNT_SIGN + open val showWarning: Boolean = false - data class Error(val amount: String) : Balance + object Loading : Balance() - data class Loaded(val amount: String) : Balance + object Failed : Balance() { + override val showWarning: Boolean = true + } + + data class Loaded( + override val amount: String, + override val showWarning: Boolean, + ) : Balance() } }