From a2b3c22e5338b4cd7fb921be47ba5fb946e287bb Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 11 Apr 2023 12:42:05 +0300 Subject: [PATCH 1/2] Updated on 2026-08-14 --- .../common/analytics/topup/TopUpController.kt | 6 +- .../tap/common/extensions/WalletManager.kt | 10 +- .../tangem/tap/common/redux/StateDialog.kt | 4 +- .../model/builders/WalletStoreBuilder.kt | 4 - .../AddressInfoBottomSheetDialog.kt | 7 +- .../send/redux/reducers/ReceiptReducer.kt | 4 +- .../stateSubscribers/SendStateSubscriber.kt | 4 +- .../tap/features/tokens/redux/TokensAction.kt | 4 +- .../tap/features/tokens/redux/TokensState.kt | 20 +-- .../features/wallet/models/WalletWarning.kt | 4 +- .../tap/features/wallet/redux/WalletAction.kt | 12 +- .../tap/features/wallet/redux/WalletData.kt | 3 +- .../tap/features/wallet/redux/WalletState.kt | 125 ++++-------------- .../wallet/redux/middlewares/Mapper.kt | 2 +- .../redux/middlewares/WalletMiddleware.kt | 38 +----- .../wallet/redux/reducers/WalletReducer.kt | 57 ++------ .../features/wallet/redux/utils/Constants.kt | 5 + 17 files changed, 85 insertions(+), 224 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/features/wallet/redux/utils/Constants.kt diff --git a/app/src/main/java/com/tangem/tap/common/analytics/topup/TopUpController.kt b/app/src/main/java/com/tangem/tap/common/analytics/topup/TopUpController.kt index 42d9e80686..2435646fed 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/topup/TopUpController.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/topup/TopUpController.kt @@ -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.events.AnalyticsParam 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.WalletDataModel 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.walletStores.WalletStoresManager 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.scope import kotlinx.coroutines.launch @@ -60,8 +60,8 @@ class TopUpController( hadMissedDerivations = blockchains.isNotEmpty() } - fun totalBalanceStateChanged(state: ProgressState) { - if (state == ProgressState.Done) tryToSend() + fun totalBalanceStateChanged(totalFiatBalance: TotalFiatBalance) { + if (totalFiatBalance is TotalFiatBalance.Loaded) tryToSend() } fun loadDataSuccess() { diff --git a/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt b/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt index 0930704d50..925cb0c728 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt @@ -12,9 +12,8 @@ import com.tangem.tap.common.TestActions import com.tangem.tap.domain.TapError import com.tangem.tap.domain.extensions.amountToCreateAccount 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.wallet.models.Currency -import com.tangem.tap.features.wallet.redux.AddressData import com.tangem.tap.features.wallet.redux.reducers.createAddressesData import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager 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 addressDataList = wallet.createAddressesData() @@ -100,11 +99,6 @@ fun WalletManager.Companion.stub(): T { } as T } -fun Wallet.getTxHistory(currency: Currency): List { - return (currency as? Currency.Token)?.let { this.getTokenTxHistory(it.token) } - ?: getBlockchainTxHistory() -} - fun Wallet.getBlockchainTxHistory(): List { return historyTransactions.filter { it.contractAddress.isNullOrEmpty() diff --git a/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt b/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt index e216493dd6..ab957b81b1 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt @@ -2,8 +2,8 @@ package com.tangem.tap.common.redux import com.tangem.common.extensions.VoidCallback 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.redux.AddressData /** [REDACTED_AUTHOR] @@ -24,7 +24,7 @@ sealed class AppDialog : StateDialog { data class AddressInfoDialog( val currency: Currency, - val addressData: AddressData, + val addressData: WalletDataModel.AddressData, ) : AppDialog() data class TestActionsDialog( diff --git a/app/src/main/java/com/tangem/tap/domain/model/builders/WalletStoreBuilder.kt b/app/src/main/java/com/tangem/tap/domain/model/builders/WalletStoreBuilder.kt index ebc45e991e..65405fa242 100644 --- a/app/src/main/java/com/tangem/tap/domain/model/builders/WalletStoreBuilder.kt +++ b/app/src/main/java/com/tangem/tap/domain/model/builders/WalletStoreBuilder.kt @@ -184,10 +184,6 @@ private fun getExistentialDeposit(walletManager: WalletManager?): BigDecimal? { private fun Wallet.getWalletAddresses(): WalletDataModel.WalletAddresses? { return this.createAddressesData() .takeIf { it.isNotEmpty() } - ?.map { - // TODO: Will be removed in next MR - with(it) { WalletDataModel.AddressData(address, type, shareUrl, exploreUrl) } - } ?.let { addresses -> WalletDataModel.WalletAddresses( list = addresses, diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/AddressInfoBottomSheetDialog.kt b/app/src/main/java/com/tangem/tap/features/onboarding/AddressInfoBottomSheetDialog.kt index 66e30fe595..280ca66499 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/AddressInfoBottomSheetDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/AddressInfoBottomSheetDialog.kt @@ -12,8 +12,9 @@ import com.tangem.tap.common.extensions.dispatchDialogHide import com.tangem.tap.common.extensions.dispatchShare import com.tangem.tap.common.extensions.dispatchToastNotification 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.features.wallet.redux.AddressData +import com.tangem.tap.domain.model.WalletDataModel import com.tangem.tap.store import com.tangem.wallet.R import com.tangem.wallet.databinding.DialogOnboardingAddressInfoBinding @@ -46,12 +47,12 @@ class AddressInfoBottomSheetDialog( showData(data = stateDialog.addressData) } - private fun showData(data: AddressData) = with(binding!!) { + private fun showData(data: WalletDataModel.AddressData) = with(binding!!) { pseudoToolbar.imvClose.setOnClickListener { dismissWithAnimation = true cancel() } - imvQrCode.setImageBitmap(data.qrCode) + imvQrCode.setImageBitmap(data.shareUrl.toQrCode()) tvAddress.text = data.address btnFlCopyAddress.setOnClickListener { Analytics.send(Token.Receive.ButtonCopyAddress()) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt index 3f403ea92c..ebfa2a391a 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt @@ -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.ReceiptTokenFiat 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.WalletState.Companion.UNKNOWN_AMOUNT_SIGN +import com.tangem.tap.features.wallet.redux.utils.CAN_BE_LOWER_SIGN +import com.tangem.tap.features.wallet.redux.utils.UNKNOWN_AMOUNT_SIGN import com.tangem.tap.store import java.math.BigDecimal diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt index e250ad075e..6e2bf1f5e9 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt @@ -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.TezosWarningDialog 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.WalletState.Companion.UNKNOWN_AMOUNT_SIGN +import com.tangem.tap.features.wallet.redux.utils.ROUGH_SIGN +import com.tangem.tap.features.wallet.redux.utils.UNKNOWN_AMOUNT_SIGN import com.tangem.tap.features.wallet.ui.adapters.WarningMessagesAdapter import com.tangem.wallet.R diff --git a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensAction.kt b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensAction.kt index 78cfc5ac9e..0c87bc313a 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensAction.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensAction.kt @@ -3,8 +3,8 @@ package com.tangem.tap.features.tokens.redux import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.DerivationStyle import com.tangem.domain.common.ScanResponse +import com.tangem.tap.domain.model.WalletDataModel import com.tangem.tap.domain.tokens.Currency -import com.tangem.tap.features.wallet.redux.WalletData import org.rekotlin.Action sealed class TokensAction : Action { @@ -23,7 +23,7 @@ sealed class TokensAction : Action { ) : TokensAction() data class SetAddedCurrencies( - val wallets: List, + val wallets: List, val derivationStyle: DerivationStyle?, ) : TokensAction() diff --git a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensState.kt b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensState.kt index 3cb59514c8..8015b9d1d5 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensState.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensState.kt @@ -6,13 +6,13 @@ import com.tangem.blockchain.common.Token import com.tangem.domain.common.ScanResponse import com.tangem.domain.common.extensions.canHandleToken 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.features.wallet.redux.WalletData import org.rekotlin.StateType import com.tangem.tap.features.wallet.models.Currency.Token as CurrencyToken data class TokensState( - val addedWallets: List = emptyList(), + val addedWallets: List = emptyList(), val addedTokens: List = emptyList(), val addedBlockchains: List = emptyList(), val currencies: List = emptyList(), @@ -31,17 +31,9 @@ data class TokensState( typealias ContractAddress = String -fun List.toTokensContractAddresses(): List { - return mapNotNull { (it.currency as? CurrencyToken)?.token?.contractAddress }.distinct() -} - -fun List.toNonCustomTokens(derivationStyle: DerivationStyle?): List { - return filter { !it.currency.isCustomCurrency(derivationStyle) } - .mapNotNull { (it.currency as? CurrencyToken)?.token } - .distinct() -} - -fun List.toNonCustomTokensWithBlockchains(derivationStyle: DerivationStyle?): List { +fun List.toNonCustomTokensWithBlockchains( + derivationStyle: DerivationStyle?, +): List { return mapNotNull { if (it.currency !is CurrencyToken) return@mapNotNull null if (it.currency.isCustomCurrency(derivationStyle)) return@mapNotNull null @@ -49,7 +41,7 @@ fun List.toNonCustomTokensWithBlockchains(derivationStyle: Derivatio }.distinct() } -fun List.toNonCustomBlockchains(derivationStyle: DerivationStyle?): List { +fun List.toNonCustomBlockchains(derivationStyle: DerivationStyle?): List { return mapNotNull { if (it.currency.isCustomCurrency(derivationStyle)) { null diff --git a/app/src/main/java/com/tangem/tap/features/wallet/models/WalletWarning.kt b/app/src/main/java/com/tangem/tap/features/wallet/models/WalletWarning.kt index 951d4f1e10..421bfa85fe 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/models/WalletWarning.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/models/WalletWarning.kt @@ -1,5 +1,7 @@ package com.tangem.tap.features.wallet.models +import com.tangem.tap.domain.model.WalletStoreModel + sealed class WalletWarning(val showingPosition: Int) { data class ExistentialDeposit( @@ -15,7 +17,7 @@ sealed class WalletWarning(val showingPosition: Int) { val blockchainSymbol: String, ) : 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) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt index 390b337f30..c026cc9e0f 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt @@ -8,11 +8,12 @@ import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.common.redux.NotificationAction import com.tangem.tap.domain.TapError 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.features.wallet.models.Currency -import com.tangem.tap.features.wallet.models.TotalBalance import com.tangem.tap.features.wallet.redux.models.WalletDialog import com.tangem.wallet.R import org.rekotlin.Action @@ -38,7 +39,6 @@ sealed class WalletAction : Action { data class RemoveWallet(val currency: Currency) : MultiWallet() object BackupWallet : MultiWallet() - object ScheduleCheckForMissingDerivation : MultiWallet() data class AddMissingDerivations(val blockchains: List) : MultiWallet() object ScanToGetDerivations : MultiWallet() } @@ -78,7 +78,7 @@ sealed class WalletAction : Action { sealed class DialogAction : WalletAction() { data class QrCode( val currency: Currency, - val selectedAddress: AddressData, + val selectedAddress: WalletDataModel.AddressData, ) : DialogAction() object SignedHashesMultiWalletDialog : DialogAction() @@ -127,9 +127,7 @@ sealed class WalletAction : Action { } data class UserWalletChanged(val userWallet: UserWallet) : WalletAction() - data class WalletStoresChanged(val walletStores: List) : WalletAction() { - data class UpdateWalletStores(val reduxWalletStores: List) : WalletAction() - } + data class WalletStoresChanged(val walletStores: List) : WalletAction() - data class TotalFiatBalanceChanged(val balance: TotalBalance) : WalletAction() + data class TotalFiatBalanceChanged(val balance: TotalFiatBalance) : WalletAction() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletData.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletData.kt index e4e5f8e78c..423306eec6 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletData.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletData.kt @@ -85,7 +85,8 @@ data class WalletData( walletWarnings.add(WalletWarning.TransactionInProgress(currency.currencyName)) } if (walletRent != null) { - walletWarnings.add(WalletWarning.Rent(walletRent)) + // TODO: Will be removed in next MR + // walletWarnings.add(WalletWarning.Rent(walletRent)) } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt index b55d54277d..6d4a5c788a 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt @@ -9,29 +9,30 @@ import com.tangem.tap.common.extensions.toQrCode import com.tangem.tap.common.redux.global.CryptoCurrencyName import com.tangem.tap.common.toggleWidget.WidgetState 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.features.onboarding.products.twins.redux.TwinCardsState 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 org.rekotlin.StateType import kotlin.properties.ReadOnlyProperty data class WalletState( - val cardId: String = "", + val userWallet: UserWallet? = null, val state: ProgressState = ProgressState.Done, val error: ErrorType? = null, val cardImage: Artwork? = null, val hashesCountVerified: Boolean? = null, val mainWarningsList: List = mutableListOf(), - val walletsStores: List = listOf(), + val walletsStores: List = listOf(), val isMultiwalletAllowed: Boolean = false, val cardCurrency: CryptoCurrencyName? = null, val selectedCurrency: Currency? = null, val isTestnet: Boolean = false, - val totalBalance: TotalBalance? = null, + val totalBalance: TotalFiatBalance? = null, val showBackupWarning: Boolean = false, val missingDerivations: List = emptyList(), val loadingUserTokens: Boolean = false, @@ -39,10 +40,10 @@ data class WalletState( val canSaveUserWallets: Boolean = false, ) : StateType { - val walletsDataFromStores: List - get() = walletsStores.map { it.walletsData }.flatten() + val walletsDataFromStores: List + get() = walletsStores.flatMap { it.walletsData } - val selectedWalletData: WalletData? + val selectedWalletData: WalletDataModel? get() = walletsDataFromStores.firstOrNull { it.currency == selectedCurrency } // if you do not delegate - the application crashes on startup, @@ -66,7 +67,7 @@ data class WalletState( val walletManagers: List get() = walletsStores.mapNotNull { it.walletManager } - private val primaryWalletStore: WalletStore? + private val primaryWalletStore: WalletStoreModel? get() = if (isMultiwalletAllowed || walletsStores.isEmpty() || walletsStores.size > 1) { null } else { @@ -76,17 +77,15 @@ data class WalletState( val primaryWalletManager: WalletManager? get() = primaryWalletStore?.walletManager - val primaryWalletData: WalletData? - get() = primaryWalletStore?.walletsData?.firstOrNull() + val primaryWalletData: WalletDataModel? + get() = primaryWalletStore?.blockchainWalletData - val primaryTokenData: WalletData? - get() = primaryWalletStore?.walletsData?.toMutableList() - ?.apply { remove(primaryWalletData) } - ?.firstOrNull() + val primaryTokenData: WalletDataModel? + get() = primaryWalletStore?.walletsData + ?.firstOrNull { it.currency !is Currency.Blockchain } val shouldShowDetails: Boolean = - primaryWalletData?.currencyData?.status != BalanceStatus.EmptyCard && - primaryWalletData?.currencyData?.status != BalanceStatus.UnknownBlockchain + primaryWalletData?.status !is WalletDataModel.Unreachable fun getWalletManager(currency: Currency?): WalletManager? { if (currency?.blockchain == null) return null @@ -94,95 +93,27 @@ data class WalletState( } 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? { - 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? { + fun getWalletStore(currency: Currency?): WalletStoreModel? { if (currency == null) return null return walletsStores.firstOrNull { - it.blockchainNetwork.derivationPath == currency.derivationPath && - it.blockchainNetwork.blockchain == currency.blockchain + it.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): 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): 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 ErrorType { NoInternetConnection } +enum class ErrorType { + NoInternetConnection, + UnknownBlockchain, +} sealed class WalletMainButton(enabled: Boolean) : Button(enabled) { class SendButton(enabled: Boolean) : WalletMainButton(enabled) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/Mapper.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/Mapper.kt index cd9ac58574..e48f89ce52 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/Mapper.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/Mapper.kt @@ -77,7 +77,7 @@ private fun WalletDataModel.mapToReduxModel( return WalletData( currency = currency, - // TODO: Will be updated in next MR + // TODO: Will be updated in next MRs walletAddresses = walletAddresses?.let { addresses -> WalletAddresses( selectedAddress = with(addresses.selectedAddress) { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt index 7893c448c5..404aa63a43 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt @@ -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.getSendableAmounts 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.WalletStore -import com.tangem.tap.features.wallet.redux.reducers.findSelectedCurrency import com.tangem.tap.preferencesStorage import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.scope @@ -106,7 +103,7 @@ class WalletMiddleware { scope.launch { when (val result = tangemSdkManager.createWallet(globalState.scanResponse?.card?.cardId)) { is CompletionResult.Success -> { - val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard { + val selectedUserWallet = walletState.userWallet.guard { Timber.e("Unable to create wallet, no user wallet selected") return@launch } @@ -132,7 +129,7 @@ class WalletMiddleware { is WalletAction.LoadData, 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") return } @@ -188,7 +185,6 @@ class WalletMiddleware { is WalletAction.WalletStoresChanged -> { // Cancel update job when new wallet stores received updateWalletStoresJob = scope.launch(Dispatchers.Default) { - ifActive { updateWalletStores(action.walletStores, walletState) } ifActive { fetchTotalFiatBalance(action.walletStores) } ifActive { findMissedDerivations(action.walletStores) } ifActive { tryToShowAppRatingWarning(action.walletStores) } @@ -227,26 +223,8 @@ class WalletMiddleware { } } - private fun updateWalletStores(walletsStores: List, 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) { - val totalFiatBalance = totalFiatBalanceCalculator.calculateOrNull(walletStores)?.mapToReduxModel() + val totalFiatBalance = totalFiatBalanceCalculator.calculateOrNull(walletStores) if (totalFiatBalance != null) { store.dispatchOnMain(WalletAction.TotalFiatBalanceChanged(totalFiatBalance)) @@ -300,7 +278,7 @@ class WalletMiddleware { return if (amount != null) { if (amount.type is AmountType.Token) { - prepareSendActionForToken(amount, state, selectedWalletData, walletStore) + prepareSendActionForToken(amount, selectedWalletData, walletStore) } else { PrepareSendScreen(amount, selectedWalletData?.fiatRate, walletStore?.walletManager) } @@ -322,7 +300,6 @@ class WalletMiddleware { ?: return WalletAction.DialogAction.ChooseCurrency(amounts) prepareSendActionForToken( amount = amountToSend, - state = state, selectedWalletData = selectedWalletData, walletStore = walletStore, ) @@ -346,11 +323,10 @@ class WalletMiddleware { private fun prepareSendActionForToken( amount: Amount, - state: WalletState?, - selectedWalletData: WalletData?, - walletStore: WalletStore?, + selectedWalletData: WalletDataModel?, + walletStore: WalletStoreModel?, ): PrepareSendScreen { - val coinRate = state?.getWalletData(walletStore?.blockchainNetwork)?.fiatRate + val coinRate = walletStore?.blockchainWalletData?.fiatRate val tokenRate = selectedWalletData?.fiatRate val coinAmount = walletStore?.walletManager?.wallet?.amounts?.get(AmountType.Coin) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt index 49c0035b5a..b1d7fcfef0 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt @@ -1,23 +1,18 @@ package com.tangem.tap.features.wallet.redux.reducers -import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Wallet import com.tangem.domain.common.CardDTO import com.tangem.domain.common.TapWorkarounds.isTestCard import com.tangem.tap.common.redux.AppState 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.redux.AddressData import com.tangem.tap.features.wallet.redux.Artwork import com.tangem.tap.features.wallet.redux.ErrorType import com.tangem.tap.features.wallet.redux.ProgressState 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.WalletStore -import com.tangem.tap.features.wallet.ui.BalanceStatus -import com.tangem.tap.features.wallet.ui.BalanceWidgetData import com.tangem.tap.proxy.AppStateHolder import org.rekotlin.Action @@ -41,44 +36,15 @@ private fun internalReduce(action: Action, state: AppState, appStateHolder: AppS is WalletAction.LoadData.Failure -> { when (action.error) { 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( state = ProgressState.Error, error = ErrorType.NoInternetConnection, - walletsStores = wallets, ) } is TapError.UnknownBlockchain -> { newState = newState.copy( - state = ProgressState.Done, - walletsStores = listOf( - WalletStore( - walletManager = null, - blockchainNetwork = BlockchainNetwork( - Blockchain.Unknown, - null, - emptyList(), - ), - walletsData = listOf( - WalletData( - currencyData = BalanceWidgetData(BalanceStatus.UnknownBlockchain), - currency = Currency.Blockchain(Blockchain.Unknown, null), - ), - ), - ), - ), + state = ProgressState.Error, + error = ErrorType.UnknownBlockchain, ) } else -> { @@ -107,7 +73,7 @@ private fun internalReduce(action: Action, state: AppState, appStateHolder: AppS is WalletAction.UserWalletChanged -> with(action.userWallet) { val card = scanResponse.card newState = WalletState( - cardId = card.cardId, + userWallet = this, isMultiwalletAllowed = isMultiCurrency, cardImage = Artwork( 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( - state = action.reduxWalletStores.flatMap { it.walletsData }.findProgressState(newState.state), - walletsStores = action.reduxWalletStores, + walletsStores = action.walletStores, ) } is WalletAction.TotalFiatBalanceChanged -> { @@ -159,7 +124,7 @@ private fun internalReduce(action: Action, state: AppState, appStateHolder: AppS } fun findSelectedCurrency( - walletsStores: List, + walletsStores: List, currentSelectedCurrency: Currency?, isMultiWalletAllowed: Boolean, ): Currency? = if (isMultiWalletAllowed) { @@ -175,11 +140,11 @@ private fun CardDTO.findCardsCount(): Int? { return (this.backupStatus as? CardDTO.BackupStatus.Active)?.cardCount?.inc() } -fun Wallet.createAddressesData(): List { - val listOfAddressData = mutableListOf() +fun Wallet.createAddressesData(): List { + val listOfAddressData = mutableListOf() // put a defaultAddress at the first place addresses.forEach { - val addressData = AddressData( + val addressData = WalletDataModel.AddressData( it.value, it.type, getShareUri(it.value), diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/utils/Constants.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/utils/Constants.kt new file mode 100644 index 0000000000..dfab28f18a --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/utils/Constants.kt @@ -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 = "<" \ No newline at end of file From 64e6a7609fba79c3a304a0d21b014604848ed481 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 11 Apr 2023 12:49:04 +0300 Subject: [PATCH 2/2] Updated on 2026-08-14 --- .../tap/features/wallet/ui/BalanceWidget.kt | 110 ++++++------- .../wallet/ui/WalletDetailsFragment.kt | 125 ++++++++------- .../tap/features/wallet/ui/WalletFragment.kt | 5 +- .../tap/features/wallet/ui/WalletViewModel.kt | 4 +- .../wallet/ui/WalletWarningConverter.kt | 4 +- .../wallet/ui/adapters/WalletAdapter.kt | 53 ++++--- .../analytics/WalletAnalyticsEventsMapper.kt | 27 ++-- .../wallet/ui/utils/WalletDataOperations.kt | 144 ++++++++++++++++++ .../wallet/ui/view/TotalBalanceCard.kt | 40 ++--- .../wallet/ui/view/WalletDetailsButtonsRow.kt | 2 +- .../wallet/ui/wallet/MultiWalletView.kt | 59 ++----- .../wallet/ui/wallet/SingleWalletView.kt | 27 ++-- .../ui/wallet/saltPay/SaltPayWalletView.kt | 12 +- .../tap/features/walletSelector/ui/Mapper.kt | 2 +- 14 files changed, 380 insertions(+), 234 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/features/wallet/ui/utils/WalletDataOperations.kt diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/BalanceWidget.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/BalanceWidget.kt index 0afeb7e3b0..3b8cac1392 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/BalanceWidget.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/BalanceWidget.kt @@ -3,6 +3,10 @@ package com.tangem.tap.features.wallet.ui import androidx.annotation.IdRes import com.tangem.tap.common.extensions.hide 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.databinding.CardBalanceBinding import java.math.BigDecimal @@ -36,36 +40,37 @@ data class BalanceWidgetData( class BalanceWidget( private val binding: CardBalanceBinding, private val fragment: WalletFragment, - private val data: BalanceWidgetData, - private val token: BalanceWidgetData?, - private val isTwinCard: Boolean, + private val blockchainWalletData: WalletDataModel, + private val tokenWalletData: WalletDataModel?, ) { @Suppress("LongMethod", "ComplexMethod") fun setup() { - when (data.status) { - BalanceStatus.Loading -> { + when (blockchainWalletData.status) { + is WalletDataModel.Loading -> { with(binding) { lBalance.root.show() lBalanceError.root.hide() lBalance.tvFiatAmount.hide() - lBalance.tvCurrency.text = data.currency + lBalance.tvCurrency.text = blockchainWalletData.currency.currencyName lBalance.tvAmount.text = "" } showStatus(R.id.tv_status_loading) - if (token != null) { - showBalanceWithToken(data, false) + if (tokenWalletData != null) { + showBalanceWithToken(blockchainWalletData, false) } 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() binding.lBalanceError.root.hide() - val statusView = if (data.status == BalanceStatus.VerifiedOnline) { + val statusView = if (blockchainWalletData.status is WalletDataModel.VerifiedOnline) { R.id.tv_status_verified } else { tvStatusError.text = @@ -75,61 +80,62 @@ class BalanceWidget( showStatus(statusView) tvStatusErrorMessage.hide() - if (token != null) { - showBalanceWithToken(data, true) + if (tokenWalletData != null) { + showBalanceWithToken(blockchainWalletData, true) } else { - showBalanceWithoutToken(data, true) + showBalanceWithoutToken(blockchainWalletData, true) } } - BalanceStatus.Unreachable -> with(binding.lBalance) { + is WalletDataModel.Unreachable -> with(binding.lBalance) { root.show() binding.lBalanceError.root.hide() tvFiatAmount.hide() groupBaseCurrency.hide() - val currency = if (token != null) token.currencySymbol else data.currency + val currency = tokenWalletData?.currency?.currencySymbol + ?: blockchainWalletData.currency.currencyName tvCurrency.text = currency tvAmount.text = "" - tvStatusErrorMessage.text = data.errorMessage + tvStatusErrorMessage.text = blockchainWalletData.status.errorMessage tvStatusError.text = fragment.getString(R.string.wallet_balance_blockchain_unreachable) showStatus(R.id.group_error) - tvStatusErrorMessage.show(!data.errorMessage.isNullOrBlank()) + tvStatusErrorMessage.show(!blockchainWalletData.status.errorMessage.isNullOrBlank()) } - BalanceStatus.EmptyCard -> with(binding.lBalanceError) { - binding.lBalance.root.hide() - binding.lBalanceError.root.show() - if (isTwinCard) { - tvErrorTitle.text = fragment.getText(R.string.wallet_error_empty_twin_card) - tvErrorDescriptions.text = - fragment.getText(R.string.wallet_error_empty_twin_card_subtitle) - } else { - tvErrorTitle.text = fragment.getText(R.string.wallet_error_empty_card) - tvErrorDescriptions.text = - fragment.getText(R.string.wallet_error_empty_card_subtitle) - } - } - BalanceStatus.NoAccount -> with(binding.lBalanceError) { + // BalanceStatus.EmptyCard -> with(binding.lBalanceError) { + // binding.lBalance.root.hide() + // binding.lBalanceError.root.show() + // if (isTwinCard) { + // tvErrorTitle.text = fragment.getText(R.string.wallet_error_empty_twin_card) + // tvErrorDescriptions.text = + // fragment.getText(R.string.wallet_error_empty_twin_card_subtitle) + // } else { + // tvErrorTitle.text = fragment.getText(R.string.wallet_error_empty_card) + // tvErrorDescriptions.text = + // fragment.getText(R.string.wallet_error_empty_card_subtitle) + // } + // } + is WalletDataModel.NoAccount -> with(binding.lBalanceError) { binding.lBalance.root.hide() binding.lBalanceError.root.show() tvErrorTitle.text = fragment.getText(R.string.wallet_error_no_account) tvErrorDescriptions.text = fragment.getString( R.string.no_account_generic, - data.amountToCreateAccount, - data.currencySymbol, + blockchainWalletData.status.amountToCreateAccount, + blockchainWalletData.currency.currencySymbol, ) } - BalanceStatus.UnknownBlockchain -> with(binding.lBalanceError) { - binding.lBalance.root.hide() - binding.lBalanceError.root.show() - tvErrorTitle.text = - fragment.getText(R.string.wallet_error_unsupported_blockchain) - tvErrorDescriptions.text = - fragment.getString(R.string.wallet_error_unsupported_blockchain_subtitle) - } + // BalanceStatus.UnknownBlockchain -> with(binding.lBalanceError) { + // binding.lBalance.root.hide() + // binding.lBalanceError.root.show() + // tvErrorTitle.text = + // fragment.getText(R.string.wallet_error_unsupported_blockchain) + // tvErrorDescriptions.text = + // fragment.getString(R.string.wallet_error_unsupported_blockchain_subtitle) + // } else -> {} } } @@ -140,25 +146,25 @@ class BalanceWidget( 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() - tvCurrency.text = token?.currencySymbol - tvBaseCurrency.text = data.currency - tvAmount.text = if (showAmount) token?.amountFormatted else "" - tvBaseAmount.text = if (showAmount) data.amountFormatted else "" + tvCurrency.text = tokenWalletData?.currency?.currencySymbol + tvBaseCurrency.text = data.currency.currencyName + tvAmount.text = if (showAmount) tokenWalletData?.getFormattedAmount() else "" + tvBaseAmount.text = if (showAmount) data.getFormattedAmount() else "" if (showAmount) { 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() - tvCurrency.text = data.currency - tvAmount.text = if (showAmount) data.amountFormatted else "" + tvCurrency.text = data.currency.currencyName + tvAmount.text = if (showAmount) data.getFormattedAmount() else "" if (showAmount) { tvFiatAmount.show() - tvFiatAmount.text = data.fiatAmountFormatted + tvFiatAmount.text = data.getFormattedFiatAmount(store.state.globalState.appCurrency) } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt index e070d1e5da..9952836ff9 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt @@ -41,21 +41,29 @@ import com.tangem.tap.common.extensions.show import com.tangem.tap.common.extensions.toQrCode import com.tangem.tap.common.recyclerView.SpaceItemDecoration 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.PendingTransaction import com.tangem.tap.features.wallet.models.PendingTransactionType 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.ProgressState 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.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.WalletDetailWarningMessagesAdapter 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.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.userWalletsListManagerSafe import com.tangem.tap.walletCurrenciesManager @@ -85,31 +93,19 @@ class WalletDetailsFragment : private val binding: FragmentWalletDetailsBinding by viewBinding(FragmentWalletDetailsBinding::bind) - private val walletDataWatcher: ModelWatcher = modelWatcher { - val addressCardStrategy: DiffStrategy = { old, new -> - old.currency != new.currency || - old.walletAddresses?.selectedAddress != new.walletAddresses?.selectedAddress || - old.shouldShowMultipleAddress() != new.shouldShowMultipleAddress() + private val walletDataWatcher: ModelWatcher = modelWatcher { + val addressCardStrategy: DiffStrategy = { old, new -> + old.currency != new.currency || old.walletAddresses != new.walletAddresses } - WalletData::pendingTransactions { - showPendingTransactionsIfPresent(it) - } - WalletData::currency { + WalletDataModel::currency { handleCurrencyIcon(it) } - WalletData::currencyData { - setupBalanceData(it) - } - WalletData::walletAddresses { walletAddresses -> + WalletDataModel::walletAddresses { walletAddresses -> setupCopyAndShareButtons(walletAddresses?.selectedAddress?.address) } - WalletData::assembleWarnings { warnings -> - handleWarnings(warnings) - } - (WalletData::currencyData or WalletData::currency) { walletData -> - setupCurrency(walletData.currencyData, walletData.currency) - setupSwipeRefresh(walletData.currencyData, walletData.currency) + WalletDataModel::currency { currency -> + setupCurrency(currency) } watch({ it }, addressCardStrategy) { walletData -> setupAddressCard( @@ -121,9 +117,28 @@ class WalletDetailsFragment : } private val walletStateWatcher: ModelWatcher = modelWatcher { - WalletState::selectedWalletData { selectedWallet -> + val walletDataStrategy: DiffStrategy = { 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) { + setupBalanceData(selectedWallet) + setupSwipeRefresh(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 -> @@ -234,8 +249,8 @@ class WalletDetailsFragment : ) } - private fun setupCurrency(currencyData: BalanceWidgetData, currency: Currency) = with(binding) { - tvCurrencyTitle.text = currencyData.currency + private fun setupCurrency(currency: Currency) = with(binding) { + tvCurrencyTitle.text = currency.currencyName if (currency is Currency.Token) { 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 { - if (currencyData.status != BalanceStatus.Loading && currencyData.status != BalanceStatus.Refreshing) { + if (walletData.status !is WalletDataModel.Loading) { 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) { - val selectedUserWallet = userWalletsListManagerSafe?.selectedUserWalletSync.guard { - Timber.e("Unable to refresh wallet details screen, no user wallet selected") - return@launch - } - walletCurrenciesManager.update(selectedUserWallet, currency) + walletCurrenciesManager.update(selectedUserWallet, walletData.currency) .doOnResult { withMainContext { 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?) { @@ -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 binding.rowButtons.apply { onBuyClick = { store.dispatch(WalletAction.TradeCryptoAction.Buy()) } @@ -340,7 +353,7 @@ class WalletDetailsFragment : private fun setupAddressCard( shouldShowMultipleAddress: Boolean, - selectedAddress: AddressData?, + selectedAddress: WalletDataModel.AddressData?, currency: Currency, ) = with(binding.lWalletDetails) { if (selectedAddress == null) return@with @@ -371,7 +384,7 @@ class WalletDetailsFragment : private fun setupAddressTypeChips( shouldShowMultipleAddress: Boolean, - selectedAddress: AddressData, + selectedAddress: WalletDataModel.AddressData, currency: Currency, ) = with(binding.lWalletDetails) { if (shouldShowMultipleAddress && currency is Currency.Blockchain) { @@ -408,54 +421,58 @@ class WalletDetailsFragment : } } - private fun setupBalanceData(data: BalanceWidgetData) = with(binding.lWalletDetails) { - when (data.status) { - BalanceStatus.Loading -> { + private fun setupBalanceData(walletData: WalletDataModel) = with(binding.lWalletDetails) { + when (val status = walletData.status) { + is WalletDataModel.Loading -> { lBalanceError.root.hide() lBalance.root.show() lBalance.groupBalance.show() lBalance.tvError.hide() - lBalance.tvAmount.text = data.amountFormatted - lBalance.tvFiatAmount.text = data.fiatAmountFormatted ?: UNKNOWN_AMOUNT_SIGN + lBalance.tvAmount.text = walletData.getFormattedAmount() + lBalance.tvFiatAmount.text = walletData.getFormattedFiatAmount(store.state.globalState.appCurrency) lBalance.tvStatus.setLoadingStatus(R.string.wallet_balance_loading) } - BalanceStatus.VerifiedOnline, BalanceStatus.SameCurrencyTransactionInProgress, - BalanceStatus.TransactionInProgress, + is WalletDataModel.VerifiedOnline, + is WalletDataModel.SameCurrencyTransactionInProgress, + is WalletDataModel.TransactionInProgress, -> { lBalanceError.root.hide() lBalance.root.show() lBalance.groupBalance.show() lBalance.tvError.hide() - lBalance.tvAmount.text = data.amountFormatted - lBalance.tvFiatAmount.text = data.fiatAmountFormatted ?: UNKNOWN_AMOUNT_SIGN - when (data.status) { - BalanceStatus.VerifiedOnline, BalanceStatus.SameCurrencyTransactionInProgress -> { + lBalance.tvAmount.text = walletData.getFormattedAmount() + lBalance.tvFiatAmount.text = walletData.getFormattedFiatAmount(store.state.globalState.appCurrency) + when (status) { + is WalletDataModel.VerifiedOnline, + is WalletDataModel.SameCurrencyTransactionInProgress, + -> { lBalance.tvStatus.setVerifiedBalanceStatus(R.string.wallet_balance_verified) } else -> { lBalance.tvStatus.setWarningStatus(R.string.wallet_balance_tx_in_progress) } } + showPendingTransactionsIfPresent(status.pendingTransactions) } - BalanceStatus.Unreachable -> { + is WalletDataModel.Unreachable -> { lBalanceError.root.hide() lBalance.root.show() lBalance.groupBalance.hide() lBalance.tvError.show() lBalance.tvError.setWarningStatus( R.string.wallet_balance_blockchain_unreachable, - data.errorMessage, + status.errorMessage, ) } - BalanceStatus.NoAccount -> { + is WalletDataModel.NoAccount -> { lBalance.root.hide() lBalanceError.root.show() lBalanceError.tvErrorTitle.text = getText(R.string.wallet_error_no_account) lBalanceError.tvErrorDescriptions.text = getString( R.string.no_account_generic, - data.amountToCreateAccount, - data.currencySymbol, + status.amountToCreateAccount, + walletData.currency.currencySymbol, ) } else -> {} diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt index 571653ff77..ca1149d987 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt @@ -68,7 +68,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber - totalBalance?.state?.let { + totalBalance?.let { viewModel.onBalanceLoaded(totalBalance) store.state.globalState.topUpController?.totalBalanceStateChanged(it) } @@ -159,8 +159,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber { + state.isMultiwalletAllowed && walletView !is MultiWalletView -> { walletView.onViewDestroy() walletView = MultiWalletView() walletView.changeWalletView(this, binding) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletViewModel.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletViewModel.kt index 2be9db11ee..bba9814685 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletViewModel.kt @@ -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.MainScreen 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.features.wallet.models.TotalBalance import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.ui.analytics.WalletAnalyticsEventsMapper import com.tangem.tap.store @@ -88,7 +88,7 @@ internal class WalletViewModel @Inject constructor( bootstrapShowSaveWalletIfNeeded() } - fun onBalanceLoaded(totalBalance: TotalBalance?) { + fun onBalanceLoaded(totalBalance: TotalFiatBalance?) { if (totalBalance != null) { walletAnalyticsEventsMapper.convert(totalBalance)?.let { balanceParam -> analyticsEventHandler.send( diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletWarningConverter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletWarningConverter.kt index 8e9e9c6219..afbf3bff5a 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletWarningConverter.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletWarningConverter.kt @@ -41,8 +41,8 @@ class WalletWarningConverter( is WalletWarning.Rent -> { context.getString( R.string.solana_rent_warning, - message.walletRent.minRentValue, - message.walletRent.rentExemptValue, + message.walletRent.rent, + message.walletRent.exemptionAmount, ) } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt index 98f4fbecee..cbb32572e7 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt @@ -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.hide 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.WalletData -import com.tangem.tap.features.wallet.ui.BalanceStatus +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.getFormattedFiatRate import com.tangem.tap.features.wallet.ui.images.load import com.tangem.tap.store import com.tangem.wallet.R import com.tangem.wallet.databinding.ItemCurrencyWalletBinding -class WalletAdapter : ListAdapter(DiffUtilCallback) { +class WalletAdapter : ListAdapter(DiffUtilCallback) { 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 { @@ -39,34 +41,33 @@ class WalletAdapter : ListAdapter(D holder.bind(currentList[position]) } - object DiffUtilCallback : DiffUtil.ItemCallback() { - override fun areContentsTheSame(oldItem: WalletData, newItem: WalletData) = oldItem == newItem + object DiffUtilCallback : DiffUtil.ItemCallback() { + 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) : RecyclerView.ViewHolder(binding.root) { - fun bind(wallet: WalletData) = with(binding) { - val status = wallet.currencyData.status - // Skip changes when on refreshing status - if (status == BalanceStatus.Refreshing) return@with + fun bind(wallet: WalletDataModel) = with(binding) { + val status = wallet.status + val fiatCurrency = store.state.globalState.appCurrency val statusMessage = when (status) { - BalanceStatus.TransactionInProgress -> { + is WalletDataModel.TransactionInProgress -> { root.getString(R.string.wallet_balance_tx_in_progress) } - BalanceStatus.Unreachable -> { + is WalletDataModel.Unreachable -> { root.getString(R.string.wallet_balance_blockchain_unreachable) } - BalanceStatus.MissedDerivation -> { + is WalletDataModel.MissedDerivation -> { root.getString(R.string.wallet_balance_missing_derivation) } else -> null } - if (status == null || status == BalanceStatus.Loading) { + if (status is WalletDataModel.Loading) { lContent.root.hide() lShimmer.root.veil() } else { @@ -82,24 +83,22 @@ class WalletAdapter : ListAdapter(D ?.derivationStyle, ) - lContent.tvCurrency.text = wallet.currencyData.currency - lContent.tvAmountFiat.text = wallet.currencyData.fiatAmountFormatted ?: "—" - lContent.tvAmount.text = wallet.currencyData.amountFormatted ?: "—" + lContent.tvCurrency.text = wallet.currency.currencyName + lContent.tvAmountFiat.text = wallet.getFormattedFiatAmount(fiatCurrency) + lContent.tvAmount.text = wallet.getFormattedAmount() lContent.tvStatus.isVisible = statusMessage != null lContent.tvStatus.text = statusMessage lContent.tvExchangeRate.isVisible = statusMessage == null - lContent.tvExchangeRate.text = wallet.fiatRateString - ?: root.getString(id = R.string.token_item_no_rate) + lContent.tvExchangeRate.text = wallet.getFormattedFiatRate( + fiatCurrency = fiatCurrency, + noRateValue = root.getString(id = R.string.token_item_no_rate), + ) - if (wallet.walletAddresses != null) { - cardWallet.setOnClickListener { - Analytics.send(Portfolio.TokenTapped()) - store.dispatch(WalletAction.MultiWallet.SelectWallet(wallet.currency)) - } - } else { - cardWallet.setOnClickListener(null) + cardWallet.setOnClickListener { + Analytics.send(Portfolio.TokenTapped()) + store.dispatch(WalletAction.MultiWallet.SelectWallet(wallet.currency)) } } } 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 6c750a5368..277ce8471c 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 @@ -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.extensions.isGreaterThan -import com.tangem.tap.features.wallet.models.TotalBalance -import com.tangem.tap.features.wallet.redux.ProgressState +import com.tangem.tap.domain.model.TotalFiatBalance import com.tangem.utils.converter.Converter import java.math.BigDecimal -class WalletAnalyticsEventsMapper : Converter { +class WalletAnalyticsEventsMapper : Converter { - override fun convert(value: TotalBalance): AnalyticsParam.CardBalanceState? { - return when (value.state) { - ProgressState.Done -> if (value.fiatAmount?.isGreaterThan(BigDecimal.ZERO) == true) { - AnalyticsParam.CardBalanceState.Full - } else { - AnalyticsParam.CardBalanceState.Empty - } - ProgressState.Error -> { - if (value.fiatAmount == null) { + override fun convert(value: TotalFiatBalance): AnalyticsParam.CardBalanceState? { + return when (value) { + is TotalFiatBalance.Error -> { + if (value.amount == null) { // if fiatAmount is null while ProgressState.Error it means error occurs when loading blockchain AnalyticsParam.CardBalanceState.BlockchainError } else { @@ -25,7 +19,14 @@ class WalletAnalyticsEventsMapper : Converter null + is TotalFiatBalance.Loaded -> { + if (value.amount.isGreaterThan(BigDecimal.ZERO)) { + AnalyticsParam.CardBalanceState.Full + } else { + AnalyticsParam.CardBalanceState.Empty + } + } + is TotalFiatBalance.Loading -> null } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/utils/WalletDataOperations.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/utils/WalletDataOperations.kt new file mode 100644 index 0000000000..6b08921a5d --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/utils/WalletDataOperations.kt @@ -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 { + 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 { + val walletWarnings = mutableListOf() + assembleNonTypedWarnings(walletWarnings, blockchainWalletRent) + assembleBlockchainWarnings(walletWarnings) + assembleTokenWarnings(walletWarnings, blockchainAmount) + + return walletWarnings.sortedBy { it.showingPosition } +} + +private fun WalletDataModel.assembleNonTypedWarnings( + walletWarnings: MutableList, + 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) { + 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, + 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 +} \ No newline at end of file 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 4efe5659b4..4eab1313a5 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 @@ -35,9 +35,8 @@ import com.tangem.core.ui.components.SpacerW4 import com.tangem.core.ui.res.TangemTheme import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.common.extensions.formatWithSpaces -import com.tangem.tap.features.wallet.models.TotalBalance -import com.tangem.tap.features.wallet.redux.ProgressState -import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN +import com.tangem.tap.domain.model.TotalFiatBalance +import com.tangem.tap.features.wallet.redux.utils.UNKNOWN_AMOUNT_SIGN import com.tangem.wallet.R import com.valentinilk.shimmer.shimmer import java.math.BigDecimal @@ -52,18 +51,25 @@ internal class TotalBalanceCard @JvmOverloads constructor( ) : AbstractComposeView(context, attrs, defStyleAttr) { private var state by mutableStateOf(TotalBalanceCardState.Empty) - var status: TotalBalance? = null + var status: TotalFiatBalance? = null set(value) { if (field == value) return field = value - updateState(value, onChangeFiatCurrencyClick) + updateState(value, fiatCurrency, onChangeFiatCurrencyClick) } var onChangeFiatCurrencyClick: () -> Unit = { /* no-op */ } set(value) { if (field == value) return 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 @@ -77,23 +83,21 @@ internal class TotalBalanceCard @JvmOverloads constructor( return javaClass.name } - private fun updateState(status: TotalBalance?, onChangeCurrencyClick: () -> Unit) { - state = when (status?.state) { + private fun updateState(status: TotalFiatBalance?, fiatCurrency: FiatCurrency, onChangeCurrencyClick: () -> Unit) { + state = when (status) { null -> TotalBalanceCardState.Empty - ProgressState.Loading -> TotalBalanceCardState.Loading( - fiatCurrency = status.fiatCurrency, + is TotalFiatBalance.Error -> TotalBalanceCardState.Failure( + amount = status.amount, + fiatCurrency = fiatCurrency, onChangeFiatCurrencyClick = onChangeCurrencyClick, ) - ProgressState.Error -> TotalBalanceCardState.Failure( - amount = status.fiatAmount, - fiatCurrency = status.fiatCurrency, + is TotalFiatBalance.Loading -> TotalBalanceCardState.Loading( + fiatCurrency = fiatCurrency, onChangeFiatCurrencyClick = onChangeCurrencyClick, ) - ProgressState.Refreshing, - ProgressState.Done, - -> TotalBalanceCardState.Success( - amount = status.fiatAmount ?: BigDecimal.ZERO, - fiatCurrency = status.fiatCurrency, + is TotalFiatBalance.Loaded -> TotalBalanceCardState.Success( + amount = status.amount, + fiatCurrency = fiatCurrency, onChangeFiatCurrencyClick = onChangeCurrencyClick, ) } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/view/WalletDetailsButtonsRow.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/view/WalletDetailsButtonsRow.kt index c7457f1158..d9bf31f00c 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/view/WalletDetailsButtonsRow.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/view/WalletDetailsButtonsRow.kt @@ -9,7 +9,7 @@ import androidx.core.view.isVisible import com.google.android.material.button.MaterialButton import com.tangem.tap.common.extensions.hide 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 internal class WalletDetailsButtonsRow @JvmOverloads constructor( diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt index d735ef9615..f981a8beca 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt @@ -1,6 +1,5 @@ package com.tangem.tap.features.wallet.ui.wallet -import android.widget.Button import androidx.core.view.isVisible import androidx.recyclerview.widget.LinearLayoutManager 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.redux.navigation.AppScreen 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.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.WalletState -import com.tangem.tap.features.wallet.ui.BalanceStatus import com.tangem.tap.features.wallet.ui.WalletFragment 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.wallet.R import com.tangem.wallet.databinding.FragmentWalletBinding @@ -151,35 +149,29 @@ class MultiWalletView : WalletView() { } } - private fun handleTotalBalance(binding: FragmentWalletBinding, totalBalance: TotalBalance?, walletsCount: Int) = - with(binding.lCardTotalBalance) { - isVisible = walletsCount > 0 + private fun handleTotalBalance( + binding: FragmentWalletBinding, + totalBalance: TotalFiatBalance?, + walletsCount: Int, + ) = with(binding.lCardTotalBalance) { + isVisible = walletsCount > 0 - onChangeFiatCurrencyClick = { - store.dispatch(WalletAction.AppCurrencyAction.ChooseAppCurrency) - } - status = totalBalance + onChangeFiatCurrencyClick = { + store.dispatch(WalletAction.AppCurrencyAction.ChooseAppCurrency) } + status = totalBalance + } private fun handleErrorStates(state: WalletState, binding: FragmentWalletBinding, fragment: WalletFragment) { - when (state.primaryWalletData?.currencyData?.status) { - BalanceStatus.EmptyCard -> { - showErrorState( - binding, - fragment.getText(R.string.wallet_error_empty_card), - fragment.getString(R.string.wallet_error_empty_card_subtitle), - ) - configureButtonsForEmptyWalletState(binding) - } - BalanceStatus.UnknownBlockchain -> { + when (state.error) { + ErrorType.UnknownBlockchain -> { showErrorState( binding, fragment.getText(R.string.wallet_error_unsupported_blockchain), 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() { super.onDestroyFragment() 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) \ No newline at end of file +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt index f29df37ca0..744fc34601 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt @@ -11,14 +11,19 @@ import com.tangem.tap.common.extensions.getQuantityString import com.tangem.tap.common.extensions.getString import com.tangem.tap.common.extensions.hide 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.wallet.models.Currency import com.tangem.tap.features.wallet.models.PendingTransaction import com.tangem.tap.features.wallet.models.PendingTransactionType 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.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.MultipleAddressUiHelper import com.tangem.tap.features.wallet.ui.WalletFragment @@ -71,7 +76,7 @@ class SingleWalletView : WalletView() { setupTwinCards(state.twinCardsState, binding) setupButtons(primaryWalletData, binding, state.isExchangeServiceFeatureOn) setupAddressCard(state, binding) - showPendingTransactionsIfPresent(primaryWalletData.pendingTransactions) + showPendingTransactionsIfPresent(primaryWalletData.status.pendingTransactions) setupBalance(state, primaryWalletData) } @@ -83,32 +88,30 @@ class SingleWalletView : WalletView() { binding?.rvPendingTransaction?.show(knownTransactions.isNotEmpty()) } - private fun setupBalance(state: WalletState, primaryWallet: WalletData) { + private fun setupBalance(state: WalletState, primaryWallet: WalletDataModel) { val fragment = fragment ?: return binding?.apply { lCardBalance.lBalance.root.show() BalanceWidget( binding = this.lCardBalance, fragment = fragment, - data = primaryWallet.currencyData, - token = state.primaryTokenData?.currencyData, - isTwinCard = state.isTangemTwins, + blockchainWalletData = primaryWallet, + tokenWalletData = state.primaryTokenData, ).setup() } } 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) { tvTwinCardNumber.hide() + } else { + tvTwinCardNumber.show() + tvTwinCardNumber.text = tvTwinCardNumber.getQuantityString(R.plurals.card_label_card_count, 2) } } private fun setupButtons( - walletData: WalletData, + walletData: WalletDataModel, binding: FragmentWalletBinding, isExchangeServiceFeatureEnabled: Boolean, ) = with(binding) { @@ -134,7 +137,7 @@ class SingleWalletView : WalletView() { } private fun setupRowButtons( - walletData: WalletData, + walletData: WalletDataModel, rowButtons: WalletDetailsButtonsRow, isExchangeServiceFeatureEnabled: Boolean, ) { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/saltPay/SaltPayWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/saltPay/SaltPayWalletView.kt index e999ee383a..ef9bf43ffa 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/saltPay/SaltPayWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/saltPay/SaltPayWalletView.kt @@ -8,13 +8,15 @@ import com.tangem.tap.common.ShimmerData import com.tangem.tap.common.ShimmerRecyclerAdapter import com.tangem.tap.common.analytics.events.MainScreen 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.show import com.tangem.tap.common.recyclerView.SpaceItemDecoration import com.tangem.tap.features.wallet.redux.ProgressState import com.tangem.tap.features.wallet.redux.WalletAction 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.wallet.WalletView import com.tangem.tap.features.wallet.ui.wallet.saltPay.rv.HistoryItemData @@ -124,16 +126,14 @@ class SaltPayWalletView : WalletView() { tvUnreachable.animateVisibility(show = mainProgressState == ProgressState.Error) veilBalanceCrypto.animateVisibility(show = mainProgressState != ProgressState.Error) - if (tokenData.currencyData.fiatAmount == null) { + if (tokenData.fiatRate == null) { veilBalance.veil() } else { veilBalance.unVeil() - tvBalance.text = tokenData.currencyData.fiatAmount.formatAmountAsSpannedString( - currencySymbol = appCurrency.symbol, - ) + tvBalance.text = tokenData.getFormattedFiatAmount(appCurrency) } - tvBalanceCrypto.text = tokenData.currencyData.amountFormatted + tvBalanceCrypto.text = tokenData.getFormattedAmount() tvCurrencyName.text = appCurrency.code tvCurrencyName.setOnClickListener { 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 a00447b139..400c4e026e 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,7 @@ 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.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.ui.model.MultiCurrencyUserWalletItem import com.tangem.tap.features.walletSelector.ui.model.SingleCurrencyUserWalletItem