Updated on 2026-08-14

This commit is contained in:
Tangem 2023-04-13 11:54:53 +03:00
commit 2c593eae50
31 changed files with 465 additions and 458 deletions

View file

@ -9,6 +9,7 @@ import com.tangem.domain.common.util.UserWalletId
import com.tangem.tap.common.analytics.converters.TopUpEventConverter import com.tangem.tap.common.analytics.converters.TopUpEventConverter
import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.extensions.copy import com.tangem.tap.common.extensions.copy
import com.tangem.tap.domain.model.TotalFiatBalance
import com.tangem.tap.domain.model.UserWallet import com.tangem.tap.domain.model.UserWallet
import com.tangem.tap.domain.model.WalletDataModel import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.WalletStoreModel import com.tangem.tap.domain.model.WalletStoreModel
@ -17,7 +18,6 @@ import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.domain.walletCurrencies.WalletCurrenciesManager import com.tangem.tap.domain.walletCurrencies.WalletCurrenciesManager
import com.tangem.tap.domain.walletStores.WalletStoresManager import com.tangem.tap.domain.walletStores.WalletStoresManager
import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.persistence.ToppedUpWalletStorage import com.tangem.tap.persistence.ToppedUpWalletStorage
import com.tangem.tap.scope import com.tangem.tap.scope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -60,8 +60,8 @@ class TopUpController(
hadMissedDerivations = blockchains.isNotEmpty() hadMissedDerivations = blockchains.isNotEmpty()
} }
fun totalBalanceStateChanged(state: ProgressState) { fun totalBalanceStateChanged(totalFiatBalance: TotalFiatBalance) {
if (state == ProgressState.Done) tryToSend() if (totalFiatBalance is TotalFiatBalance.Loaded) tryToSend()
} }
fun loadDataSuccess() { fun loadDataSuccess() {

View file

@ -12,9 +12,8 @@ import com.tangem.tap.common.TestActions
import com.tangem.tap.domain.TapError import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.extensions.amountToCreateAccount import com.tangem.tap.domain.extensions.amountToCreateAccount
import com.tangem.tap.domain.getFirstToken import com.tangem.tap.domain.getFirstToken
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.features.demo.isDemoCard import com.tangem.tap.features.demo.isDemoCard
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.redux.AddressData
import com.tangem.tap.features.wallet.redux.reducers.createAddressesData import com.tangem.tap.features.wallet.redux.reducers.createAddressesData
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.proxy.redux.DaggerGraphState
@ -81,7 +80,7 @@ fun WalletManager.getTopUpUrl(): String? {
) )
} }
fun WalletManager?.getAddressData(): AddressData? { fun WalletManager?.getAddressData(): WalletDataModel.AddressData? {
val wallet = this?.wallet ?: return null val wallet = this?.wallet ?: return null
val addressDataList = wallet.createAddressesData() val addressDataList = wallet.createAddressesData()
@ -100,11 +99,6 @@ fun <T> WalletManager.Companion.stub(): T {
} as T } as T
} }
fun Wallet.getTxHistory(currency: Currency): List<TransactionData> {
return (currency as? Currency.Token)?.let { this.getTokenTxHistory(it.token) }
?: getBlockchainTxHistory()
}
fun Wallet.getBlockchainTxHistory(): List<TransactionData> { fun Wallet.getBlockchainTxHistory(): List<TransactionData> {
return historyTransactions.filter { return historyTransactions.filter {
it.contractAddress.isNullOrEmpty() it.contractAddress.isNullOrEmpty()

View file

@ -2,8 +2,8 @@ package com.tangem.tap.common.redux
import com.tangem.common.extensions.VoidCallback import com.tangem.common.extensions.VoidCallback
import com.tangem.tap.common.TestAction import com.tangem.tap.common.TestAction
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.redux.AddressData
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
@ -24,7 +24,7 @@ sealed class AppDialog : StateDialog {
data class AddressInfoDialog( data class AddressInfoDialog(
val currency: Currency, val currency: Currency,
val addressData: AddressData, val addressData: WalletDataModel.AddressData,
) : AppDialog() ) : AppDialog()
data class TestActionsDialog( data class TestActionsDialog(

View file

@ -184,10 +184,6 @@ private fun getExistentialDeposit(walletManager: WalletManager?): BigDecimal? {
private fun Wallet.getWalletAddresses(): WalletDataModel.WalletAddresses? { private fun Wallet.getWalletAddresses(): WalletDataModel.WalletAddresses? {
return this.createAddressesData() return this.createAddressesData()
.takeIf { it.isNotEmpty() } .takeIf { it.isNotEmpty() }
?.map {
// TODO: Will be removed in next MR
with(it) { WalletDataModel.AddressData(address, type, shareUrl, exploreUrl) }
}
?.let { addresses -> ?.let { addresses ->
WalletDataModel.WalletAddresses( WalletDataModel.WalletAddresses(
list = addresses, list = addresses,

View file

@ -12,8 +12,9 @@ import com.tangem.tap.common.extensions.dispatchDialogHide
import com.tangem.tap.common.extensions.dispatchShare import com.tangem.tap.common.extensions.dispatchShare
import com.tangem.tap.common.extensions.dispatchToastNotification import com.tangem.tap.common.extensions.dispatchToastNotification
import com.tangem.tap.common.extensions.getString import com.tangem.tap.common.extensions.getString
import com.tangem.tap.common.extensions.toQrCode
import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.features.wallet.redux.AddressData import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.store import com.tangem.tap.store
import com.tangem.wallet.R import com.tangem.wallet.R
import com.tangem.wallet.databinding.DialogOnboardingAddressInfoBinding import com.tangem.wallet.databinding.DialogOnboardingAddressInfoBinding
@ -46,12 +47,12 @@ class AddressInfoBottomSheetDialog(
showData(data = stateDialog.addressData) showData(data = stateDialog.addressData)
} }
private fun showData(data: AddressData) = with(binding!!) { private fun showData(data: WalletDataModel.AddressData) = with(binding!!) {
pseudoToolbar.imvClose.setOnClickListener { pseudoToolbar.imvClose.setOnClickListener {
dismissWithAnimation = true dismissWithAnimation = true
cancel() cancel()
} }
imvQrCode.setImageBitmap(data.qrCode) imvQrCode.setImageBitmap(data.shareUrl.toQrCode())
tvAddress.text = data.address tvAddress.text = data.address
btnFlCopyAddress.setOnClickListener { btnFlCopyAddress.setOnClickListener {
Analytics.send(Token.Receive.ButtonCopyAddress()) Analytics.send(Token.Receive.ButtonCopyAddress())

View file

@ -17,8 +17,8 @@ import com.tangem.tap.features.send.redux.states.ReceiptSymbols
import com.tangem.tap.features.send.redux.states.ReceiptTokenCrypto import com.tangem.tap.features.send.redux.states.ReceiptTokenCrypto
import com.tangem.tap.features.send.redux.states.ReceiptTokenFiat import com.tangem.tap.features.send.redux.states.ReceiptTokenFiat
import com.tangem.tap.features.send.redux.states.SendState import com.tangem.tap.features.send.redux.states.SendState
import com.tangem.tap.features.wallet.redux.WalletState.Companion.CAN_BE_LOWER_SIGN import com.tangem.tap.features.wallet.redux.utils.CAN_BE_LOWER_SIGN
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN import com.tangem.tap.features.wallet.redux.utils.UNKNOWN_AMOUNT_SIGN
import com.tangem.tap.store import com.tangem.tap.store
import java.math.BigDecimal import java.math.BigDecimal

View file

@ -38,8 +38,8 @@ import com.tangem.tap.features.send.ui.dialogs.RequestFeeErrorDialog
import com.tangem.tap.features.send.ui.dialogs.SendTransactionFailsDialog import com.tangem.tap.features.send.ui.dialogs.SendTransactionFailsDialog
import com.tangem.tap.features.send.ui.dialogs.TezosWarningDialog import com.tangem.tap.features.send.ui.dialogs.TezosWarningDialog
import com.tangem.tap.features.wallet.redux.ProgressState import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.features.wallet.redux.WalletState.Companion.ROUGH_SIGN import com.tangem.tap.features.wallet.redux.utils.ROUGH_SIGN
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN import com.tangem.tap.features.wallet.redux.utils.UNKNOWN_AMOUNT_SIGN
import com.tangem.tap.features.wallet.ui.adapters.WarningMessagesAdapter import com.tangem.tap.features.wallet.ui.adapters.WarningMessagesAdapter
import com.tangem.wallet.R import com.tangem.wallet.R

View file

@ -3,8 +3,8 @@ package com.tangem.tap.features.tokens.redux
import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.DerivationStyle import com.tangem.blockchain.common.DerivationStyle
import com.tangem.domain.common.ScanResponse import com.tangem.domain.common.ScanResponse
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.tokens.Currency import com.tangem.tap.domain.tokens.Currency
import com.tangem.tap.features.wallet.redux.WalletData
import org.rekotlin.Action import org.rekotlin.Action
sealed class TokensAction : Action { sealed class TokensAction : Action {
@ -23,7 +23,7 @@ sealed class TokensAction : Action {
) : TokensAction() ) : TokensAction()
data class SetAddedCurrencies( data class SetAddedCurrencies(
val wallets: List<WalletData>, val wallets: List<WalletDataModel>,
val derivationStyle: DerivationStyle?, val derivationStyle: DerivationStyle?,
) : TokensAction() ) : TokensAction()

View file

@ -6,13 +6,13 @@ import com.tangem.blockchain.common.Token
import com.tangem.domain.common.ScanResponse import com.tangem.domain.common.ScanResponse
import com.tangem.domain.common.extensions.canHandleToken import com.tangem.domain.common.extensions.canHandleToken
import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.domain.common.extensions.fromNetworkId
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.tokens.Currency import com.tangem.tap.domain.tokens.Currency
import com.tangem.tap.features.wallet.redux.WalletData
import org.rekotlin.StateType import org.rekotlin.StateType
import com.tangem.tap.features.wallet.models.Currency.Token as CurrencyToken import com.tangem.tap.features.wallet.models.Currency.Token as CurrencyToken
data class TokensState( data class TokensState(
val addedWallets: List<WalletData> = emptyList(), val addedWallets: List<WalletDataModel> = emptyList(),
val addedTokens: List<TokenWithBlockchain> = emptyList(), val addedTokens: List<TokenWithBlockchain> = emptyList(),
val addedBlockchains: List<Blockchain> = emptyList(), val addedBlockchains: List<Blockchain> = emptyList(),
val currencies: List<Currency> = emptyList(), val currencies: List<Currency> = emptyList(),
@ -31,17 +31,9 @@ data class TokensState(
typealias ContractAddress = String typealias ContractAddress = String
fun List<WalletData>.toTokensContractAddresses(): List<ContractAddress> { fun List<WalletDataModel>.toNonCustomTokensWithBlockchains(
return mapNotNull { (it.currency as? CurrencyToken)?.token?.contractAddress }.distinct() derivationStyle: DerivationStyle?,
} ): List<TokenWithBlockchain> {
fun List<WalletData>.toNonCustomTokens(derivationStyle: DerivationStyle?): List<Token> {
return filter { !it.currency.isCustomCurrency(derivationStyle) }
.mapNotNull { (it.currency as? CurrencyToken)?.token }
.distinct()
}
fun List<WalletData>.toNonCustomTokensWithBlockchains(derivationStyle: DerivationStyle?): List<TokenWithBlockchain> {
return mapNotNull { return mapNotNull {
if (it.currency !is CurrencyToken) return@mapNotNull null if (it.currency !is CurrencyToken) return@mapNotNull null
if (it.currency.isCustomCurrency(derivationStyle)) return@mapNotNull null if (it.currency.isCustomCurrency(derivationStyle)) return@mapNotNull null
@ -49,7 +41,7 @@ fun List<WalletData>.toNonCustomTokensWithBlockchains(derivationStyle: Derivatio
}.distinct() }.distinct()
} }
fun List<WalletData>.toNonCustomBlockchains(derivationStyle: DerivationStyle?): List<Blockchain> { fun List<WalletDataModel>.toNonCustomBlockchains(derivationStyle: DerivationStyle?): List<Blockchain> {
return mapNotNull { return mapNotNull {
if (it.currency.isCustomCurrency(derivationStyle)) { if (it.currency.isCustomCurrency(derivationStyle)) {
null null

View file

@ -1,5 +1,7 @@
package com.tangem.tap.features.wallet.models package com.tangem.tap.features.wallet.models
import com.tangem.tap.domain.model.WalletStoreModel
sealed class WalletWarning(val showingPosition: Int) { sealed class WalletWarning(val showingPosition: Int) {
data class ExistentialDeposit( data class ExistentialDeposit(
@ -15,7 +17,7 @@ sealed class WalletWarning(val showingPosition: Int) {
val blockchainSymbol: String, val blockchainSymbol: String,
) : WalletWarning(showingPosition = 30) ) : WalletWarning(showingPosition = 30)
data class Rent(val walletRent: WalletRent) : WalletWarning(showingPosition = 40) data class Rent(val walletRent: WalletStoreModel.WalletRent) : WalletWarning(showingPosition = 40)
} }
data class WalletWarningDescription(val title: String, val message: String) data class WalletWarningDescription(val title: String, val message: String)

View file

@ -8,11 +8,12 @@ import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.redux.NotificationAction import com.tangem.tap.common.redux.NotificationAction
import com.tangem.tap.domain.TapError import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.domain.model.TotalFiatBalance
import com.tangem.tap.domain.model.UserWallet import com.tangem.tap.domain.model.UserWallet
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.WalletStoreModel import com.tangem.tap.domain.model.WalletStoreModel
import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.models.TotalBalance
import com.tangem.tap.features.wallet.redux.models.WalletDialog import com.tangem.tap.features.wallet.redux.models.WalletDialog
import com.tangem.wallet.R import com.tangem.wallet.R
import org.rekotlin.Action import org.rekotlin.Action
@ -38,7 +39,6 @@ sealed class WalletAction : Action {
data class RemoveWallet(val currency: Currency) : MultiWallet() data class RemoveWallet(val currency: Currency) : MultiWallet()
object BackupWallet : MultiWallet() object BackupWallet : MultiWallet()
object ScheduleCheckForMissingDerivation : MultiWallet()
data class AddMissingDerivations(val blockchains: List<BlockchainNetwork>) : MultiWallet() data class AddMissingDerivations(val blockchains: List<BlockchainNetwork>) : MultiWallet()
object ScanToGetDerivations : MultiWallet() object ScanToGetDerivations : MultiWallet()
} }
@ -78,7 +78,7 @@ sealed class WalletAction : Action {
sealed class DialogAction : WalletAction() { sealed class DialogAction : WalletAction() {
data class QrCode( data class QrCode(
val currency: Currency, val currency: Currency,
val selectedAddress: AddressData, val selectedAddress: WalletDataModel.AddressData,
) : DialogAction() ) : DialogAction()
object SignedHashesMultiWalletDialog : DialogAction() object SignedHashesMultiWalletDialog : DialogAction()
@ -127,9 +127,7 @@ sealed class WalletAction : Action {
} }
data class UserWalletChanged(val userWallet: UserWallet) : WalletAction() data class UserWalletChanged(val userWallet: UserWallet) : WalletAction()
data class WalletStoresChanged(val walletStores: List<WalletStoreModel>) : WalletAction() { data class WalletStoresChanged(val walletStores: List<WalletStoreModel>) : WalletAction()
data class UpdateWalletStores(val reduxWalletStores: List<WalletStore>) : WalletAction()
}
data class TotalFiatBalanceChanged(val balance: TotalBalance) : WalletAction() data class TotalFiatBalanceChanged(val balance: TotalFiatBalance) : WalletAction()
} }

View file

@ -85,7 +85,8 @@ data class WalletData(
walletWarnings.add(WalletWarning.TransactionInProgress(currency.currencyName)) walletWarnings.add(WalletWarning.TransactionInProgress(currency.currencyName))
} }
if (walletRent != null) { if (walletRent != null) {
walletWarnings.add(WalletWarning.Rent(walletRent)) // TODO: Will be removed in next MR
// walletWarnings.add(WalletWarning.Rent(walletRent))
} }
} }

View file

@ -9,29 +9,30 @@ import com.tangem.tap.common.extensions.toQrCode
import com.tangem.tap.common.redux.global.CryptoCurrencyName import com.tangem.tap.common.redux.global.CryptoCurrencyName
import com.tangem.tap.common.toggleWidget.WidgetState import com.tangem.tap.common.toggleWidget.WidgetState
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.domain.model.TotalFiatBalance
import com.tangem.tap.domain.model.UserWallet
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.WalletStoreModel
import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.models.TotalBalance
import com.tangem.tap.features.wallet.redux.reducers.findProgressState
import com.tangem.tap.features.wallet.ui.BalanceStatus
import com.tangem.tap.store import com.tangem.tap.store
import org.rekotlin.StateType import org.rekotlin.StateType
import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadOnlyProperty
data class WalletState( data class WalletState(
val cardId: String = "", val userWallet: UserWallet? = null,
val state: ProgressState = ProgressState.Done, val state: ProgressState = ProgressState.Done,
val error: ErrorType? = null, val error: ErrorType? = null,
val cardImage: Artwork? = null, val cardImage: Artwork? = null,
val hashesCountVerified: Boolean? = null, val hashesCountVerified: Boolean? = null,
val mainWarningsList: List<WarningMessage> = mutableListOf(), val mainWarningsList: List<WarningMessage> = mutableListOf(),
val walletsStores: List<WalletStore> = listOf(), val walletsStores: List<WalletStoreModel> = listOf(),
val isMultiwalletAllowed: Boolean = false, val isMultiwalletAllowed: Boolean = false,
val cardCurrency: CryptoCurrencyName? = null, val cardCurrency: CryptoCurrencyName? = null,
val selectedCurrency: Currency? = null, val selectedCurrency: Currency? = null,
val isTestnet: Boolean = false, val isTestnet: Boolean = false,
val totalBalance: TotalBalance? = null, val totalBalance: TotalFiatBalance? = null,
val showBackupWarning: Boolean = false, val showBackupWarning: Boolean = false,
val missingDerivations: List<BlockchainNetwork> = emptyList(), val missingDerivations: List<BlockchainNetwork> = emptyList(),
val loadingUserTokens: Boolean = false, val loadingUserTokens: Boolean = false,
@ -39,10 +40,10 @@ data class WalletState(
val canSaveUserWallets: Boolean = false, val canSaveUserWallets: Boolean = false,
) : StateType { ) : StateType {
val walletsDataFromStores: List<WalletData> val walletsDataFromStores: List<WalletDataModel>
get() = walletsStores.map { it.walletsData }.flatten() get() = walletsStores.flatMap { it.walletsData }
val selectedWalletData: WalletData? val selectedWalletData: WalletDataModel?
get() = walletsDataFromStores.firstOrNull { it.currency == selectedCurrency } get() = walletsDataFromStores.firstOrNull { it.currency == selectedCurrency }
// if you do not delegate - the application crashes on startup, // if you do not delegate - the application crashes on startup,
@ -66,7 +67,7 @@ data class WalletState(
val walletManagers: List<WalletManager> val walletManagers: List<WalletManager>
get() = walletsStores.mapNotNull { it.walletManager } get() = walletsStores.mapNotNull { it.walletManager }
private val primaryWalletStore: WalletStore? private val primaryWalletStore: WalletStoreModel?
get() = if (isMultiwalletAllowed || walletsStores.isEmpty() || walletsStores.size > 1) { get() = if (isMultiwalletAllowed || walletsStores.isEmpty() || walletsStores.size > 1) {
null null
} else { } else {
@ -76,17 +77,15 @@ data class WalletState(
val primaryWalletManager: WalletManager? val primaryWalletManager: WalletManager?
get() = primaryWalletStore?.walletManager get() = primaryWalletStore?.walletManager
val primaryWalletData: WalletData? val primaryWalletData: WalletDataModel?
get() = primaryWalletStore?.walletsData?.firstOrNull() get() = primaryWalletStore?.blockchainWalletData
val primaryTokenData: WalletData? val primaryTokenData: WalletDataModel?
get() = primaryWalletStore?.walletsData?.toMutableList() get() = primaryWalletStore?.walletsData
?.apply { remove(primaryWalletData) } ?.firstOrNull { it.currency !is Currency.Blockchain }
?.firstOrNull()
val shouldShowDetails: Boolean = val shouldShowDetails: Boolean =
primaryWalletData?.currencyData?.status != BalanceStatus.EmptyCard && primaryWalletData?.status !is WalletDataModel.Unreachable
primaryWalletData?.currencyData?.status != BalanceStatus.UnknownBlockchain
fun getWalletManager(currency: Currency?): WalletManager? { fun getWalletManager(currency: Currency?): WalletManager? {
if (currency?.blockchain == null) return null if (currency?.blockchain == null) return null
@ -94,95 +93,27 @@ data class WalletState(
} }
fun getWalletManager(blockchain: BlockchainNetwork): WalletManager? { fun getWalletManager(blockchain: BlockchainNetwork): WalletManager? {
return walletsStores.find { it.blockchainNetwork == blockchain }?.walletManager return walletsStores.firstOrNull {
it.blockchain == blockchain.blockchain &&
it.derivationPath?.rawPath == blockchain.derivationPath
}?.walletManager
} }
fun getWalletData(blockchain: BlockchainNetwork?): WalletData? { fun getWalletStore(currency: Currency?): WalletStoreModel? {
if (blockchain == null) return null
return walletsDataFromStores.find {
it.currency is Currency.Blockchain &&
it.currency.blockchain == blockchain.blockchain &&
it.currency.derivationPath == blockchain.derivationPath
}
}
fun getWalletStore(currency: Currency?): WalletStore? {
if (currency == null) return null if (currency == null) return null
return walletsStores.firstOrNull { return walletsStores.firstOrNull {
it.blockchainNetwork.derivationPath == currency.derivationPath && it.blockchain == currency.blockchain &&
it.blockchainNetwork.blockchain == currency.blockchain it.derivationPath?.rawPath == currency.derivationPath
} }
} }
private fun getWalletStore(blockchainNetwork: BlockchainNetwork?): WalletStore? {
if (blockchainNetwork == null) return null
return walletsStores.firstOrNull {
it.blockchainNetwork.derivationPath == blockchainNetwork.derivationPath &&
it.blockchainNetwork.blockchain == blockchainNetwork.blockchain
}
}
fun getWalletData(currency: Currency?): WalletData? {
if (currency == null) return null
return getWalletStore(currency)?.walletsData?.firstOrNull { it.currency == currency }
}
fun updateWalletData(walletData: WalletData?): WalletState {
if (walletData == null) return this
return updateWalletsData(listOf(walletData))
}
private fun updateWalletsData(walletsData: List<WalletData>): WalletState {
val walletStores = walletsData
.map { BlockchainNetwork(it.currency.blockchain, it.currency.derivationPath, emptyList()) }
.distinct().map { getWalletStore(it) }.mapNotNull { it?.updateWallets(walletsData) }
return updateWalletsStores(walletStores)
}
private fun updateWalletsStores(walletStores: List<WalletStore>): WalletState {
val walletStoresMutable = walletStores.toMutableList()
val updatedWallets = walletsStores.map { oldWalletStore ->
val walletStore = walletStoresMutable.find {
it.blockchainNetwork == oldWalletStore.blockchainNetwork
}
if (walletStore != null) {
walletStoresMutable.remove(walletStore)
walletStore
} else {
oldWalletStore
}
}
return copy(walletsStores = updatedWallets + walletStoresMutable)
.updateProgressState()
}
private fun updateProgressState(): WalletState {
val walletsData = this.walletsStores
.flatMap(WalletStore::walletsData)
return if (walletsData.isNotEmpty()) {
val newProgressState = walletsData.findProgressState()
this.copy(
state = walletsData.findProgressState(),
error = this.error.takeIf { newProgressState == ProgressState.Error },
)
} else {
this
}
}
companion object {
const val UNKNOWN_AMOUNT_SIGN = ""
const val ROUGH_SIGN = ""
const val CAN_BE_LOWER_SIGN = "<"
}
} }
enum class ProgressState : WidgetState { Loading, Refreshing, Done, Error } enum class ProgressState : WidgetState { Loading, Refreshing, Done, Error }
enum class ErrorType { NoInternetConnection } enum class ErrorType {
NoInternetConnection,
UnknownBlockchain,
}
sealed class WalletMainButton(enabled: Boolean) : Button(enabled) { sealed class WalletMainButton(enabled: Boolean) : Button(enabled) {
class SendButton(enabled: Boolean) : WalletMainButton(enabled) class SendButton(enabled: Boolean) : WalletMainButton(enabled)

View file

@ -77,7 +77,7 @@ private fun WalletDataModel.mapToReduxModel(
return WalletData( return WalletData(
currency = currency, currency = currency,
// TODO: Will be updated in next MR // TODO: Will be updated in next MRs
walletAddresses = walletAddresses?.let { addresses -> walletAddresses = walletAddresses?.let { addresses ->
WalletAddresses( WalletAddresses(
selectedAddress = with(addresses.selectedAddress) { selectedAddress = with(addresses.selectedAddress) {

View file

@ -32,10 +32,7 @@ import com.tangem.tap.features.send.redux.PrepareSendScreen
import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.models.getSendableAmounts import com.tangem.tap.features.wallet.models.getSendableAmounts
import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletData
import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.redux.WalletStore
import com.tangem.tap.features.wallet.redux.reducers.findSelectedCurrency
import com.tangem.tap.preferencesStorage import com.tangem.tap.preferencesStorage
import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.scope import com.tangem.tap.scope
@ -106,7 +103,7 @@ class WalletMiddleware {
scope.launch { scope.launch {
when (val result = tangemSdkManager.createWallet(globalState.scanResponse?.card?.cardId)) { when (val result = tangemSdkManager.createWallet(globalState.scanResponse?.card?.cardId)) {
is CompletionResult.Success -> { is CompletionResult.Success -> {
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard { val selectedUserWallet = walletState.userWallet.guard {
Timber.e("Unable to create wallet, no user wallet selected") Timber.e("Unable to create wallet, no user wallet selected")
return@launch return@launch
} }
@ -132,7 +129,7 @@ class WalletMiddleware {
is WalletAction.LoadData, is WalletAction.LoadData,
is WalletAction.LoadData.Refresh, is WalletAction.LoadData.Refresh,
-> { -> {
val selectedWallet = userWalletsListManager.selectedUserWalletSync.guard { val selectedWallet = walletState.userWallet.guard {
Timber.e("Unable to load/refresh wallets data, no user wallet selected") Timber.e("Unable to load/refresh wallets data, no user wallet selected")
return return
} }
@ -188,7 +185,6 @@ class WalletMiddleware {
is WalletAction.WalletStoresChanged -> { is WalletAction.WalletStoresChanged -> {
// Cancel update job when new wallet stores received // Cancel update job when new wallet stores received
updateWalletStoresJob = scope.launch(Dispatchers.Default) { updateWalletStoresJob = scope.launch(Dispatchers.Default) {
ifActive { updateWalletStores(action.walletStores, walletState) }
ifActive { fetchTotalFiatBalance(action.walletStores) } ifActive { fetchTotalFiatBalance(action.walletStores) }
ifActive { findMissedDerivations(action.walletStores) } ifActive { findMissedDerivations(action.walletStores) }
ifActive { tryToShowAppRatingWarning(action.walletStores) } ifActive { tryToShowAppRatingWarning(action.walletStores) }
@ -227,26 +223,8 @@ class WalletMiddleware {
} }
} }
private fun updateWalletStores(walletsStores: List<WalletStoreModel>, state: WalletState) {
val reduxWalletStores = walletsStores.mapToReduxModels()
if (!state.isMultiwalletAllowed) {
findSelectedCurrency(
walletsStores = reduxWalletStores,
currentSelectedCurrency = null,
isMultiWalletAllowed = false,
)?.let {
store.dispatchOnMain(WalletAction.MultiWallet.SetSingleWalletCurrency(it))
}
}
store.dispatchOnMain(
WalletAction.WalletStoresChanged.UpdateWalletStores(
reduxWalletStores = reduxWalletStores,
),
)
}
private suspend fun fetchTotalFiatBalance(walletStores: List<WalletStoreModel>) { private suspend fun fetchTotalFiatBalance(walletStores: List<WalletStoreModel>) {
val totalFiatBalance = totalFiatBalanceCalculator.calculateOrNull(walletStores)?.mapToReduxModel() val totalFiatBalance = totalFiatBalanceCalculator.calculateOrNull(walletStores)
if (totalFiatBalance != null) { if (totalFiatBalance != null) {
store.dispatchOnMain(WalletAction.TotalFiatBalanceChanged(totalFiatBalance)) store.dispatchOnMain(WalletAction.TotalFiatBalanceChanged(totalFiatBalance))
@ -300,7 +278,7 @@ class WalletMiddleware {
return if (amount != null) { return if (amount != null) {
if (amount.type is AmountType.Token) { if (amount.type is AmountType.Token) {
prepareSendActionForToken(amount, state, selectedWalletData, walletStore) prepareSendActionForToken(amount, selectedWalletData, walletStore)
} else { } else {
PrepareSendScreen(amount, selectedWalletData?.fiatRate, walletStore?.walletManager) PrepareSendScreen(amount, selectedWalletData?.fiatRate, walletStore?.walletManager)
} }
@ -322,7 +300,6 @@ class WalletMiddleware {
?: return WalletAction.DialogAction.ChooseCurrency(amounts) ?: return WalletAction.DialogAction.ChooseCurrency(amounts)
prepareSendActionForToken( prepareSendActionForToken(
amount = amountToSend, amount = amountToSend,
state = state,
selectedWalletData = selectedWalletData, selectedWalletData = selectedWalletData,
walletStore = walletStore, walletStore = walletStore,
) )
@ -346,11 +323,10 @@ class WalletMiddleware {
private fun prepareSendActionForToken( private fun prepareSendActionForToken(
amount: Amount, amount: Amount,
state: WalletState?, selectedWalletData: WalletDataModel?,
selectedWalletData: WalletData?, walletStore: WalletStoreModel?,
walletStore: WalletStore?,
): PrepareSendScreen { ): PrepareSendScreen {
val coinRate = state?.getWalletData(walletStore?.blockchainNetwork)?.fiatRate val coinRate = walletStore?.blockchainWalletData?.fiatRate
val tokenRate = selectedWalletData?.fiatRate val tokenRate = selectedWalletData?.fiatRate
val coinAmount = walletStore?.walletManager?.wallet?.amounts?.get(AmountType.Coin) val coinAmount = walletStore?.walletManager?.wallet?.amounts?.get(AmountType.Coin)

View file

@ -1,23 +1,18 @@
package com.tangem.tap.features.wallet.redux.reducers package com.tangem.tap.features.wallet.redux.reducers
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Wallet import com.tangem.blockchain.common.Wallet
import com.tangem.domain.common.CardDTO import com.tangem.domain.common.CardDTO
import com.tangem.domain.common.TapWorkarounds.isTestCard import com.tangem.domain.common.TapWorkarounds.isTestCard
import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.AppState
import com.tangem.tap.domain.TapError import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.WalletStoreModel
import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.redux.AddressData
import com.tangem.tap.features.wallet.redux.Artwork import com.tangem.tap.features.wallet.redux.Artwork
import com.tangem.tap.features.wallet.redux.ErrorType import com.tangem.tap.features.wallet.redux.ErrorType
import com.tangem.tap.features.wallet.redux.ProgressState import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletData
import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.redux.WalletStore
import com.tangem.tap.features.wallet.ui.BalanceStatus
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
import com.tangem.tap.proxy.AppStateHolder import com.tangem.tap.proxy.AppStateHolder
import org.rekotlin.Action import org.rekotlin.Action
@ -41,44 +36,15 @@ private fun internalReduce(action: Action, state: AppState, appStateHolder: AppS
is WalletAction.LoadData.Failure -> { is WalletAction.LoadData.Failure -> {
when (action.error) { when (action.error) {
is TapError.NoInternetConnection -> { is TapError.NoInternetConnection -> {
val wallets = newState.walletsStores
.map { store ->
store.copy(
walletsData = store.walletsData.map {
it.copy(
currencyData = it.currencyData.copy(
status = BalanceStatus.Unreachable,
),
)
},
)
}
newState = newState.copy( newState = newState.copy(
state = ProgressState.Error, state = ProgressState.Error,
error = ErrorType.NoInternetConnection, error = ErrorType.NoInternetConnection,
walletsStores = wallets,
) )
} }
is TapError.UnknownBlockchain -> { is TapError.UnknownBlockchain -> {
newState = newState.copy( newState = newState.copy(
state = ProgressState.Done, state = ProgressState.Error,
walletsStores = listOf( error = ErrorType.UnknownBlockchain,
WalletStore(
walletManager = null,
blockchainNetwork = BlockchainNetwork(
Blockchain.Unknown,
null,
emptyList(),
),
walletsData = listOf(
WalletData(
currencyData = BalanceWidgetData(BalanceStatus.UnknownBlockchain),
currency = Currency.Blockchain(Blockchain.Unknown, null),
),
),
),
),
) )
} }
else -> { else -> {
@ -107,7 +73,7 @@ private fun internalReduce(action: Action, state: AppState, appStateHolder: AppS
is WalletAction.UserWalletChanged -> with(action.userWallet) { is WalletAction.UserWalletChanged -> with(action.userWallet) {
val card = scanResponse.card val card = scanResponse.card
newState = WalletState( newState = WalletState(
cardId = card.cardId, userWallet = this,
isMultiwalletAllowed = isMultiCurrency, isMultiwalletAllowed = isMultiCurrency,
cardImage = Artwork( cardImage = Artwork(
artworkId = artworkUrl, artworkId = artworkUrl,
@ -126,10 +92,9 @@ private fun internalReduce(action: Action, state: AppState, appStateHolder: AppS
}, },
) )
} }
is WalletAction.WalletStoresChanged.UpdateWalletStores -> { is WalletAction.WalletStoresChanged -> {
newState = newState.copy( newState = newState.copy(
state = action.reduxWalletStores.flatMap { it.walletsData }.findProgressState(newState.state), walletsStores = action.walletStores,
walletsStores = action.reduxWalletStores,
) )
} }
is WalletAction.TotalFiatBalanceChanged -> { is WalletAction.TotalFiatBalanceChanged -> {
@ -159,7 +124,7 @@ private fun internalReduce(action: Action, state: AppState, appStateHolder: AppS
} }
fun findSelectedCurrency( fun findSelectedCurrency(
walletsStores: List<WalletStore>, walletsStores: List<WalletStoreModel>,
currentSelectedCurrency: Currency?, currentSelectedCurrency: Currency?,
isMultiWalletAllowed: Boolean, isMultiWalletAllowed: Boolean,
): Currency? = if (isMultiWalletAllowed) { ): Currency? = if (isMultiWalletAllowed) {
@ -175,11 +140,11 @@ private fun CardDTO.findCardsCount(): Int? {
return (this.backupStatus as? CardDTO.BackupStatus.Active)?.cardCount?.inc() return (this.backupStatus as? CardDTO.BackupStatus.Active)?.cardCount?.inc()
} }
fun Wallet.createAddressesData(): List<AddressData> { fun Wallet.createAddressesData(): List<WalletDataModel.AddressData> {
val listOfAddressData = mutableListOf<AddressData>() val listOfAddressData = mutableListOf<WalletDataModel.AddressData>()
// put a defaultAddress at the first place // put a defaultAddress at the first place
addresses.forEach { addresses.forEach {
val addressData = AddressData( val addressData = WalletDataModel.AddressData(
it.value, it.value,
it.type, it.type,
getShareUri(it.value), getShareUri(it.value),

View file

@ -0,0 +1,5 @@
package com.tangem.tap.features.wallet.redux.utils
const val UNKNOWN_AMOUNT_SIGN = ""
const val ROUGH_SIGN = ""
const val CAN_BE_LOWER_SIGN = "<"

View file

@ -3,6 +3,10 @@ package com.tangem.tap.features.wallet.ui
import androidx.annotation.IdRes import androidx.annotation.IdRes
import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show import com.tangem.tap.common.extensions.show
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.features.wallet.ui.utils.getFormattedAmount
import com.tangem.tap.features.wallet.ui.utils.getFormattedFiatAmount
import com.tangem.tap.store
import com.tangem.wallet.R import com.tangem.wallet.R
import com.tangem.wallet.databinding.CardBalanceBinding import com.tangem.wallet.databinding.CardBalanceBinding
import java.math.BigDecimal import java.math.BigDecimal
@ -36,36 +40,37 @@ data class BalanceWidgetData(
class BalanceWidget( class BalanceWidget(
private val binding: CardBalanceBinding, private val binding: CardBalanceBinding,
private val fragment: WalletFragment, private val fragment: WalletFragment,
private val data: BalanceWidgetData, private val blockchainWalletData: WalletDataModel,
private val token: BalanceWidgetData?, private val tokenWalletData: WalletDataModel?,
private val isTwinCard: Boolean,
) { ) {
@Suppress("LongMethod", "ComplexMethod") @Suppress("LongMethod", "ComplexMethod")
fun setup() { fun setup() {
when (data.status) { when (blockchainWalletData.status) {
BalanceStatus.Loading -> { is WalletDataModel.Loading -> {
with(binding) { with(binding) {
lBalance.root.show() lBalance.root.show()
lBalanceError.root.hide() lBalanceError.root.hide()
lBalance.tvFiatAmount.hide() lBalance.tvFiatAmount.hide()
lBalance.tvCurrency.text = data.currency lBalance.tvCurrency.text = blockchainWalletData.currency.currencyName
lBalance.tvAmount.text = "" lBalance.tvAmount.text = ""
} }
showStatus(R.id.tv_status_loading) showStatus(R.id.tv_status_loading)
if (token != null) { if (tokenWalletData != null) {
showBalanceWithToken(data, false) showBalanceWithToken(blockchainWalletData, false)
} else { } else {
showBalanceWithoutToken(data, false) showBalanceWithoutToken(blockchainWalletData, false)
} }
} }
BalanceStatus.VerifiedOnline, BalanceStatus.TransactionInProgress -> with(binding.lBalance) { is WalletDataModel.VerifiedOnline,
is WalletDataModel.TransactionInProgress,
-> with(binding.lBalance) {
root.show() root.show()
binding.lBalanceError.root.hide() binding.lBalanceError.root.hide()
val statusView = if (data.status == BalanceStatus.VerifiedOnline) { val statusView = if (blockchainWalletData.status is WalletDataModel.VerifiedOnline) {
R.id.tv_status_verified R.id.tv_status_verified
} else { } else {
tvStatusError.text = tvStatusError.text =
@ -75,61 +80,62 @@ class BalanceWidget(
showStatus(statusView) showStatus(statusView)
tvStatusErrorMessage.hide() tvStatusErrorMessage.hide()
if (token != null) { if (tokenWalletData != null) {
showBalanceWithToken(data, true) showBalanceWithToken(blockchainWalletData, true)
} else { } else {
showBalanceWithoutToken(data, true) showBalanceWithoutToken(blockchainWalletData, true)
} }
} }
BalanceStatus.Unreachable -> with(binding.lBalance) { is WalletDataModel.Unreachable -> with(binding.lBalance) {
root.show() root.show()
binding.lBalanceError.root.hide() binding.lBalanceError.root.hide()
tvFiatAmount.hide() tvFiatAmount.hide()
groupBaseCurrency.hide() groupBaseCurrency.hide()
val currency = if (token != null) token.currencySymbol else data.currency val currency = tokenWalletData?.currency?.currencySymbol
?: blockchainWalletData.currency.currencyName
tvCurrency.text = currency tvCurrency.text = currency
tvAmount.text = "" tvAmount.text = ""
tvStatusErrorMessage.text = data.errorMessage tvStatusErrorMessage.text = blockchainWalletData.status.errorMessage
tvStatusError.text = tvStatusError.text =
fragment.getString(R.string.wallet_balance_blockchain_unreachable) fragment.getString(R.string.wallet_balance_blockchain_unreachable)
showStatus(R.id.group_error) showStatus(R.id.group_error)
tvStatusErrorMessage.show(!data.errorMessage.isNullOrBlank()) tvStatusErrorMessage.show(!blockchainWalletData.status.errorMessage.isNullOrBlank())
} }
BalanceStatus.EmptyCard -> with(binding.lBalanceError) { // BalanceStatus.EmptyCard -> with(binding.lBalanceError) {
binding.lBalance.root.hide() // binding.lBalance.root.hide()
binding.lBalanceError.root.show() // binding.lBalanceError.root.show()
if (isTwinCard) { // if (isTwinCard) {
tvErrorTitle.text = fragment.getText(R.string.wallet_error_empty_twin_card) // tvErrorTitle.text = fragment.getText(R.string.wallet_error_empty_twin_card)
tvErrorDescriptions.text = // tvErrorDescriptions.text =
fragment.getText(R.string.wallet_error_empty_twin_card_subtitle) // fragment.getText(R.string.wallet_error_empty_twin_card_subtitle)
} else { // } else {
tvErrorTitle.text = fragment.getText(R.string.wallet_error_empty_card) // tvErrorTitle.text = fragment.getText(R.string.wallet_error_empty_card)
tvErrorDescriptions.text = // tvErrorDescriptions.text =
fragment.getText(R.string.wallet_error_empty_card_subtitle) // fragment.getText(R.string.wallet_error_empty_card_subtitle)
} // }
} // }
BalanceStatus.NoAccount -> with(binding.lBalanceError) { is WalletDataModel.NoAccount -> with(binding.lBalanceError) {
binding.lBalance.root.hide() binding.lBalance.root.hide()
binding.lBalanceError.root.show() binding.lBalanceError.root.show()
tvErrorTitle.text = fragment.getText(R.string.wallet_error_no_account) tvErrorTitle.text = fragment.getText(R.string.wallet_error_no_account)
tvErrorDescriptions.text = tvErrorDescriptions.text =
fragment.getString( fragment.getString(
R.string.no_account_generic, R.string.no_account_generic,
data.amountToCreateAccount, blockchainWalletData.status.amountToCreateAccount,
data.currencySymbol, blockchainWalletData.currency.currencySymbol,
) )
} }
BalanceStatus.UnknownBlockchain -> with(binding.lBalanceError) { // BalanceStatus.UnknownBlockchain -> with(binding.lBalanceError) {
binding.lBalance.root.hide() // binding.lBalance.root.hide()
binding.lBalanceError.root.show() // binding.lBalanceError.root.show()
tvErrorTitle.text = // tvErrorTitle.text =
fragment.getText(R.string.wallet_error_unsupported_blockchain) // fragment.getText(R.string.wallet_error_unsupported_blockchain)
tvErrorDescriptions.text = // tvErrorDescriptions.text =
fragment.getString(R.string.wallet_error_unsupported_blockchain_subtitle) // fragment.getString(R.string.wallet_error_unsupported_blockchain_subtitle)
} // }
else -> {} else -> {}
} }
} }
@ -140,25 +146,25 @@ class BalanceWidget(
tvStatusVerified.show(viewRes == R.id.tv_status_verified) tvStatusVerified.show(viewRes == R.id.tv_status_verified)
} }
private fun showBalanceWithToken(data: BalanceWidgetData, showAmount: Boolean) = with(binding.lBalance) { private fun showBalanceWithToken(data: WalletDataModel, showAmount: Boolean) = with(binding.lBalance) {
groupBaseCurrency.show() groupBaseCurrency.show()
tvCurrency.text = token?.currencySymbol tvCurrency.text = tokenWalletData?.currency?.currencySymbol
tvBaseCurrency.text = data.currency tvBaseCurrency.text = data.currency.currencyName
tvAmount.text = if (showAmount) token?.amountFormatted else "" tvAmount.text = if (showAmount) tokenWalletData?.getFormattedAmount() else ""
tvBaseAmount.text = if (showAmount) data.amountFormatted else "" tvBaseAmount.text = if (showAmount) data.getFormattedAmount() else ""
if (showAmount) { if (showAmount) {
tvFiatAmount.show() tvFiatAmount.show()
tvFiatAmount.text = token?.fiatAmountFormatted tvFiatAmount.text = tokenWalletData?.getFormattedFiatAmount(store.state.globalState.appCurrency)
} }
} }
private fun showBalanceWithoutToken(data: BalanceWidgetData, showAmount: Boolean) = with(binding.lBalance) { private fun showBalanceWithoutToken(data: WalletDataModel, showAmount: Boolean) = with(binding.lBalance) {
groupBaseCurrency.hide() groupBaseCurrency.hide()
tvCurrency.text = data.currency tvCurrency.text = data.currency.currencyName
tvAmount.text = if (showAmount) data.amountFormatted else "" tvAmount.text = if (showAmount) data.getFormattedAmount() else ""
if (showAmount) { if (showAmount) {
tvFiatAmount.show() tvFiatAmount.show()
tvFiatAmount.text = data.fiatAmountFormatted tvFiatAmount.text = data.getFormattedFiatAmount(store.state.globalState.appCurrency)
} }
} }
} }

View file

@ -41,21 +41,29 @@ import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.extensions.toQrCode import com.tangem.tap.common.extensions.toQrCode
import com.tangem.tap.common.recyclerView.SpaceItemDecoration import com.tangem.tap.common.recyclerView.SpaceItemDecoration
import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.models.PendingTransaction import com.tangem.tap.features.wallet.models.PendingTransaction
import com.tangem.tap.features.wallet.models.PendingTransactionType import com.tangem.tap.features.wallet.models.PendingTransactionType
import com.tangem.tap.features.wallet.models.WalletWarning import com.tangem.tap.features.wallet.models.WalletWarning
import com.tangem.tap.features.wallet.redux.AddressData
import com.tangem.tap.features.wallet.redux.ErrorType import com.tangem.tap.features.wallet.redux.ErrorType
import com.tangem.tap.features.wallet.redux.ProgressState import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletData
import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN import com.tangem.tap.features.wallet.redux.utils.UNKNOWN_AMOUNT_SIGN
import com.tangem.tap.features.wallet.ui.adapters.PendingTransactionsAdapter import com.tangem.tap.features.wallet.ui.adapters.PendingTransactionsAdapter
import com.tangem.tap.features.wallet.ui.adapters.WalletDetailWarningMessagesAdapter import com.tangem.tap.features.wallet.ui.adapters.WalletDetailWarningMessagesAdapter
import com.tangem.tap.features.wallet.ui.images.load import com.tangem.tap.features.wallet.ui.images.load
import com.tangem.tap.features.wallet.ui.test.TestWallet import com.tangem.tap.features.wallet.ui.test.TestWallet
import com.tangem.tap.features.wallet.ui.utils.assembleWarnings
import com.tangem.tap.features.wallet.ui.utils.getAvailableActions
import com.tangem.tap.features.wallet.ui.utils.getFormattedAmount
import com.tangem.tap.features.wallet.ui.utils.getFormattedFiatAmount
import com.tangem.tap.features.wallet.ui.utils.isAvailableToBuy
import com.tangem.tap.features.wallet.ui.utils.isAvailableToSell
import com.tangem.tap.features.wallet.ui.utils.isAvailableToSwap
import com.tangem.tap.features.wallet.ui.utils.mainButton
import com.tangem.tap.features.wallet.ui.utils.shouldShowMultipleAddress
import com.tangem.tap.store import com.tangem.tap.store
import com.tangem.tap.userWalletsListManagerSafe import com.tangem.tap.userWalletsListManagerSafe
import com.tangem.tap.walletCurrenciesManager import com.tangem.tap.walletCurrenciesManager
@ -85,31 +93,19 @@ class WalletDetailsFragment :
private val binding: FragmentWalletDetailsBinding by viewBinding(FragmentWalletDetailsBinding::bind) private val binding: FragmentWalletDetailsBinding by viewBinding(FragmentWalletDetailsBinding::bind)
private val walletDataWatcher: ModelWatcher<WalletData> = modelWatcher { private val walletDataWatcher: ModelWatcher<WalletDataModel> = modelWatcher {
val addressCardStrategy: DiffStrategy<WalletData> = { old, new -> val addressCardStrategy: DiffStrategy<WalletDataModel> = { old, new ->
old.currency != new.currency || old.currency != new.currency || old.walletAddresses != new.walletAddresses
old.walletAddresses?.selectedAddress != new.walletAddresses?.selectedAddress ||
old.shouldShowMultipleAddress() != new.shouldShowMultipleAddress()
} }
WalletData::pendingTransactions { WalletDataModel::currency {
showPendingTransactionsIfPresent(it)
}
WalletData::currency {
handleCurrencyIcon(it) handleCurrencyIcon(it)
} }
WalletData::currencyData { WalletDataModel::walletAddresses { walletAddresses ->
setupBalanceData(it)
}
WalletData::walletAddresses { walletAddresses ->
setupCopyAndShareButtons(walletAddresses?.selectedAddress?.address) setupCopyAndShareButtons(walletAddresses?.selectedAddress?.address)
} }
WalletData::assembleWarnings { warnings -> WalletDataModel::currency { currency ->
handleWarnings(warnings) setupCurrency(currency)
}
(WalletData::currencyData or WalletData::currency) { walletData ->
setupCurrency(walletData.currencyData, walletData.currency)
setupSwipeRefresh(walletData.currencyData, walletData.currency)
} }
watch({ it }, addressCardStrategy) { walletData -> watch({ it }, addressCardStrategy) { walletData ->
setupAddressCard( setupAddressCard(
@ -121,9 +117,28 @@ class WalletDetailsFragment :
} }
private val walletStateWatcher: ModelWatcher<WalletState> = modelWatcher { private val walletStateWatcher: ModelWatcher<WalletState> = modelWatcher {
WalletState::selectedWalletData { selectedWallet -> val walletDataStrategy: DiffStrategy<WalletState> = { old, new ->
new.walletsStores.isNotEmpty() &&
new.selectedCurrency != null &&
(old.selectedCurrency != new.selectedCurrency || old.walletsStores != new.walletsStores)
}
watch({ it }, walletDataStrategy) { state ->
val selectedWallet = state.selectedWalletData
if (selectedWallet != null) { if (selectedWallet != null) {
setupBalanceData(selectedWallet)
setupSwipeRefresh(selectedWallet)
walletDataWatcher.invoke(selectedWallet) walletDataWatcher.invoke(selectedWallet)
val walletStore = state.getWalletStore(state.selectedCurrency)
if (walletStore != null) {
handleWarnings(
selectedWallet.assembleWarnings(
blockchainAmount = walletStore.blockchainWalletData.status.amount,
blockchainWalletRent = walletStore.walletRent,
),
)
}
} }
} }
(WalletState::selectedWalletData or WalletState::isExchangeServiceFeatureOn) { state -> (WalletState::selectedWalletData or WalletState::isExchangeServiceFeatureOn) { state ->
@ -234,8 +249,8 @@ class WalletDetailsFragment :
) )
} }
private fun setupCurrency(currencyData: BalanceWidgetData, currency: Currency) = with(binding) { private fun setupCurrency(currency: Currency) = with(binding) {
tvCurrencyTitle.text = currencyData.currency tvCurrencyTitle.text = currency.currencyName
if (currency is Currency.Token) { if (currency is Currency.Token) {
tvCurrencySubtitle.text = tvCurrencySubtitle.getString( tvCurrencySubtitle.text = tvCurrencySubtitle.getString(
@ -248,16 +263,17 @@ class WalletDetailsFragment :
} }
} }
private fun setupSwipeRefresh(currencyData: BalanceWidgetData, currency: Currency) { private fun setupSwipeRefresh(walletData: WalletDataModel) {
binding.srlWalletDetails.setOnRefreshListener { binding.srlWalletDetails.setOnRefreshListener {
if (currencyData.status != BalanceStatus.Loading && currencyData.status != BalanceStatus.Refreshing) { if (walletData.status !is WalletDataModel.Loading) {
Analytics.send(Token.Refreshed()) Analytics.send(Token.Refreshed())
val selectedUserWallet = userWalletsListManagerSafe?.selectedUserWalletSync.guard {
Timber.e("Unable to refresh wallet details screen, no user wallet selected")
return@setOnRefreshListener
}
binding.srlWalletDetails.isRefreshing = true
lifecycleScope.launch(Dispatchers.Default) { lifecycleScope.launch(Dispatchers.Default) {
val selectedUserWallet = userWalletsListManagerSafe?.selectedUserWalletSync.guard { walletCurrenciesManager.update(selectedUserWallet, walletData.currency)
Timber.e("Unable to refresh wallet details screen, no user wallet selected")
return@launch
}
walletCurrenciesManager.update(selectedUserWallet, currency)
.doOnResult { .doOnResult {
withMainContext { withMainContext {
binding.srlWalletDetails.isRefreshing = false binding.srlWalletDetails.isRefreshing = false
@ -266,9 +282,6 @@ class WalletDetailsFragment :
} }
} }
} }
binding.srlWalletDetails.isRefreshing = currencyData.status == BalanceStatus.Loading ||
currencyData.status == BalanceStatus.Refreshing
} }
private fun setupCopyAndShareButtons(walletAddress: String?) { private fun setupCopyAndShareButtons(walletAddress: String?) {
@ -284,7 +297,7 @@ class WalletDetailsFragment :
} }
} }
private fun setupButtonsRow(selectedWallet: WalletData, isExchangeServiceFeatureOn: Boolean) { private fun setupButtonsRow(selectedWallet: WalletDataModel, isExchangeServiceFeatureOn: Boolean) {
val exchangeManager = store.state.globalState.exchangeManager val exchangeManager = store.state.globalState.exchangeManager
binding.rowButtons.apply { binding.rowButtons.apply {
onBuyClick = { store.dispatch(WalletAction.TradeCryptoAction.Buy()) } onBuyClick = { store.dispatch(WalletAction.TradeCryptoAction.Buy()) }
@ -340,7 +353,7 @@ class WalletDetailsFragment :
private fun setupAddressCard( private fun setupAddressCard(
shouldShowMultipleAddress: Boolean, shouldShowMultipleAddress: Boolean,
selectedAddress: AddressData?, selectedAddress: WalletDataModel.AddressData?,
currency: Currency, currency: Currency,
) = with(binding.lWalletDetails) { ) = with(binding.lWalletDetails) {
if (selectedAddress == null) return@with if (selectedAddress == null) return@with
@ -371,7 +384,7 @@ class WalletDetailsFragment :
private fun setupAddressTypeChips( private fun setupAddressTypeChips(
shouldShowMultipleAddress: Boolean, shouldShowMultipleAddress: Boolean,
selectedAddress: AddressData, selectedAddress: WalletDataModel.AddressData,
currency: Currency, currency: Currency,
) = with(binding.lWalletDetails) { ) = with(binding.lWalletDetails) {
if (shouldShowMultipleAddress && currency is Currency.Blockchain) { if (shouldShowMultipleAddress && currency is Currency.Blockchain) {
@ -408,54 +421,58 @@ class WalletDetailsFragment :
} }
} }
private fun setupBalanceData(data: BalanceWidgetData) = with(binding.lWalletDetails) { private fun setupBalanceData(walletData: WalletDataModel) = with(binding.lWalletDetails) {
when (data.status) { when (val status = walletData.status) {
BalanceStatus.Loading -> { is WalletDataModel.Loading -> {
lBalanceError.root.hide() lBalanceError.root.hide()
lBalance.root.show() lBalance.root.show()
lBalance.groupBalance.show() lBalance.groupBalance.show()
lBalance.tvError.hide() lBalance.tvError.hide()
lBalance.tvAmount.text = data.amountFormatted lBalance.tvAmount.text = walletData.getFormattedAmount()
lBalance.tvFiatAmount.text = data.fiatAmountFormatted ?: UNKNOWN_AMOUNT_SIGN lBalance.tvFiatAmount.text = walletData.getFormattedFiatAmount(store.state.globalState.appCurrency)
lBalance.tvStatus.setLoadingStatus(R.string.wallet_balance_loading) lBalance.tvStatus.setLoadingStatus(R.string.wallet_balance_loading)
} }
BalanceStatus.VerifiedOnline, BalanceStatus.SameCurrencyTransactionInProgress, is WalletDataModel.VerifiedOnline,
BalanceStatus.TransactionInProgress, is WalletDataModel.SameCurrencyTransactionInProgress,
is WalletDataModel.TransactionInProgress,
-> { -> {
lBalanceError.root.hide() lBalanceError.root.hide()
lBalance.root.show() lBalance.root.show()
lBalance.groupBalance.show() lBalance.groupBalance.show()
lBalance.tvError.hide() lBalance.tvError.hide()
lBalance.tvAmount.text = data.amountFormatted lBalance.tvAmount.text = walletData.getFormattedAmount()
lBalance.tvFiatAmount.text = data.fiatAmountFormatted ?: UNKNOWN_AMOUNT_SIGN lBalance.tvFiatAmount.text = walletData.getFormattedFiatAmount(store.state.globalState.appCurrency)
when (data.status) { when (status) {
BalanceStatus.VerifiedOnline, BalanceStatus.SameCurrencyTransactionInProgress -> { is WalletDataModel.VerifiedOnline,
is WalletDataModel.SameCurrencyTransactionInProgress,
-> {
lBalance.tvStatus.setVerifiedBalanceStatus(R.string.wallet_balance_verified) lBalance.tvStatus.setVerifiedBalanceStatus(R.string.wallet_balance_verified)
} }
else -> { else -> {
lBalance.tvStatus.setWarningStatus(R.string.wallet_balance_tx_in_progress) lBalance.tvStatus.setWarningStatus(R.string.wallet_balance_tx_in_progress)
} }
} }
showPendingTransactionsIfPresent(status.pendingTransactions)
} }
BalanceStatus.Unreachable -> { is WalletDataModel.Unreachable -> {
lBalanceError.root.hide() lBalanceError.root.hide()
lBalance.root.show() lBalance.root.show()
lBalance.groupBalance.hide() lBalance.groupBalance.hide()
lBalance.tvError.show() lBalance.tvError.show()
lBalance.tvError.setWarningStatus( lBalance.tvError.setWarningStatus(
R.string.wallet_balance_blockchain_unreachable, R.string.wallet_balance_blockchain_unreachable,
data.errorMessage, status.errorMessage,
) )
} }
BalanceStatus.NoAccount -> { is WalletDataModel.NoAccount -> {
lBalance.root.hide() lBalance.root.hide()
lBalanceError.root.show() lBalanceError.root.show()
lBalanceError.tvErrorTitle.text = getText(R.string.wallet_error_no_account) lBalanceError.tvErrorTitle.text = getText(R.string.wallet_error_no_account)
lBalanceError.tvErrorDescriptions.text = lBalanceError.tvErrorDescriptions.text =
getString( getString(
R.string.no_account_generic, R.string.no_account_generic,
data.amountToCreateAccount, status.amountToCreateAccount,
data.currencySymbol, walletData.currency.currencySymbol,
) )
} }
else -> {} else -> {}

View file

@ -68,7 +68,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
private val totalBalanceWatcher = modelWatcher { private val totalBalanceWatcher = modelWatcher {
(WalletState::totalBalance) { totalBalance -> (WalletState::totalBalance) { totalBalance ->
totalBalance?.state?.let { totalBalance?.let {
viewModel.onBalanceLoaded(totalBalance) viewModel.onBalanceLoaded(totalBalance)
store.state.globalState.topUpController?.totalBalanceStateChanged(it) store.state.globalState.topUpController?.totalBalanceStateChanged(it)
} }
@ -159,8 +159,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
walletView = SaltPayWalletView() walletView = SaltPayWalletView()
walletView.changeWalletView(this, binding) walletView.changeWalletView(this, binding)
} }
state.isMultiwalletAllowed && state.primaryWalletData?.currencyData?.status != BalanceStatus.EmptyCard && state.isMultiwalletAllowed && walletView !is MultiWalletView -> {
walletView !is MultiWalletView -> {
walletView.onViewDestroy() walletView.onViewDestroy()
walletView = MultiWalletView() walletView = MultiWalletView()
walletView.changeWalletView(this, binding) walletView.changeWalletView(this, binding)

View file

@ -9,8 +9,8 @@ import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter
import com.tangem.tap.common.analytics.events.Basic import com.tangem.tap.common.analytics.events.Basic
import com.tangem.tap.common.analytics.events.MainScreen import com.tangem.tap.common.analytics.events.MainScreen
import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.domain.model.TotalFiatBalance
import com.tangem.tap.domain.userWalletList.UserWalletsListManager import com.tangem.tap.domain.userWalletList.UserWalletsListManager
import com.tangem.tap.features.wallet.models.TotalBalance
import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.ui.analytics.WalletAnalyticsEventsMapper import com.tangem.tap.features.wallet.ui.analytics.WalletAnalyticsEventsMapper
import com.tangem.tap.store import com.tangem.tap.store
@ -88,7 +88,7 @@ internal class WalletViewModel @Inject constructor(
bootstrapShowSaveWalletIfNeeded() bootstrapShowSaveWalletIfNeeded()
} }
fun onBalanceLoaded(totalBalance: TotalBalance?) { fun onBalanceLoaded(totalBalance: TotalFiatBalance?) {
if (totalBalance != null) { if (totalBalance != null) {
walletAnalyticsEventsMapper.convert(totalBalance)?.let { balanceParam -> walletAnalyticsEventsMapper.convert(totalBalance)?.let { balanceParam ->
analyticsEventHandler.send( analyticsEventHandler.send(

View file

@ -41,8 +41,8 @@ class WalletWarningConverter(
is WalletWarning.Rent -> { is WalletWarning.Rent -> {
context.getString( context.getString(
R.string.solana_rent_warning, R.string.solana_rent_warning,
message.walletRent.minRentValue, message.walletRent.rent,
message.walletRent.rentExemptValue, message.walletRent.exemptionAmount,
) )
} }
} }

View file

@ -12,18 +12,20 @@ import com.tangem.tap.common.analytics.events.Portfolio
import com.tangem.tap.common.extensions.getString import com.tangem.tap.common.extensions.getString
import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show import com.tangem.tap.common.extensions.show
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletData import com.tangem.tap.features.wallet.ui.utils.getFormattedAmount
import com.tangem.tap.features.wallet.ui.BalanceStatus import com.tangem.tap.features.wallet.ui.utils.getFormattedFiatAmount
import com.tangem.tap.features.wallet.ui.utils.getFormattedFiatRate
import com.tangem.tap.features.wallet.ui.images.load import com.tangem.tap.features.wallet.ui.images.load
import com.tangem.tap.store import com.tangem.tap.store
import com.tangem.wallet.R import com.tangem.wallet.R
import com.tangem.wallet.databinding.ItemCurrencyWalletBinding import com.tangem.wallet.databinding.ItemCurrencyWalletBinding
class WalletAdapter : ListAdapter<WalletData, WalletAdapter.WalletsViewHolder>(DiffUtilCallback) { class WalletAdapter : ListAdapter<WalletDataModel, WalletAdapter.WalletsViewHolder>(DiffUtilCallback) {
override fun getItemId(position: Int): Long { override fun getItemId(position: Int): Long {
return currentList[position].currencyData.currencySymbol?.hashCode()?.toLong() ?: 0 return currentList[position].currency.currencySymbol.hashCode().toLong()
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WalletsViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WalletsViewHolder {
@ -39,34 +41,33 @@ class WalletAdapter : ListAdapter<WalletData, WalletAdapter.WalletsViewHolder>(D
holder.bind(currentList[position]) holder.bind(currentList[position])
} }
object DiffUtilCallback : DiffUtil.ItemCallback<WalletData>() { object DiffUtilCallback : DiffUtil.ItemCallback<WalletDataModel>() {
override fun areContentsTheSame(oldItem: WalletData, newItem: WalletData) = oldItem == newItem override fun areContentsTheSame(oldItem: WalletDataModel, newItem: WalletDataModel) = oldItem == newItem
override fun areItemsTheSame(oldItem: WalletData, newItem: WalletData) = oldItem == newItem override fun areItemsTheSame(oldItem: WalletDataModel, newItem: WalletDataModel) = oldItem == newItem
} }
class WalletsViewHolder(val binding: ItemCurrencyWalletBinding) : class WalletsViewHolder(val binding: ItemCurrencyWalletBinding) :
RecyclerView.ViewHolder(binding.root) { RecyclerView.ViewHolder(binding.root) {
fun bind(wallet: WalletData) = with(binding) { fun bind(wallet: WalletDataModel) = with(binding) {
val status = wallet.currencyData.status val status = wallet.status
// Skip changes when on refreshing status val fiatCurrency = store.state.globalState.appCurrency
if (status == BalanceStatus.Refreshing) return@with
val statusMessage = when (status) { val statusMessage = when (status) {
BalanceStatus.TransactionInProgress -> { is WalletDataModel.TransactionInProgress -> {
root.getString(R.string.wallet_balance_tx_in_progress) root.getString(R.string.wallet_balance_tx_in_progress)
} }
BalanceStatus.Unreachable -> { is WalletDataModel.Unreachable -> {
root.getString(R.string.wallet_balance_blockchain_unreachable) root.getString(R.string.wallet_balance_blockchain_unreachable)
} }
BalanceStatus.MissedDerivation -> { is WalletDataModel.MissedDerivation -> {
root.getString(R.string.wallet_balance_missing_derivation) root.getString(R.string.wallet_balance_missing_derivation)
} }
else -> null else -> null
} }
if (status == null || status == BalanceStatus.Loading) { if (status is WalletDataModel.Loading) {
lContent.root.hide() lContent.root.hide()
lShimmer.root.veil() lShimmer.root.veil()
} else { } else {
@ -82,24 +83,22 @@ class WalletAdapter : ListAdapter<WalletData, WalletAdapter.WalletsViewHolder>(D
?.derivationStyle, ?.derivationStyle,
) )
lContent.tvCurrency.text = wallet.currencyData.currency lContent.tvCurrency.text = wallet.currency.currencyName
lContent.tvAmountFiat.text = wallet.currencyData.fiatAmountFormatted ?: "" lContent.tvAmountFiat.text = wallet.getFormattedFiatAmount(fiatCurrency)
lContent.tvAmount.text = wallet.currencyData.amountFormatted ?: "" lContent.tvAmount.text = wallet.getFormattedAmount()
lContent.tvStatus.isVisible = statusMessage != null lContent.tvStatus.isVisible = statusMessage != null
lContent.tvStatus.text = statusMessage lContent.tvStatus.text = statusMessage
lContent.tvExchangeRate.isVisible = statusMessage == null lContent.tvExchangeRate.isVisible = statusMessage == null
lContent.tvExchangeRate.text = wallet.fiatRateString lContent.tvExchangeRate.text = wallet.getFormattedFiatRate(
?: root.getString(id = R.string.token_item_no_rate) fiatCurrency = fiatCurrency,
noRateValue = root.getString(id = R.string.token_item_no_rate),
)
if (wallet.walletAddresses != null) { cardWallet.setOnClickListener {
cardWallet.setOnClickListener { Analytics.send(Portfolio.TokenTapped())
Analytics.send(Portfolio.TokenTapped()) store.dispatch(WalletAction.MultiWallet.SelectWallet(wallet.currency))
store.dispatch(WalletAction.MultiWallet.SelectWallet(wallet.currency))
}
} else {
cardWallet.setOnClickListener(null)
} }
} }
} }

View file

@ -2,22 +2,16 @@ package com.tangem.tap.features.wallet.ui.analytics
import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.extensions.isGreaterThan import com.tangem.tap.common.extensions.isGreaterThan
import com.tangem.tap.features.wallet.models.TotalBalance import com.tangem.tap.domain.model.TotalFiatBalance
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.utils.converter.Converter import com.tangem.utils.converter.Converter
import java.math.BigDecimal import java.math.BigDecimal
class WalletAnalyticsEventsMapper : Converter<TotalBalance, AnalyticsParam.CardBalanceState?> { class WalletAnalyticsEventsMapper : Converter<TotalFiatBalance, AnalyticsParam.CardBalanceState?> {
override fun convert(value: TotalBalance): AnalyticsParam.CardBalanceState? { override fun convert(value: TotalFiatBalance): AnalyticsParam.CardBalanceState? {
return when (value.state) { return when (value) {
ProgressState.Done -> if (value.fiatAmount?.isGreaterThan(BigDecimal.ZERO) == true) { is TotalFiatBalance.Error -> {
AnalyticsParam.CardBalanceState.Full if (value.amount == null) {
} else {
AnalyticsParam.CardBalanceState.Empty
}
ProgressState.Error -> {
if (value.fiatAmount == null) {
// if fiatAmount is null while ProgressState.Error it means error occurs when loading blockchain // if fiatAmount is null while ProgressState.Error it means error occurs when loading blockchain
AnalyticsParam.CardBalanceState.BlockchainError AnalyticsParam.CardBalanceState.BlockchainError
} else { } else {
@ -25,7 +19,14 @@ class WalletAnalyticsEventsMapper : Converter<TotalBalance, AnalyticsParam.CardB
AnalyticsParam.CardBalanceState.CustomToken AnalyticsParam.CardBalanceState.CustomToken
} }
} }
else -> null is TotalFiatBalance.Loaded -> {
if (value.amount.isGreaterThan(BigDecimal.ZERO)) {
AnalyticsParam.CardBalanceState.Full
} else {
AnalyticsParam.CardBalanceState.Empty
}
}
is TotalFiatBalance.Loading -> null
} }
} }
} }

View file

@ -0,0 +1,144 @@
package com.tangem.tap.features.wallet.ui.utils
import com.tangem.blockchain.common.Blockchain
import com.tangem.common.extensions.isZero
import com.tangem.domain.common.extensions.toNetworkId
import com.tangem.feature.swap.api.SwapFeatureToggleManager
import com.tangem.feature.swap.domain.SwapInteractor
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.extensions.toFiatRateString
import com.tangem.tap.common.extensions.toFiatValue
import com.tangem.tap.common.extensions.toFormattedCurrencyString
import com.tangem.tap.common.extensions.toFormattedFiatValue
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.WalletStoreModel
import com.tangem.tap.features.wallet.models.WalletWarning
import com.tangem.tap.features.wallet.redux.WalletMainButton
import com.tangem.tap.features.wallet.redux.utils.UNKNOWN_AMOUNT_SIGN
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
import java.math.BigDecimal
internal val WalletDataModel.mainButton: WalletMainButton
get() = WalletMainButton.SendButton(
enabled = !status.amount.isZero() && status.pendingTransactions.isEmpty(),
)
internal fun WalletDataModel.getFormattedAmount(): String {
return status.amount.toFormattedCurrencyString(
decimals = currency.decimals,
currency = currency.currencySymbol,
)
}
internal fun WalletDataModel.getFormattedFiatAmount(
fiatCurrency: FiatCurrency,
unknownAmountSign: String = UNKNOWN_AMOUNT_SIGN,
): String {
return this.fiatRate?.let { status.amount.toFiatValue(it) }
?.takeIf { !status.isErrorStatus }
?.toFormattedFiatValue(fiatCurrency.symbol)
?: unknownAmountSign
}
internal fun WalletDataModel.getFormattedFiatRate(fiatCurrency: FiatCurrency, noRateValue: String): String {
return fiatRate?.toFiatRateString(fiatCurrency.symbol)
?: noRateValue
}
internal fun WalletDataModel.isAvailableToBuy(exchangeManager: CurrencyExchangeManager): Boolean {
return exchangeManager.availableForBuy(currency)
}
internal fun WalletDataModel.isAvailableToSell(exchangeManager: CurrencyExchangeManager): Boolean {
return exchangeManager.availableForSell(currency)
}
internal fun WalletDataModel.isAvailableToSwap(
swapFeatureToggleManager: SwapFeatureToggleManager,
swapInteractor: SwapInteractor,
): Boolean {
if (currency.blockchain.id == Blockchain.Optimism.id && !swapFeatureToggleManager.isOptimismSwapEnabled) {
return false
}
return swapInteractor.isAvailableToSwap(currency.blockchain.toNetworkId()) &&
!currency.isCustomCurrency(null)
}
internal fun WalletDataModel.getAvailableActions(
swapInteractor: SwapInteractor,
exchangeManager: CurrencyExchangeManager,
swapFeatureToggleManager: SwapFeatureToggleManager,
): Set<CurrencyAction> {
return setOfNotNull(
if (isAvailableToBuy(exchangeManager)) CurrencyAction.Buy else null,
if (isAvailableToSell(exchangeManager)) CurrencyAction.Sell else null,
if (isAvailableToSwap(swapFeatureToggleManager, swapInteractor)) CurrencyAction.Swap else null,
)
}
internal fun WalletDataModel.shouldShowMultipleAddress(): Boolean {
val listOfAddresses = walletAddresses?.list.orEmpty()
return listOfAddresses.size > 1
}
internal fun WalletDataModel.assembleWarnings(
blockchainAmount: BigDecimal,
blockchainWalletRent: WalletStoreModel.WalletRent?,
): List<WalletWarning> {
val walletWarnings = mutableListOf<WalletWarning>()
assembleNonTypedWarnings(walletWarnings, blockchainWalletRent)
assembleBlockchainWarnings(walletWarnings)
assembleTokenWarnings(walletWarnings, blockchainAmount)
return walletWarnings.sortedBy { it.showingPosition }
}
private fun WalletDataModel.assembleNonTypedWarnings(
walletWarnings: MutableList<WalletWarning>,
walletRent: WalletStoreModel.WalletRent?,
) {
if (this.status is WalletDataModel.SameCurrencyTransactionInProgress) {
walletWarnings.add(WalletWarning.TransactionInProgress(currency.currencyName))
}
if (walletRent != null) {
walletWarnings.add(WalletWarning.Rent(walletRent))
}
}
private fun WalletDataModel.assembleBlockchainWarnings(walletWarnings: MutableList<WalletWarning>) {
with(currency) {
if (!isBlockchain()) return
if (existentialDeposit != null) {
val warning = WalletWarning.ExistentialDeposit(
currencyName = currencyName,
edStringValueWithSymbol = "${existentialDeposit.toPlainString()} $currencySymbol",
)
walletWarnings.add(warning)
}
}
}
private fun WalletDataModel.assembleTokenWarnings(
walletWarnings: MutableList<WalletWarning>,
blockchainAmount: BigDecimal,
) {
if (!currency.isToken()) return
if (!this.isEmptyAmount && blockchainAmount.isZero()) {
walletWarnings.add(
WalletWarning.BalanceNotEnoughForFee(
currencyName = currency.currencyName,
blockchainFullName = currency.blockchain.fullName,
blockchainSymbol = currency.blockchain.currency,
),
)
}
}
private val WalletDataModel.isEmptyAmount: Boolean
get() = this.status.amount.isZero()
enum class CurrencyAction {
Buy, Sell, Swap
}

View file

@ -35,9 +35,8 @@ import com.tangem.core.ui.components.SpacerW4
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.extensions.formatWithSpaces import com.tangem.tap.common.extensions.formatWithSpaces
import com.tangem.tap.features.wallet.models.TotalBalance import com.tangem.tap.domain.model.TotalFiatBalance
import com.tangem.tap.features.wallet.redux.ProgressState import com.tangem.tap.features.wallet.redux.utils.UNKNOWN_AMOUNT_SIGN
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN
import com.tangem.wallet.R import com.tangem.wallet.R
import com.valentinilk.shimmer.shimmer import com.valentinilk.shimmer.shimmer
import java.math.BigDecimal import java.math.BigDecimal
@ -52,18 +51,25 @@ internal class TotalBalanceCard @JvmOverloads constructor(
) : AbstractComposeView(context, attrs, defStyleAttr) { ) : AbstractComposeView(context, attrs, defStyleAttr) {
private var state by mutableStateOf<TotalBalanceCardState>(TotalBalanceCardState.Empty) private var state by mutableStateOf<TotalBalanceCardState>(TotalBalanceCardState.Empty)
var status: TotalBalance? = null var status: TotalFiatBalance? = null
set(value) { set(value) {
if (field == value) return if (field == value) return
field = value field = value
updateState(value, onChangeFiatCurrencyClick) updateState(value, fiatCurrency, onChangeFiatCurrencyClick)
} }
var onChangeFiatCurrencyClick: () -> Unit = { /* no-op */ } var onChangeFiatCurrencyClick: () -> Unit = { /* no-op */ }
set(value) { set(value) {
if (field == value) return if (field == value) return
field = value field = value
updateState(status, value) updateState(status, fiatCurrency, value)
}
var fiatCurrency: FiatCurrency = FiatCurrency.Default
set(value) {
if (field == value) return
field = value
updateState(status, value, onChangeFiatCurrencyClick)
} }
@Composable @Composable
@ -77,23 +83,21 @@ internal class TotalBalanceCard @JvmOverloads constructor(
return javaClass.name return javaClass.name
} }
private fun updateState(status: TotalBalance?, onChangeCurrencyClick: () -> Unit) { private fun updateState(status: TotalFiatBalance?, fiatCurrency: FiatCurrency, onChangeCurrencyClick: () -> Unit) {
state = when (status?.state) { state = when (status) {
null -> TotalBalanceCardState.Empty null -> TotalBalanceCardState.Empty
ProgressState.Loading -> TotalBalanceCardState.Loading( is TotalFiatBalance.Error -> TotalBalanceCardState.Failure(
fiatCurrency = status.fiatCurrency, amount = status.amount,
fiatCurrency = fiatCurrency,
onChangeFiatCurrencyClick = onChangeCurrencyClick, onChangeFiatCurrencyClick = onChangeCurrencyClick,
) )
ProgressState.Error -> TotalBalanceCardState.Failure( is TotalFiatBalance.Loading -> TotalBalanceCardState.Loading(
amount = status.fiatAmount, fiatCurrency = fiatCurrency,
fiatCurrency = status.fiatCurrency,
onChangeFiatCurrencyClick = onChangeCurrencyClick, onChangeFiatCurrencyClick = onChangeCurrencyClick,
) )
ProgressState.Refreshing, is TotalFiatBalance.Loaded -> TotalBalanceCardState.Success(
ProgressState.Done, amount = status.amount,
-> TotalBalanceCardState.Success( fiatCurrency = fiatCurrency,
amount = status.fiatAmount ?: BigDecimal.ZERO,
fiatCurrency = status.fiatCurrency,
onChangeFiatCurrencyClick = onChangeCurrencyClick, onChangeFiatCurrencyClick = onChangeCurrencyClick,
) )
} }

View file

@ -9,7 +9,7 @@ import androidx.core.view.isVisible
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show import com.tangem.tap.common.extensions.show
import com.tangem.tap.features.wallet.redux.CurrencyAction import com.tangem.tap.features.wallet.ui.utils.CurrencyAction
import com.tangem.wallet.databinding.ViewWalletDetailsButtonsRowBinding import com.tangem.wallet.databinding.ViewWalletDetailsButtonsRowBinding
internal class WalletDetailsButtonsRow @JvmOverloads constructor( internal class WalletDetailsButtonsRow @JvmOverloads constructor(

View file

@ -1,6 +1,5 @@
package com.tangem.tap.features.wallet.ui.wallet package com.tangem.tap.features.wallet.ui.wallet
import android.widget.Button
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.badoo.mvicore.modelWatcher import com.badoo.mvicore.modelWatcher
@ -14,14 +13,13 @@ import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.model.TotalFiatBalance
import com.tangem.tap.features.tokens.redux.TokensAction import com.tangem.tap.features.tokens.redux.TokensAction
import com.tangem.tap.features.wallet.models.TotalBalance import com.tangem.tap.features.wallet.redux.ErrorType
import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.ui.BalanceStatus
import com.tangem.tap.features.wallet.ui.WalletFragment import com.tangem.tap.features.wallet.ui.WalletFragment
import com.tangem.tap.features.wallet.ui.adapters.WalletAdapter import com.tangem.tap.features.wallet.ui.adapters.WalletAdapter
import com.tangem.tap.features.wallet.ui.view.WalletDetailsButtonsRow
import com.tangem.tap.store import com.tangem.tap.store
import com.tangem.wallet.R import com.tangem.wallet.R
import com.tangem.wallet.databinding.FragmentWalletBinding import com.tangem.wallet.databinding.FragmentWalletBinding
@ -151,35 +149,29 @@ class MultiWalletView : WalletView() {
} }
} }
private fun handleTotalBalance(binding: FragmentWalletBinding, totalBalance: TotalBalance?, walletsCount: Int) = private fun handleTotalBalance(
with(binding.lCardTotalBalance) { binding: FragmentWalletBinding,
isVisible = walletsCount > 0 totalBalance: TotalFiatBalance?,
walletsCount: Int,
) = with(binding.lCardTotalBalance) {
isVisible = walletsCount > 0
onChangeFiatCurrencyClick = { onChangeFiatCurrencyClick = {
store.dispatch(WalletAction.AppCurrencyAction.ChooseAppCurrency) store.dispatch(WalletAction.AppCurrencyAction.ChooseAppCurrency)
}
status = totalBalance
} }
status = totalBalance
}
private fun handleErrorStates(state: WalletState, binding: FragmentWalletBinding, fragment: WalletFragment) { private fun handleErrorStates(state: WalletState, binding: FragmentWalletBinding, fragment: WalletFragment) {
when (state.primaryWalletData?.currencyData?.status) { when (state.error) {
BalanceStatus.EmptyCard -> { ErrorType.UnknownBlockchain -> {
showErrorState(
binding,
fragment.getText(R.string.wallet_error_empty_card),
fragment.getString(R.string.wallet_error_empty_card_subtitle),
)
configureButtonsForEmptyWalletState(binding)
}
BalanceStatus.UnknownBlockchain -> {
showErrorState( showErrorState(
binding, binding,
fragment.getText(R.string.wallet_error_unsupported_blockchain), fragment.getText(R.string.wallet_error_unsupported_blockchain),
fragment.getString(R.string.wallet_error_unsupported_blockchain_subtitle), fragment.getString(R.string.wallet_error_unsupported_blockchain_subtitle),
) )
} }
else -> { /* no-op */ else -> { /* no-op */ }
}
} }
} }
@ -199,27 +191,8 @@ class MultiWalletView : WalletView() {
} }
} }
private fun configureButtonsForEmptyWalletState(binding: FragmentWalletBinding) = with(binding) {
rowButtons.btnBuy.hide()
rowButtons.btnSell.hide()
rowButtons.btnTrade.hide()
rowButtons.show()
rowButtons.btnSend.text = fragment?.getText(R.string.wallet_button_create_wallet)
rowButtons.onSendClick = { store.dispatch(WalletAction.CreateWallet) }
}
override fun onDestroyFragment() { override fun onDestroyFragment() {
super.onDestroyFragment() super.onDestroyFragment()
watcher.clear() watcher.clear()
} }
} }
private val WalletDetailsButtonsRow.btnBuy: Button
get() = this.findViewById(R.id.btn_buy)
private val WalletDetailsButtonsRow.btnSell: Button
get() = this.findViewById(R.id.btn_sell)
private val WalletDetailsButtonsRow.btnTrade: Button
get() = this.findViewById(R.id.btn_trade)
private val WalletDetailsButtonsRow.btnSend: Button
get() = this.findViewById(R.id.btn_send)

View file

@ -11,14 +11,19 @@ import com.tangem.tap.common.extensions.getQuantityString
import com.tangem.tap.common.extensions.getString import com.tangem.tap.common.extensions.getString
import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show import com.tangem.tap.common.extensions.show
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.models.PendingTransaction import com.tangem.tap.features.wallet.models.PendingTransaction
import com.tangem.tap.features.wallet.models.PendingTransactionType import com.tangem.tap.features.wallet.models.PendingTransactionType
import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletData
import com.tangem.tap.features.wallet.redux.WalletMainButton import com.tangem.tap.features.wallet.redux.WalletMainButton
import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.ui.utils.getAvailableActions
import com.tangem.tap.features.wallet.ui.utils.isAvailableToBuy
import com.tangem.tap.features.wallet.ui.utils.isAvailableToSell
import com.tangem.tap.features.wallet.ui.utils.mainButton
import com.tangem.tap.features.wallet.ui.utils.shouldShowMultipleAddress
import com.tangem.tap.features.wallet.ui.BalanceWidget import com.tangem.tap.features.wallet.ui.BalanceWidget
import com.tangem.tap.features.wallet.ui.MultipleAddressUiHelper import com.tangem.tap.features.wallet.ui.MultipleAddressUiHelper
import com.tangem.tap.features.wallet.ui.WalletFragment import com.tangem.tap.features.wallet.ui.WalletFragment
@ -71,7 +76,7 @@ class SingleWalletView : WalletView() {
setupTwinCards(state.twinCardsState, binding) setupTwinCards(state.twinCardsState, binding)
setupButtons(primaryWalletData, binding, state.isExchangeServiceFeatureOn) setupButtons(primaryWalletData, binding, state.isExchangeServiceFeatureOn)
setupAddressCard(state, binding) setupAddressCard(state, binding)
showPendingTransactionsIfPresent(primaryWalletData.pendingTransactions) showPendingTransactionsIfPresent(primaryWalletData.status.pendingTransactions)
setupBalance(state, primaryWalletData) setupBalance(state, primaryWalletData)
} }
@ -83,32 +88,30 @@ class SingleWalletView : WalletView() {
binding?.rvPendingTransaction?.show(knownTransactions.isNotEmpty()) binding?.rvPendingTransaction?.show(knownTransactions.isNotEmpty())
} }
private fun setupBalance(state: WalletState, primaryWallet: WalletData) { private fun setupBalance(state: WalletState, primaryWallet: WalletDataModel) {
val fragment = fragment ?: return val fragment = fragment ?: return
binding?.apply { binding?.apply {
lCardBalance.lBalance.root.show() lCardBalance.lBalance.root.show()
BalanceWidget( BalanceWidget(
binding = this.lCardBalance, binding = this.lCardBalance,
fragment = fragment, fragment = fragment,
data = primaryWallet.currencyData, blockchainWalletData = primaryWallet,
token = state.primaryTokenData?.currencyData, tokenWalletData = state.primaryTokenData,
isTwinCard = state.isTangemTwins,
).setup() ).setup()
} }
} }
private fun setupTwinCards(twinCardsState: TwinCardsState?, binding: FragmentWalletBinding) = with(binding) { private fun setupTwinCards(twinCardsState: TwinCardsState?, binding: FragmentWalletBinding) = with(binding) {
twinCardsState?.cardNumber?.let { cardNumber ->
tvTwinCardNumber.show()
tvTwinCardNumber.text = tvTwinCardNumber.getQuantityString(R.plurals.card_label_card_count, 2)
}
if (twinCardsState?.cardNumber == null) { if (twinCardsState?.cardNumber == null) {
tvTwinCardNumber.hide() tvTwinCardNumber.hide()
} else {
tvTwinCardNumber.show()
tvTwinCardNumber.text = tvTwinCardNumber.getQuantityString(R.plurals.card_label_card_count, 2)
} }
} }
private fun setupButtons( private fun setupButtons(
walletData: WalletData, walletData: WalletDataModel,
binding: FragmentWalletBinding, binding: FragmentWalletBinding,
isExchangeServiceFeatureEnabled: Boolean, isExchangeServiceFeatureEnabled: Boolean,
) = with(binding) { ) = with(binding) {
@ -134,7 +137,7 @@ class SingleWalletView : WalletView() {
} }
private fun setupRowButtons( private fun setupRowButtons(
walletData: WalletData, walletData: WalletDataModel,
rowButtons: WalletDetailsButtonsRow, rowButtons: WalletDetailsButtonsRow,
isExchangeServiceFeatureEnabled: Boolean, isExchangeServiceFeatureEnabled: Boolean,
) { ) {

View file

@ -8,13 +8,15 @@ import com.tangem.tap.common.ShimmerData
import com.tangem.tap.common.ShimmerRecyclerAdapter import com.tangem.tap.common.ShimmerRecyclerAdapter
import com.tangem.tap.common.analytics.events.MainScreen import com.tangem.tap.common.analytics.events.MainScreen
import com.tangem.tap.common.extensions.animateVisibility import com.tangem.tap.common.extensions.animateVisibility
import com.tangem.tap.common.extensions.formatAmountAsSpannedString
import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.recyclerView.SpaceItemDecoration import com.tangem.tap.common.recyclerView.SpaceItemDecoration
import com.tangem.tap.features.wallet.redux.ProgressState import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.ui.utils.getFormattedAmount
import com.tangem.tap.features.wallet.ui.utils.getFormattedFiatAmount
import com.tangem.tap.features.wallet.ui.utils.isAvailableToBuy
import com.tangem.tap.features.wallet.ui.WalletFragment import com.tangem.tap.features.wallet.ui.WalletFragment
import com.tangem.tap.features.wallet.ui.wallet.WalletView import com.tangem.tap.features.wallet.ui.wallet.WalletView
import com.tangem.tap.features.wallet.ui.wallet.saltPay.rv.HistoryItemData import com.tangem.tap.features.wallet.ui.wallet.saltPay.rv.HistoryItemData
@ -124,16 +126,14 @@ class SaltPayWalletView : WalletView() {
tvUnreachable.animateVisibility(show = mainProgressState == ProgressState.Error) tvUnreachable.animateVisibility(show = mainProgressState == ProgressState.Error)
veilBalanceCrypto.animateVisibility(show = mainProgressState != ProgressState.Error) veilBalanceCrypto.animateVisibility(show = mainProgressState != ProgressState.Error)
if (tokenData.currencyData.fiatAmount == null) { if (tokenData.fiatRate == null) {
veilBalance.veil() veilBalance.veil()
} else { } else {
veilBalance.unVeil() veilBalance.unVeil()
tvBalance.text = tokenData.currencyData.fiatAmount.formatAmountAsSpannedString( tvBalance.text = tokenData.getFormattedFiatAmount(appCurrency)
currencySymbol = appCurrency.symbol,
)
} }
tvBalanceCrypto.text = tokenData.currencyData.amountFormatted tvBalanceCrypto.text = tokenData.getFormattedAmount()
tvCurrencyName.text = appCurrency.code tvCurrencyName.text = appCurrency.code
tvCurrencyName.setOnClickListener { tvCurrencyName.setOnClickListener {

View file

@ -3,7 +3,7 @@ package com.tangem.tap.features.walletSelector.ui
import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.extensions.toFormattedFiatValue import com.tangem.tap.common.extensions.toFormattedFiatValue
import com.tangem.tap.domain.model.TotalFiatBalance import com.tangem.tap.domain.model.TotalFiatBalance
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN 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.redux.UserWalletModel
import com.tangem.tap.features.walletSelector.ui.model.MultiCurrencyUserWalletItem import com.tangem.tap.features.walletSelector.ui.model.MultiCurrencyUserWalletItem
import com.tangem.tap.features.walletSelector.ui.model.SingleCurrencyUserWalletItem import com.tangem.tap.features.walletSelector.ui.model.SingleCurrencyUserWalletItem