From bd3ab3e125bbf88aa187e68bbe738bf5456a522a Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 17 May 2022 00:12:17 +0300 Subject: [PATCH 1/5] Updated on 2026-08-14 --- .../features/wallet/models/TotalBalance.kt | 16 ++++ .../tap/features/wallet/redux/WalletState.kt | 22 ++++-- .../redux/reducers/OnWalletLoadedReducer.kt | 77 ++++++++++++++++--- .../wallet/ui/wallet/MultiWalletView.kt | 19 +++++ .../main/res/layout/card_total_balance.xml | 71 +++++++++++++++++ app/src/main/res/layout/fragment_wallet.xml | 14 +++- app/src/main/res/values-ru/strings.xml | 3 +- app/src/main/res/values/strings.xml | 3 +- 8 files changed, 204 insertions(+), 21 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/features/wallet/models/TotalBalance.kt create mode 100644 app/src/main/res/layout/card_total_balance.xml diff --git a/app/src/main/java/com/tangem/tap/features/wallet/models/TotalBalance.kt b/app/src/main/java/com/tangem/tap/features/wallet/models/TotalBalance.kt new file mode 100644 index 0000000000..31cc8a8d7f --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/wallet/models/TotalBalance.kt @@ -0,0 +1,16 @@ +package com.tangem.tap.features.wallet.models + +import com.tangem.tap.common.redux.global.FiatCurrencyName +import java.math.BigDecimal + +data class TotalBalance( + val state: State, + val fiatAmount: BigDecimal, + val fiatCurrencyName: FiatCurrencyName, +) { + enum class State { + Loading, + Failed, + Success + } +} \ No newline at end of file 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 278ccee818..b9e63b7190 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 @@ -20,6 +20,7 @@ import com.tangem.tap.domain.tokens.BlockchainNetwork import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState import com.tangem.tap.features.tokens.redux.TokenWithBlockchain import com.tangem.tap.features.wallet.models.PendingTransaction +import com.tangem.tap.features.wallet.models.TotalBalance import com.tangem.tap.features.wallet.models.toPendingTransactions import com.tangem.tap.features.wallet.models.toPendingTransactionsForToken import com.tangem.tap.features.wallet.ui.BalanceStatus @@ -44,6 +45,7 @@ data class WalletState( val primaryBlockchain: Blockchain? = null, val primaryToken: Token? = null, val isTestnet: Boolean = false, + val totalBalance: TotalBalance? = null, ) : StateType { // if you do not delegate - the application crashes on startup, @@ -196,18 +198,20 @@ data class WalletState( return copy(wallets = replaceWalletInWallets(walletStore)) } - fun updateWalletStores(walletStores: List): WalletState { - val walletStores = walletStores.toMutableList() + private fun updateWalletStores(walletStores: List): WalletState { + val walletStoresMutable = walletStores.toMutableList() val updatedWallets = wallets.map { oldWalletStore -> - val walletStore = walletStores.find { it.blockchainNetwork == oldWalletStore.blockchainNetwork } + val walletStore = walletStoresMutable.find { + it.blockchainNetwork == oldWalletStore.blockchainNetwork + } if (walletStore != null) { - walletStores.remove(walletStore) + walletStoresMutable.remove(walletStore) walletStore } else { oldWalletStore } } - return copy(wallets = updatedWallets + walletStores) + return copy(wallets = updatedWallets + walletStoresMutable) } fun removeWallet(walletData: WalletData?): WalletState { @@ -268,6 +272,14 @@ data class WalletState( ) } } + + fun updateTotalBalance( + totalBalance: TotalBalance + ): WalletState { + return this.copy( + totalBalance = totalBalance + ) + } } sealed class WalletDialog : StateDialog { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/OnWalletLoadedReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/OnWalletLoadedReducer.kt index acd7c4d8b7..1809910e90 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/OnWalletLoadedReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/OnWalletLoadedReducer.kt @@ -9,6 +9,7 @@ import com.tangem.tap.common.extensions.toFormattedCurrencyString import com.tangem.tap.common.extensions.toFormattedFiatValue import com.tangem.tap.domain.getFirstToken import com.tangem.tap.domain.tokens.BlockchainNetwork +import com.tangem.tap.features.wallet.models.TotalBalance import com.tangem.tap.features.wallet.models.removeUnknownTransactions import com.tangem.tap.features.wallet.models.toPendingTransactions import com.tangem.tap.features.wallet.redux.* @@ -16,6 +17,7 @@ import com.tangem.tap.features.wallet.ui.BalanceStatus import com.tangem.tap.features.wallet.ui.BalanceWidgetData import com.tangem.tap.features.wallet.ui.TokenData import com.tangem.tap.store +import java.math.BigDecimal import java.math.RoundingMode class OnWalletLoadedReducer { @@ -33,13 +35,12 @@ class OnWalletLoadedReducer { blockchainNetwork: BlockchainNetwork, walletState: WalletState ): WalletState { + val walletData = walletState.getWalletData(blockchainNetwork) ?: return walletState + val fiatCurrencySymbol = store.state.globalState.appCurrency val exchangeManager = store.state.globalState.currencyExchangeManager val coinAmountValue = wallet.amounts[AmountType.Coin]?.value - if (walletState.getWalletData(blockchainNetwork) == null) { - return walletState - } val formattedAmount = coinAmountValue?.toFormattedCurrencyString( wallet.blockchain.decimals(), wallet.blockchain.currency @@ -54,10 +55,9 @@ class OnWalletLoadedReducer { } else { BalanceStatus.VerifiedOnline } - val walletData = walletState.getWalletData(blockchainNetwork) - val fiatAmount = walletData?.fiatRate?.let { coinAmountValue?.toFiatValue(it) } - val newWalletData = walletData?.copy( + val fiatAmount = walletData.fiatRate?.let { coinAmountValue?.toFiatValue(it) } + val newWalletData = walletData.copy( currencyData = walletData.currencyData.copy( status = balanceStatus, currency = wallet.blockchain.fullName, currencySymbol = wallet.blockchain.currency, @@ -87,7 +87,7 @@ class OnWalletLoadedReducer { val tokenFiatAmount = tokenWalletData?.fiatRate?.let { rate -> tokenAmountValue?.toFiatValue(rate) } - val tokenSendButton = newWalletData?.shouldEnableTokenSendButton() == true + val tokenSendButton = newWalletData.shouldEnableTokenSendButton() && tokenPendingTransactions.isEmpty() tokenWalletData?.copy( currencyData = tokenWalletData.currencyData.copy( @@ -106,18 +106,27 @@ class OnWalletLoadedReducer { tradeCryptoState = TradeCryptoState.from(exchangeManager, tokenWalletData), ) } - val newWallets = (tokens + newWalletData).mapNotNull { it } + val newWallets = tokens + newWalletData val wallets = walletState.replaceSomeWallets((newWallets)) + val totalBalance = TotalBalance( + state = wallets.findTotalBalanceState(), + fiatAmount = wallets.calculateTotalFiatAmount(), + fiatCurrencyName = fiatCurrencySymbol, + ) + val state = if (wallets.any { it.currencyData.status == BalanceStatus.Loading }) { ProgressState.Loading } else { ProgressState.Done } - val newState = walletState.updateWalletsData(wallets) - return newState.copy( - state = state, error = null - ) + return walletState + .updateWalletsData(wallets) + .updateTotalBalance(totalBalance) + .copy( + state = state, + error = null + ) } private fun onSingleWalletLoaded(wallet: Wallet, walletState: WalletState): WalletState { @@ -183,4 +192,48 @@ class OnWalletLoadedReducer { state = ProgressState.Done, error = null ) } + + private fun List.findTotalBalanceState(): TotalBalance.State { + return this.mapToTotalBalanceState() + .fold(initial = TotalBalance.State.Loading) { accState, newState -> + accState or newState + } + } + + private fun List.calculateTotalFiatAmount(): BigDecimal { + return this.map { it.currencyData.fiatAmount ?: BigDecimal.ZERO } + .reduce(BigDecimal::plus) + } + + private fun List.mapToTotalBalanceState(): List { + return this.map { + when (it.currencyData.status) { + BalanceStatus.VerifiedOnline, + BalanceStatus.SameCurrencyTransactionInProgress, + BalanceStatus.TransactionInProgress -> TotalBalance.State.Success + BalanceStatus.Unreachable, + BalanceStatus.NoAccount, + BalanceStatus.EmptyCard, + BalanceStatus.UnknownBlockchain -> TotalBalance.State.Failed + BalanceStatus.Loading, + null -> TotalBalance.State.Loading + } + } + } + + infix fun TotalBalance.State.or(newState: TotalBalance.State): TotalBalance.State { + return when (this) { + TotalBalance.State.Loading -> when (newState) { + TotalBalance.State.Loading -> this + TotalBalance.State.Failed, + TotalBalance.State.Success -> newState + } + TotalBalance.State.Success, + TotalBalance.State.Failed -> when (newState) { + TotalBalance.State.Loading, + TotalBalance.State.Failed -> newState + TotalBalance.State.Success -> this + } + } + } } \ No newline at end of file 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 16c85a88fb..fc97a44777 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 @@ -12,6 +12,7 @@ import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.currenciesRepository import com.tangem.tap.features.tokens.redux.TokensAction +import com.tangem.tap.features.wallet.models.TotalBalance import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.redux.WalletDialog import com.tangem.tap.features.wallet.redux.WalletState @@ -51,6 +52,7 @@ class MultiWalletView : WalletView { btnScanMultiwallet.show() rvMultiwallet.show() btnAddToken.show() + lCardTotalBalance.root.show() setupWalletCardNumber(binding) } @@ -97,6 +99,7 @@ class MultiWalletView : WalletView { val fragment = fragment ?: return val binding = binding ?: return + state.totalBalance?.let { handleTotalBalance(binding, it) } walletsAdapter.submitList(state.walletsData, state.primaryBlockchain, state.primaryToken) binding.btnAddToken.setOnClickListener { @@ -127,6 +130,20 @@ class MultiWalletView : WalletView { handleDialogs(state.walletDialog) } + private fun handleTotalBalance( + binding: FragmentWalletBinding, + totalBalance: TotalBalance, + ) = with(binding.lCardTotalBalance) { + val fiatAmountFormatted = with(totalBalance) { + "${fiatAmount.stripTrailingZeros()} $fiatCurrencyName" + } + tvBalance.text = fiatAmountFormatted + tvCurrencyName.text = totalBalance.fiatCurrencyName + tvCurrencyName.setOnClickListener { + // TODO: Open app currency selector + } + } + private fun handleErrorStates( state: WalletState, binding: FragmentWalletBinding, @@ -148,6 +165,8 @@ class MultiWalletView : WalletView { fragment.getString(R.string.wallet_error_unsupported_blockchain_subtitle) ) } + else -> { /* no-op */ + } } } diff --git a/app/src/main/res/layout/card_total_balance.xml b/app/src/main/res/layout/card_total_balance.xml new file mode 100644 index 0000000000..51fa4aecb7 --- /dev/null +++ b/app/src/main/res/layout/card_total_balance.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_wallet.xml b/app/src/main/res/layout/fragment_wallet.xml index bc98ff9e41..829a1f019b 100644 --- a/app/src/main/res/layout/fragment_wallet.xml +++ b/app/src/main/res/layout/fragment_wallet.xml @@ -101,16 +101,26 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/barrier" /> + + + app:layout_constraintTop_toBottomOf="@id/l_card_total_balance" /> - \ No newline at end of file + diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 9a0a6881fd..014599d80e 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -382,4 +382,5 @@ Привет, команда поддержки, Пожалуйста, расскажите нам больше о Вашей проблеме. Каждая деталь может быть полезной. Информация ниже не является обязательной. Вы можете стереть её, если хотите. - \ No newline at end of file + Баланс + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c3e70dee2f..82023e7837 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -206,4 +206,5 @@ To avoid paying an increased commission the next time you top up your wallet, reduce the amount by %s XTZ Reduce by %s XTZ No, send all - \ No newline at end of file + Total balance + From 714eaf5668c2cf0a4c1cc89b2415455812b04e8e Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 18 May 2022 11:20:42 +0300 Subject: [PATCH 2/5] Updated on 2026-08-14 --- .../tap/common/entities/FiatCurrency.kt | 18 +++++++++ .../tangem/tap/common/extensions/Specific.kt | 10 +++-- .../tap/common/extensions/WalletManager.kt | 2 +- .../tap/common/redux/global/GlobalAction.kt | 5 ++- .../tap/common/redux/global/GlobalState.kt | 5 +-- .../features/details/redux/DetailsAction.kt | 11 +++--- .../details/redux/DetailsMiddleware.kt | 35 +++++++++++++++--- .../features/details/redux/DetailsReducer.kt | 5 ++- .../features/details/redux/DetailsState.kt | 8 ++-- .../details/ui/CurrencySelectionDialog.kt | 36 +++++++++++------- .../features/details/ui/DetailsFragment.kt | 8 ++-- .../send/redux/reducers/ReceiptReducer.kt | 2 +- .../features/send/redux/states/SendState.kt | 28 +++++++------- .../tap/features/send/ui/SendFragment.kt | 11 +++--- .../features/wallet/models/TotalBalance.kt | 4 +- .../middlewares/TradeCryptoMiddleware.kt | 5 ++- .../redux/middlewares/WalletMiddleware.kt | 4 +- .../redux/reducers/MultiWalletReducer.kt | 2 +- .../redux/reducers/OnWalletLoadedReducer.kt | 14 +++---- .../wallet/redux/reducers/WalletReducer.kt | 37 +++++++++++++------ .../CurrencyExchangeManager.kt | 12 ++++-- 21 files changed, 165 insertions(+), 97 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/common/entities/FiatCurrency.kt diff --git a/app/src/main/java/com/tangem/tap/common/entities/FiatCurrency.kt b/app/src/main/java/com/tangem/tap/common/entities/FiatCurrency.kt new file mode 100644 index 0000000000..c0df80eb2a --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/entities/FiatCurrency.kt @@ -0,0 +1,18 @@ +package com.tangem.tap.common.entities + +data class FiatCurrency( + val code: String, + val name: String, + val symbol: String, +) { + val displayName: String + get() = "${this.name} (${this.code}) - ${this.symbol}" + + companion object { + val Default = FiatCurrency( + symbol = "$", + code = "USD", + name = "US Dollar", + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt b/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt index 4b978e18f6..c7386eb7f0 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt @@ -1,8 +1,10 @@ package com.tangem.tap.common.extensions +import android.text.Spanned +import android.text.SpannedString +import android.text.style.RelativeSizeSpan +import androidx.core.text.buildSpannedString import com.tangem.common.extensions.isZero -import com.tangem.network.api.tangemTech.CurrenciesResponse -import com.tangem.tap.common.redux.global.FiatCurrencyName import java.math.BigDecimal import java.math.RoundingMode import java.text.DecimalFormat @@ -37,7 +39,7 @@ fun BigDecimal.toFormattedCurrencyString( return "$formattedAmount $currency" } -fun BigDecimal.toFiatString(rateValue: BigDecimal, fiatCurrencyName: FiatCurrencyName): String { +fun BigDecimal.toFiatString(rateValue: BigDecimal, fiatCurrencyName: String): String { var fiatValue = rateValue.multiply(this) fiatValue = fiatValue.setScale(2, RoundingMode.HALF_UP) return "≈ ${fiatCurrencyName}  $fiatValue" @@ -48,7 +50,7 @@ fun BigDecimal.toFiatValue(rateValue: BigDecimal): BigDecimal { return fiatValue.setScale(2, RoundingMode.HALF_UP) } -fun BigDecimal.toFormattedFiatValue(fiatCurrencyName: FiatCurrencyName): String { +fun BigDecimal.toFormattedFiatValue(fiatCurrencyName: String): String { return "≈ ${fiatCurrencyName}  $this" } 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 540d23c3f7..d671b60a29 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 @@ -57,7 +57,7 @@ fun WalletManager?.getToUpUrl(): String? { action = CurrencyExchangeManager.Action.Buy, blockchain = wallet.blockchain, cryptoCurrencyName = wallet.blockchain.currency, - fiatCurrency = globalState.appCurrency, + fiatCurrencyName = globalState.appCurrency.code, walletAddress = defaultAddress, ) } diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt index 21fd824d71..b871b7853f 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt @@ -6,6 +6,7 @@ import com.tangem.common.CompletionResult import com.tangem.common.core.TangemError import com.tangem.domain.common.ScanResponse import com.tangem.tap.common.analytics.GlobalAnalyticsHandler +import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.common.redux.* import com.tangem.tap.domain.TapError import com.tangem.tap.domain.configurable.config.ConfigManager @@ -54,9 +55,9 @@ sealed class GlobalAction : Action { data class SetIfCardVerifiedOnline(val verified: Boolean) : GlobalAction() - data class ChangeAppCurrency(val appCurrency: FiatCurrencyName) : GlobalAction() + data class ChangeAppCurrency(val appCurrency: FiatCurrency) : GlobalAction() object RestoreAppCurrency : GlobalAction() { - data class Success(val appCurrency: FiatCurrencyName) : GlobalAction() + data class Success(val appCurrency: FiatCurrency) : GlobalAction() } data class UpdateWalletSignedHashes( diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt index a58e92b944..75504f3ef2 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt @@ -2,7 +2,7 @@ package com.tangem.tap.common.redux.global import com.tangem.domain.common.ScanResponse import com.tangem.tap.common.analytics.AnalyticsHandler -import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY +import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.domain.PayIdManager import com.tangem.tap.domain.TapWalletManager @@ -22,7 +22,7 @@ data class GlobalState( val configManager: ConfigManager? = null, val warningManager: WarningMessagesManager? = null, val feedbackManager: FeedbackManager? = null, - val appCurrency: FiatCurrencyName = DEFAULT_FIAT_CURRENCY, + val appCurrency: FiatCurrency = FiatCurrency.Default, val scanCardFailsCounter: Int = 0, val dialog: StateDialog? = null, val currencyExchangeManager: CurrencyExchangeManager? = null, @@ -40,7 +40,6 @@ data class AndroidResources( ) } typealias CryptoCurrencyName = String -typealias FiatCurrencyName = String data class OnboardingState( val onboardingStarted: Boolean = false, diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt index 5b572f8fff..16c5bbd1c8 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt @@ -4,11 +4,10 @@ import com.tangem.blockchain.common.Wallet import com.tangem.common.card.Card import com.tangem.domain.common.ScanResponse import com.tangem.domain.common.TwinCardNumber -import com.tangem.network.api.tangemTech.CurrenciesResponse import com.tangem.network.api.tangemTech.TangemTechService import com.tangem.operations.pins.CheckUserCodesResponse +import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.common.redux.NotificationAction -import com.tangem.tap.common.redux.global.FiatCurrencyName import com.tangem.tap.domain.termsOfUse.CardTou import com.tangem.wallet.R import org.rekotlin.Action @@ -19,8 +18,8 @@ sealed class DetailsAction : Action { val scanResponse: ScanResponse, val wallets: List, val cardTou: CardTou, - val fiatCurrencyName: FiatCurrencyName, - val fiatCurrencies: List? = null, + val fiatCurrencyName: FiatCurrency, + val fiatCurrencies: List? = null, val tangemTechService: TangemTechService, ) : DetailsAction() @@ -48,10 +47,10 @@ sealed class DetailsAction : Action { object CreateBackup : DetailsAction() sealed class AppCurrencyAction : DetailsAction() { - data class SetCurrencies(val currencies: List) : AppCurrencyAction() + data class SetCurrencies(val currencies: List) : AppCurrencyAction() object ChooseAppCurrency : AppCurrencyAction() object Cancel : AppCurrencyAction() - data class SelectAppCurrency(val fiatCurrencyName: FiatCurrencyName) : AppCurrencyAction() + data class SelectAppCurrency(val fiatCurrency: FiatCurrency) : AppCurrencyAction() } sealed class ManageSecurity : DetailsAction() { diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt index 9f1b6b6187..e938026085 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt @@ -4,10 +4,12 @@ import com.tangem.common.CompletionResult import com.tangem.common.card.FirmwareVersion import com.tangem.common.core.TangemSdkError import com.tangem.common.services.Result +import com.tangem.network.api.tangemTech.CurrenciesResponse import com.tangem.operations.pins.CheckUserCodesResponse import com.tangem.tap.* import com.tangem.tap.common.analytics.Analytics import com.tangem.tap.common.analytics.AnalyticsParam +import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.common.extensions.dispatchNotification import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.AppState @@ -71,7 +73,11 @@ class DetailsMiddleware { val fiatCurrenciesPrefStorage = preferencesStorage.fiatCurrenciesPrefStorage val storedFiatCurrencies = fiatCurrenciesPrefStorage.restore() if (storedFiatCurrencies.isNotEmpty()) { - store.dispatch(DetailsAction.AppCurrencyAction.SetCurrencies(storedFiatCurrencies)) + store.dispatch( + DetailsAction.AppCurrencyAction.SetCurrencies( + currencies = storedFiatCurrencies.mapToUiModel() + ) + ) } scope.launch { @@ -79,9 +85,15 @@ class DetailsMiddleware { when (val result = tangemTechService.currencies()) { is Result.Success -> { val currenciesList = result.data.currencies - if (currenciesList.isNotEmpty() && currenciesList.toSet() != storedFiatCurrencies.toSet()) { + if (currenciesList.isNotEmpty() && + currenciesList.toSet() != storedFiatCurrencies.toSet() + ) { fiatCurrenciesPrefStorage.save(currenciesList) - dispatchOnMain(DetailsAction.AppCurrencyAction.SetCurrencies(currenciesList)) + dispatchOnMain( + DetailsAction.AppCurrencyAction.SetCurrencies( + currencies = currenciesList.mapToUiModel() + ) + ) } } is Result.Failure -> {} @@ -89,7 +101,17 @@ class DetailsMiddleware { } } - class EraseWalletMiddleware() { + private fun List.mapToUiModel(): List { + return this.map { + FiatCurrency( + code = it.code, + name = it.name, + symbol = it.unit + ) + } + } + + class EraseWalletMiddleware { fun handle(action: DetailsAction.ResetToFactory) { when (action) { is DetailsAction.ResetToFactory.Proceed -> { @@ -137,8 +159,9 @@ class DetailsMiddleware { when (action) { is DetailsAction.AppCurrencyAction.SelectAppCurrency -> { store.state.globalState.tapWalletManager.rates.clear() - preferencesStorage.saveAppCurrency(action.fiatCurrencyName) - store.dispatch(GlobalAction.ChangeAppCurrency(action.fiatCurrencyName)) + preferencesStorage.fiatCurrenciesPrefStorage + .saveAppCurrency(action.fiatCurrency) + store.dispatch(GlobalAction.ChangeAppCurrency(action.fiatCurrency)) store.dispatch(WalletAction.LoadFiatRate()) } } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index 796c7c171f..a000db965e 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -49,7 +49,7 @@ private fun handlePrepareScreen( wallets = action.wallets, cardInfo = action.scanResponse.card.toCardInfo(), appCurrencyState = state.appCurrencyState.copy( - fiatCurrencyName = action.fiatCurrencyName, + currentFiatCurrency = action.fiatCurrencyName, showAppCurrencyDialog = false, ), cardTermsOfUseUrl = action.cardTou.getUrl(action.scanResponse.card), @@ -109,7 +109,8 @@ private fun handleAppCurrencyAction( is DetailsAction.AppCurrencyAction.SelectAppCurrency -> { state.copy( appCurrencyState = state.appCurrencyState.copy( - fiatCurrencyName = action.fiatCurrencyName, showAppCurrencyDialog = false + currentFiatCurrency = action.fiatCurrency, + showAppCurrencyDialog = false ) ) } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt index 0d2cf689d5..1315f43e15 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt @@ -3,10 +3,8 @@ package com.tangem.tap.features.details.redux import android.net.Uri import com.tangem.blockchain.common.Wallet import com.tangem.domain.common.ScanResponse -import com.tangem.network.api.tangemTech.CurrenciesResponse import com.tangem.tap.common.entities.Button -import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY -import com.tangem.tap.common.redux.global.FiatCurrencyName +import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState import com.tangem.tap.store import org.rekotlin.StateType @@ -53,7 +51,7 @@ data class SecurityScreenState( enum class SecurityOption { LongTap, PassCode, AccessCode } data class AppCurrencyState( - val fiatCurrencyName: FiatCurrencyName = DEFAULT_FIAT_CURRENCY, + val currentFiatCurrency: FiatCurrency = FiatCurrency.Default, val showAppCurrencyDialog: Boolean = false, - val fiatCurrencies: List? = null, + val fiatCurrencies: List? = null, ) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/CurrencySelectionDialog.kt b/app/src/main/java/com/tangem/tap/features/details/ui/CurrencySelectionDialog.kt index d7248b75c8..23e79bd846 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/CurrencySelectionDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/CurrencySelectionDialog.kt @@ -2,9 +2,7 @@ package com.tangem.tap.features.details.ui import android.content.Context import androidx.appcompat.app.AlertDialog -import com.tangem.network.api.tangemTech.CurrenciesResponse -import com.tangem.tap.common.extensions.toFormattedString -import com.tangem.tap.common.redux.global.FiatCurrencyName +import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.features.details.redux.DetailsAction import com.tangem.tap.store import com.tangem.wallet.R @@ -13,20 +11,30 @@ class CurrencySelectionDialog { var dialog: AlertDialog? = null - fun show(currenciesList: List, currentAppCurrency: FiatCurrencyName, context: Context) { - + fun show( + currenciesList: List, + currentAppCurrency: FiatCurrency, + context: Context + ) { if (dialog == null) { - val currenciesToShow = currenciesList.map { it.toFormattedString() }.toTypedArray() - var currentSelection = currenciesList.indexOfFirst { it.code == currentAppCurrency } + val currenciesToShow = currenciesList + .map { it.displayName } + .toTypedArray() + var currentSelection = currenciesList + .indexOfFirst { it.code == currentAppCurrency.code } dialog = AlertDialog.Builder(context) - .setTitle(context.getString(R.string.details_row_title_currency)) - .setNegativeButton(context.getString(R.string.common_cancel)) { _, _ -> - store.dispatch(DetailsAction.AppCurrencyAction.Cancel) - } - .setPositiveButton(context.getString(R.string.common_done)) { _, _ -> - val selectedCurrency = currenciesList[currentSelection] - store.dispatch(DetailsAction.AppCurrencyAction.SelectAppCurrency(selectedCurrency.code)) + .setTitle(context.getString(R.string.details_row_title_currency)) + .setNegativeButton(context.getString(R.string.common_cancel)) { _, _ -> + store.dispatch(DetailsAction.AppCurrencyAction.Cancel) + } + .setPositiveButton(context.getString(R.string.common_done)) { _, _ -> + val selectedCurrency = currenciesList[currentSelection] + store.dispatch( + DetailsAction.AppCurrencyAction.SelectAppCurrency( + fiatCurrency = selectedCurrency + ) + ) } .setOnDismissListener { store.dispatch(DetailsAction.AppCurrencyAction.Cancel) diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt index 781ba05548..2dbab2ee0b 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt @@ -115,7 +115,7 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber amountType.token.symbol diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt index ed98986ae3..98fbfa47e1 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt @@ -5,8 +5,8 @@ import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.WalletManager import com.tangem.common.extensions.isZero import com.tangem.tap.common.CurrencyConverter +import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.common.entities.IndeterminateProgressButton -import com.tangem.tap.common.entities.TapCurrency import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.common.text.DecimalDigitsInputFilter import com.tangem.tap.domain.TapError @@ -123,18 +123,18 @@ enum class ButtonState { } data class AmountState( - val amountToExtract: Amount? = null, - val typeOfAmount: AmountType = AmountType.Coin, - val viewAmountValue: InputViewValue = InputViewValue(BigDecimal.ZERO.toPlainString()), - val viewBalanceValue: String = BigDecimal.ZERO.toPlainString(), - val mainCurrency: MainCurrency = MainCurrency(MainCurrencyType.FIAT, TapCurrency.DEFAULT_FIAT_CURRENCY), - val amountToSendCrypto: BigDecimal = BigDecimal.ZERO, - val balanceCrypto: BigDecimal = BigDecimal.ZERO, - val cursorAtTheSamePosition: Boolean = true, - val maxLengthOfAmount: Int = 2, - val decimalSeparator: String = ".", - val error: TapError? = null, - val inputIsEnabled: Boolean = true, + val amountToExtract: Amount? = null, + val typeOfAmount: AmountType = AmountType.Coin, + val viewAmountValue: InputViewValue = InputViewValue(BigDecimal.ZERO.toPlainString()), + val viewBalanceValue: String = BigDecimal.ZERO.toPlainString(), + val mainCurrency: MainCurrency = MainCurrency(MainCurrencyType.FIAT, FiatCurrency.Default.code), + val amountToSendCrypto: BigDecimal = BigDecimal.ZERO, + val balanceCrypto: BigDecimal = BigDecimal.ZERO, + val cursorAtTheSamePosition: Boolean = true, + val maxLengthOfAmount: Int = 2, + val decimalSeparator: String = ".", + val error: TapError? = null, + val inputIsEnabled: Boolean = true, ) : SendScreenState { override val stateId: StateId = StateId.AMOUNT @@ -146,7 +146,7 @@ data class AmountState( fun createMainCurrency(type: MainCurrencyType, canSwitched: Boolean): MainCurrency { return if (!canSwitched) MainCurrency(type, amountToExtract?.currencySymbol ?: "NONE", false) else when (type) { - MainCurrencyType.FIAT -> MainCurrency(type, store.state.globalState.appCurrency) + MainCurrencyType.FIAT -> MainCurrency(type, store.state.globalState.appCurrency.code) MainCurrencyType.CRYPTO -> MainCurrency(type, amountToExtract?.currencySymbol ?: "NONE") } } diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt index d999d79bcb..4fa59376fb 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt @@ -17,7 +17,7 @@ import com.tangem.Message import com.tangem.tangem_sdk_new.extensions.dpToPx import com.tangem.tangem_sdk_new.extensions.hideSoftKeyboard import com.tangem.tap.common.KeyboardObserver -import com.tangem.tap.common.entities.TapCurrency +import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.common.extensions.getFromClipboard import com.tangem.tap.common.extensions.setOnImeActionListener import com.tangem.tap.common.qrCodeScan.ScanQrCodeActivity @@ -261,11 +261,10 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { private fun restoreMainCurrency(): MainCurrencyType { val sp = requireContext().getSharedPreferences("SendScreen", Context.MODE_PRIVATE) - val mainCurrency = sp.getString("mainCurrency", TapCurrency.DEFAULT_FIAT_CURRENCY) - val foundType = MainCurrencyType.values() - .firstOrNull { it.name.equals(mainCurrency!!, ignoreCase = true) } - ?: MainCurrencyType.CRYPTO - return foundType + val mainCurrency = sp.getString("mainCurrency", FiatCurrency.Default.code) + return MainCurrencyType.values() + .firstOrNull { it.name.equals(mainCurrency!!, ignoreCase = true) } + ?: MainCurrencyType.CRYPTO } fun saveMainCurrency(type: MainCurrencyType) { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/models/TotalBalance.kt b/app/src/main/java/com/tangem/tap/features/wallet/models/TotalBalance.kt index 31cc8a8d7f..16676936e5 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/models/TotalBalance.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/models/TotalBalance.kt @@ -1,12 +1,12 @@ package com.tangem.tap.features.wallet.models -import com.tangem.tap.common.redux.global.FiatCurrencyName +import com.tangem.tap.common.entities.FiatCurrency import java.math.BigDecimal data class TotalBalance( val state: State, val fiatAmount: BigDecimal, - val fiatCurrencyName: FiatCurrencyName, + val fiatCurrency: FiatCurrency, ) { enum class State { Loading, diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt index 90796fd143..45e34c15f9 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt @@ -61,8 +61,9 @@ class TradeCryptoMiddleware { action = exchangeAction, blockchain = currency.blockchain, cryptoCurrencyName = currencySymbol, - fiatCurrency = appCurrency, - walletAddress = defaultAddress)?.let { store.dispatchOnMain(NavigationAction.OpenUrl(it)) } + fiatCurrencyName = appCurrency.code, + walletAddress = defaultAddress + )?.let { store.dispatchOnMain(NavigationAction.OpenUrl(it)) } } 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 034122f2c1..8b9033964d 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 @@ -104,7 +104,7 @@ class WalletMiddleware { warningsMiddleware.tryToShowAppRatingWarning(action.wallet) } is WalletAction.LoadFiatRate -> { - val appCurrencyId = globalState.appCurrency + val appCurrencyId = globalState.appCurrency.code scope.launch { val coinsList = when { action.wallet != null -> { @@ -344,4 +344,4 @@ class WalletMiddleware { } } } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt index 21e3ed698f..a7437c94eb 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt @@ -126,7 +126,7 @@ class MultiWalletReducer { ), fiatAmountFormatted = tokenWalletData.fiatRate?.let { action.amount.value - ?.toFiatString(it, store.state.globalState.appCurrency) + ?.toFiatString(it, store.state.globalState.appCurrency.code) }, blockchainAmount = wallet.amounts[AmountType.Coin]?.value ?: BigDecimal.ZERO ), diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/OnWalletLoadedReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/OnWalletLoadedReducer.kt index 1809910e90..3f4b539e20 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/OnWalletLoadedReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/OnWalletLoadedReducer.kt @@ -37,7 +37,7 @@ class OnWalletLoadedReducer { ): WalletState { val walletData = walletState.getWalletData(blockchainNetwork) ?: return walletState - val fiatCurrencySymbol = store.state.globalState.appCurrency + val fiatCurrency = store.state.globalState.appCurrency val exchangeManager = store.state.globalState.currencyExchangeManager val coinAmountValue = wallet.amounts[AmountType.Coin]?.value @@ -65,7 +65,7 @@ class OnWalletLoadedReducer { amount = coinAmountValue, amountFormatted = formattedAmount, fiatAmount = fiatAmount, - fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(fiatCurrencySymbol) + fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(fiatCurrency.code) ), pendingTransactions = pendingTransactions.removeUnknownTransactions(), mainButton = WalletMainButton.SendButton(coinSendButton), @@ -99,7 +99,7 @@ class OnWalletLoadedReducer { token.symbol ), fiatAmount = tokenFiatAmount, - fiatAmountFormatted = tokenFiatAmount?.toFormattedFiatValue(fiatCurrencySymbol) + fiatAmountFormatted = tokenFiatAmount?.toFormattedFiatValue(fiatCurrency.code) ), pendingTransactions = tokenPendingTransactions.removeUnknownTransactions(), mainButton = WalletMainButton.SendButton(tokenSendButton), @@ -112,7 +112,7 @@ class OnWalletLoadedReducer { val totalBalance = TotalBalance( state = wallets.findTotalBalanceState(), fiatAmount = wallets.calculateTotalFiatAmount(), - fiatCurrencyName = fiatCurrencySymbol, + fiatCurrency = fiatCurrency, ) val state = if (wallets.any { it.currencyData.status == BalanceStatus.Loading }) { @@ -132,7 +132,7 @@ class OnWalletLoadedReducer { private fun onSingleWalletLoaded(wallet: Wallet, walletState: WalletState): WalletState { if (wallet.blockchain != walletState.primaryBlockchain) return walletState - val fiatCurrencySymbol = store.state.globalState.appCurrency + val fiatCurrencyName = store.state.globalState.appCurrency.code val exchangeManager = store.state.globalState.currencyExchangeManager val token = wallet.getFirstToken() @@ -141,7 +141,7 @@ class OnWalletLoadedReducer { if (tokenAmount != null) { val tokenFiatRate = walletState.primaryWallet?.currencyData?.token?.fiatRate val tokenFiatAmount = - tokenFiatRate?.let { tokenAmount.value?.toFiatString(it, fiatCurrencySymbol) } + tokenFiatRate?.let { tokenAmount.value?.toFiatString(it, fiatCurrencyName) } TokenData( tokenAmount.value?.toFormattedCurrencyString( token.decimals, token.symbol @@ -161,7 +161,7 @@ class OnWalletLoadedReducer { ) val fiatRate = walletState.primaryWallet?.fiatRate val fiatAmountRaw = fiatRate?.multiply(amount)?.setScale(2, RoundingMode.DOWN) - val fiatAmount = fiatRate?.let { amount?.toFiatString(it, fiatCurrencySymbol) } + val fiatAmount = fiatRate?.let { amount?.toFiatString(it, fiatCurrencyName) } val pendingTransactions = wallet.recentTransactions.toPendingTransactions(wallet.address) val sendButtonEnabled = amount?.isZero() == false && pendingTransactions.isEmpty() 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 97e6e778ec..3219b7cd2f 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 @@ -4,12 +4,12 @@ import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Wallet import com.tangem.domain.common.TwinCardNumber +import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.common.extensions.toFiatString 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.common.redux.AppState -import com.tangem.tap.common.redux.global.FiatCurrencyName import com.tangem.tap.domain.TapError import com.tangem.tap.domain.extensions.getArtworkUrl import com.tangem.tap.domain.getFirstToken @@ -377,22 +377,30 @@ private fun handleCheckSignedHashesActions( private fun setNewFiatRate( fiatRate: Pair, - appCurrency: FiatCurrencyName, state: WalletState + appCurrency: FiatCurrency, + state: WalletState ): WalletState { val rate = fiatRate.second ?: return state - val rateFormatted = rate.toFormattedCurrencyString(2, appCurrency, RoundingMode.HALF_UP) + val rateFormatted = rate.toFormattedCurrencyString( + decimals = 2, + currency = appCurrency.code, + roundingMode = RoundingMode.HALF_UP + ) val currency = fiatRate.first return if (!state.isMultiwalletAllowed) { - setSingeWalletFiatRate(rate, rateFormatted, currency, appCurrency, state) + setSingleWalletFiatRate(rate, rateFormatted, currency, appCurrency, state) } else { setMultiWalletFiatRate(rate, rateFormatted, currency, appCurrency, state) } } private fun setMultiWalletFiatRate( - rate: BigDecimal, rateFormatted: String, currency: Currency, - appCurrency: FiatCurrencyName, state: WalletState + rate: BigDecimal, + rateFormatted: String, + currency: Currency, + appCurrency: FiatCurrency, + state: WalletState ): WalletState { val walletStore = state.getWalletStore(currency) ?: return state @@ -405,7 +413,7 @@ private fun setMultiWalletFiatRate( is Currency.Token -> wallet?.getTokenAmount(currency.token)?.value?.toFiatValue(rate) } - val fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(appCurrency) + val fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(appCurrency.code) val newWalletData = state.getWalletData(currency)?.copy( currencyData = walletData.currencyData.copy( fiatAmountFormatted = fiatAmountFormatted, @@ -416,16 +424,19 @@ private fun setMultiWalletFiatRate( return state.updateWalletData(newWalletData) } -private fun setSingeWalletFiatRate( - rate: BigDecimal, rateFormatted: String, currency: Currency, - appCurrency: FiatCurrencyName, state: WalletState +private fun setSingleWalletFiatRate( + rate: BigDecimal, + rateFormatted: String, + currency: Currency, + appCurrency: FiatCurrency, + state: WalletState ): WalletState { val wallet = state.primaryWalletManager?.wallet ?: return state val token = wallet.getFirstToken() if (currency == state.primaryWallet?.currency) { val fiatAmount = wallet.amounts[AmountType.Coin]?.value - ?.toFiatString(rate, appCurrency) + ?.toFiatString(rate, appCurrency.code) val walletData = state.primaryWallet.copy( currencyData = state.primaryWallet.currencyData.copy(fiatAmountFormatted = fiatAmount), fiatRate = rate, @@ -433,7 +444,9 @@ private fun setSingeWalletFiatRate( ) return state.updateWalletData(walletData) } else if (currency is Currency.Token && currency.token == token) { - val tokenFiatAmount = wallet.getTokenAmount(token)?.value?.toFiatString(rate, appCurrency) + val tokenFiatAmount = wallet.getTokenAmount(token) + ?.value + ?.toFiatString(rate, appCurrency.code) val tokenData = state.primaryWallet?.currencyData?.token?.copy( fiatAmount = tokenFiatAmount, fiatRate = rate, diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt index a6e9c55cba..7a89edc4b0 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt @@ -30,7 +30,7 @@ interface ExchangeUrlBuilder { action: CurrencyExchangeManager.Action, blockchain: Blockchain, cryptoCurrencyName: CryptoCurrencyName, - fiatCurrency: String, + fiatCurrencyName: String, walletAddress: String, ): String? @@ -74,13 +74,19 @@ class CurrencyExchangeManager( action: Action, blockchain: Blockchain, cryptoCurrencyName: CryptoCurrencyName, - fiatCurrency: String, + fiatCurrencyName: String, walletAddress: String, ): String? { if (blockchain.isTestnet()) return blockchain.getTestnetTopUpUrl() val urlBuilder = getExchangeUrlBuilder(action) - return urlBuilder.getUrl(action, blockchain, cryptoCurrencyName, fiatCurrency, walletAddress) + return urlBuilder.getUrl( + action, + blockchain, + cryptoCurrencyName, + fiatCurrencyName, + walletAddress + ) } override fun getSellCryptoReceiptUrl(action: Action, transactionId: String): String? { From 47ffea91f29d7f8803e9294b46ef6047b3e59552 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 18 May 2022 11:29:18 +0300 Subject: [PATCH 3/5] Updated on 2026-08-14 --- .../common/redux/global/GlobalMidlleware.kt | 2 +- .../persistence/FiatCurrenciesPrefStorage.kt | 25 ++++++++++++++++--- .../tap/persistence/PreferencesStorage.kt | 12 --------- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt index 722fe8a067..75dccd51ab 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt @@ -45,7 +45,7 @@ private val globalMiddlewareHandler: Middleware = { dispatch, appState } is GlobalAction.RestoreAppCurrency -> { store.dispatch(GlobalAction.RestoreAppCurrency.Success( - preferencesStorage.getAppCurrency() + preferencesStorage.fiatCurrenciesPrefStorage.getAppCurrency() )) } is GlobalAction.HideWarningMessage -> { diff --git a/app/src/main/java/com/tangem/tap/persistence/FiatCurrenciesPrefStorage.kt b/app/src/main/java/com/tangem/tap/persistence/FiatCurrenciesPrefStorage.kt index 3af9991f25..50a8859516 100644 --- a/app/src/main/java/com/tangem/tap/persistence/FiatCurrenciesPrefStorage.kt +++ b/app/src/main/java/com/tangem/tap/persistence/FiatCurrenciesPrefStorage.kt @@ -4,6 +4,7 @@ import android.content.SharedPreferences import androidx.core.content.edit import com.tangem.common.json.MoshiJsonConverter import com.tangem.network.api.tangemTech.CurrenciesResponse +import com.tangem.tap.common.entities.FiatCurrency /** [REDACTED_AUTHOR] @@ -12,15 +13,25 @@ class FiatCurrenciesPrefStorage( private val preferences: SharedPreferences, private val converter: MoshiJsonConverter, ) { - private val FIAT_CURRENCIES_KEY_OLD = "fiatCurrencies" - private val FIAT_CURRENCIES_KEY = "fiatCurrencies_v2" - fun migrate() { preferences.edit(true) { remove(FIAT_CURRENCIES_KEY_OLD) + remove(APP_CURRENCY_KEY_OLD) } } + fun getAppCurrency(): FiatCurrency { + val json = preferences.getString(APP_CURRENCY_KEY, "") + if (json.isNullOrBlank()) return FiatCurrency.Default + + return converter.fromJson(json) ?: FiatCurrency.Default + } + + fun saveAppCurrency(fiatCurrency: FiatCurrency) { + val json = converter.toJson(fiatCurrency) + preferences.edit { putString(APP_CURRENCY_KEY, json) } + } + fun save(currencies: List) { val json: String = converter.toJson(currencies) return preferences.edit().putString(FIAT_CURRENCIES_KEY, json).apply() @@ -33,4 +44,12 @@ class FiatCurrenciesPrefStorage( return converter.fromJson(json, type) ?: emptyList() } + + companion object { + private const val FIAT_CURRENCIES_KEY_OLD = "fiatCurrencies" + private const val APP_CURRENCY_KEY_OLD = "appCurrency" + + private const val FIAT_CURRENCIES_KEY = "fiatCurrencies_v2" + private const val APP_CURRENCY_KEY = "appCurrency_v2" + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt b/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt index edc53e26fb..f676467852 100644 --- a/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt +++ b/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt @@ -5,8 +5,6 @@ import android.content.Context import android.content.SharedPreferences import androidx.core.content.edit import com.tangem.common.json.MoshiJsonConverter -import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY -import com.tangem.tap.common.redux.global.FiatCurrencyName import java.util.* @@ -27,15 +25,6 @@ class PreferencesStorage(applicationContext: Application) { fiatCurrenciesPrefStorage.migrate() } - fun getAppCurrency(): FiatCurrencyName { - return preferences.getString(APP_CURRENCY_KEY, DEFAULT_FIAT_CURRENCY) - ?: DEFAULT_FIAT_CURRENCY - } - - fun saveAppCurrency(fiatCurrencyName: FiatCurrencyName) { - preferences.edit { putString(APP_CURRENCY_KEY, fiatCurrencyName) } - } - fun getCountOfLaunches(): Int = preferences.getInt(APP_LAUNCH_COUNT_KEY, 1) @Deprecated("Use UsedCardsPrefStorage instead") @@ -74,7 +63,6 @@ class PreferencesStorage(applicationContext: Application) { companion object { private const val PREFERENCES_NAME = "tapPrefs" - private const val APP_CURRENCY_KEY = "appCurrency" private const val DISCLAIMER_ACCEPTED_KEY = "disclaimerAccepted" private const val TWINS_ONBOARDING_SHOWN_KEY = "twinsOnboardingShown" private const val APP_LAUNCH_COUNT_KEY = "launchCount" From 355f67a325419ed6255ce7ab7e31544995bc8e25 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 18 May 2022 11:30:57 +0300 Subject: [PATCH 4/5] Updated on 2026-08-14 --- .../tangem/tap/common/extensions/Specific.kt | 24 +++++++++++++++++-- .../wallet/ui/wallet/MultiWalletView.kt | 10 ++++---- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt b/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt index c7386eb7f0..83f6b0e3aa 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt @@ -54,8 +54,6 @@ fun BigDecimal.toFormattedFiatValue(fiatCurrencyName: String): String { return "≈ ${fiatCurrencyName}  $this" } -fun CurrenciesResponse.Currency.toFormattedString(): String = "${this.name} (${this.code}) - ${this.unit}" - fun BigDecimal.stripZeroPlainString(): String = this.stripTrailingZeros().toPlainString() // 0.00 -> 0.00 @@ -89,4 +87,26 @@ fun BigDecimal.isGreaterThanOrEqual(value: BigDecimal): Boolean { fun BigDecimal.isLessThanOrEqual(value: BigDecimal): Boolean { val compareResult = this.compareTo(value) return compareResult == -1 || compareResult == 0 +} + +fun BigDecimal.formatAmountAsSpannedString( + currencySymbol: String, + integerPartSizeProportion: Float = 1.4f +): SpannedString { + val amount = this.scaleToFiat(applyPrecision = true) + .stripZeroPlainString() + val integer = amount.substringAfter('.') + val reminder = amount.substringBefore('.') + + return buildSpannedString { + append( + integer, + RelativeSizeSpan(integerPartSizeProportion), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + append('.') + append(reminder) + append(' ') + append(currencySymbol) + } } \ No newline at end of file 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 fc97a44777..996be5039a 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 @@ -5,6 +5,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.tangem.common.card.Card import com.tangem.domain.common.TapWorkarounds.derivationStyle import com.tangem.domain.common.TapWorkarounds.isTestCard +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.redux.StateDialog @@ -134,11 +135,10 @@ class MultiWalletView : WalletView { binding: FragmentWalletBinding, totalBalance: TotalBalance, ) = with(binding.lCardTotalBalance) { - val fiatAmountFormatted = with(totalBalance) { - "${fiatAmount.stripTrailingZeros()} $fiatCurrencyName" - } - tvBalance.text = fiatAmountFormatted - tvCurrencyName.text = totalBalance.fiatCurrencyName + tvBalance.text = totalBalance.fiatAmount.formatAmountAsSpannedString( + currencySymbol = totalBalance.fiatCurrency.symbol + ) + tvCurrencyName.text = totalBalance.fiatCurrency.code tvCurrencyName.setOnClickListener { // TODO: Open app currency selector } From 2357aa509f0ac1777c0460ecba95a9520acd2161 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 18 May 2022 11:31:36 +0300 Subject: [PATCH 5/5] Updated on 2026-08-14 --- .../tangem/tap/common/entities/TapCurrency.kt | 10 ---- .../common/redux/global/GlobalMidlleware.kt | 2 +- .../details/redux/DetailsMiddleware.kt | 16 ++++-- .../wallet/redux/reducers/WalletReducer.kt | 24 +++++---- .../network/coinmarketcap/CoinMarketCapApi.kt | 33 ------------ .../coinmarketcap/CoinMarketCapService.kt | 36 ------------- .../tap/network/coinmarketcap/Response.kt | 51 ------------------- 7 files changed, 27 insertions(+), 145 deletions(-) delete mode 100644 app/src/main/java/com/tangem/tap/common/entities/TapCurrency.kt delete mode 100644 app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapApi.kt delete mode 100644 app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapService.kt delete mode 100644 app/src/main/java/com/tangem/tap/network/coinmarketcap/Response.kt diff --git a/app/src/main/java/com/tangem/tap/common/entities/TapCurrency.kt b/app/src/main/java/com/tangem/tap/common/entities/TapCurrency.kt deleted file mode 100644 index 1aa3f5c351..0000000000 --- a/app/src/main/java/com/tangem/tap/common/entities/TapCurrency.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.tangem.tap.common.entities - -/** -[REDACTED_AUTHOR] - */ -class TapCurrency { - companion object{ - const val DEFAULT_FIAT_CURRENCY = "USD" - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt index 75dccd51ab..1e104b5dd4 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt @@ -24,7 +24,7 @@ class GlobalMiddleware { } } -private val globalMiddlewareHandler: Middleware = { dispatch, appState -> +private val globalMiddlewareHandler: Middleware = { _, appState -> { nextDispatch -> { action -> when (action) { diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt index e938026085..4dba1b6bd6 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt @@ -30,7 +30,7 @@ class DetailsMiddleware { private val eraseWalletMiddleware = EraseWalletMiddleware() private val appCurrencyMiddleware = AppCurrencyMiddleware() private val manageSecurityMiddleware = ManageSecurityMiddleware() - val detailsMiddleware: Middleware = { dispatch, state -> + val detailsMiddleware: Middleware = { _, _ -> { next -> { action -> when (action) { @@ -122,6 +122,8 @@ class DetailsMiddleware { store.dispatch(DetailsAction.ResetToFactory.Proceed.NotAllowedByCard) EraseWalletState.NotEmpty -> store.dispatch(DetailsAction.ResetToFactory.Proceed.NotEmpty) + else -> { /* no-op */ + } } } is DetailsAction.ResetToFactory.Cancel -> { @@ -150,6 +152,8 @@ class DetailsMiddleware { } } } + else -> { /* no-op */ + } } } } @@ -164,6 +168,8 @@ class DetailsMiddleware { store.dispatch(GlobalAction.ChangeAppCurrency(action.fiatCurrency)) store.dispatch(WalletAction.LoadFiatRate()) } + else -> { /* no-op */ + } } } } @@ -234,18 +240,22 @@ class DetailsMiddleware { actionToLog = Analytics.ActionToLog.ChangeSecOptions, parameters = mapOf( AnalyticsParam.NEW_SECURITY_OPTION to - (selectedOption?.name ?: "") + (selectedOption?.name ?: "") ), card = store.state.detailsState.scanResponse?.card ) } store.dispatch(DetailsAction.ManageSecurity.SaveChanges.Failure) } + else -> { /* no-op */ + } } } } } + else -> { /* no-op */ + } } } } -} +} \ No newline at end of file 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 3219b7cd2f..eb079d3116 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 @@ -70,9 +70,9 @@ private fun internalReduce(action: Action, state: AppState): WalletState { when (action.error) { is TapError.NoInternetConnection -> { val wallets = newState.wallets - .map { - it.copy( - walletsData = it.walletsData.map { + .map { store -> + store.copy( + walletsData = store.walletsData.map { it.copy( currencyData = it.currencyData.copy( status = BalanceStatus.Unreachable @@ -110,6 +110,8 @@ private fun internalReduce(action: Action, state: AppState): WalletState { ) ) } + else -> { /* no-op */ + } } } @@ -306,23 +308,23 @@ private fun internalReduce(action: Action, state: AppState): WalletState { } is WalletAction.SetWalletRent -> { var walletData = newState.getWalletData(action.blockchain) - if (walletData == null) { - newState - } else { - walletData = - walletData.copy(warningRent = WalletRent(action.minRent, action.rentExempt)) + if (walletData != null) { + walletData = walletData.copy( + warningRent = WalletRent(action.minRent, action.rentExempt) + ) newState = newState.updateWalletsData(listOf(walletData)) + } } is WalletAction.RemoveWalletRent -> { var walletData = newState.getWalletData(action.blockchain) - if (walletData == null) { - newState - } else { + if (walletData != null) { walletData = walletData.copy(warningRent = null) newState = newState.updateWalletsData(listOf(walletData)) } } + else -> { /* no-op */ + } } return newState } diff --git a/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapApi.kt b/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapApi.kt deleted file mode 100644 index 6836861c8c..0000000000 --- a/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapApi.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.tangem.tap.network.coinmarketcap - -import com.tangem.network.common.AddHeaderInterceptor -import com.tangem.network.common.createRetrofitInstance -import retrofit2.http.GET -import retrofit2.http.Query - -interface CoinMarketCapApi { - - @GET("v1/tools/price-conversion") - suspend fun getRateInfo( - @Query("amount") amount: Int, - @Query("symbol") cryptoCurrencyName: String, - @Query("convert") fiatCurrencyName: String? = null - ): RateInfoResponse - - @GET("v1/fiat/map") - suspend fun getFiatMap(): FiatMapResponse - - - companion object { - private const val baseUrl = "https://pro-api.coinmarketcap.com/" - - fun create(apiKey: String): CoinMarketCapApi { - return createRetrofitInstance( - baseUrl = baseUrl, - interceptors = listOf( - AddHeaderInterceptor(mapOf("X-CMC_PRO_API_KEY" to apiKey)), - ), - ).create(CoinMarketCapApi::class.java) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapService.kt b/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapService.kt deleted file mode 100644 index aa4de2bf44..0000000000 --- a/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapService.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.tangem.tap.network.coinmarketcap - -import com.tangem.common.services.Result -import com.tangem.common.services.performRequest -import com.tangem.tap.common.redux.global.FiatCurrencyName -import com.tangem.tap.store -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import java.math.BigDecimal - -//TODO: refactoring: move to the domain module and aggregate it as the alternative service for TangemTech -class CoinMarketCapService() { - private val api: CoinMarketCapApi by lazy { CoinMarketCapApi.create(getApiKey()) } - - private fun getApiKey(): String { - return store.state.globalState.configManager?.config?.coinMarketCapKey ?: "" - } - - suspend fun getRate( - currency: String, fiatCurrency: FiatCurrencyName? = null - ): Result = withContext(Dispatchers.IO) { - val response = performRequest { api.getRateInfo(1, currency, fiatCurrency) } - return@withContext when (response) { - is Result.Success -> Result.Success(response.data.data.getRate()) - is Result.Failure -> response - } - } - - suspend fun getFiatCurrencies(): Result> = withContext(Dispatchers.IO) { - val response = performRequest { api.getFiatMap() } - return@withContext when (response) { - is Result.Success -> Result.Success(response.data.data.sortedBy { it.name }) - is Result.Failure -> response - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/network/coinmarketcap/Response.kt b/app/src/main/java/com/tangem/tap/network/coinmarketcap/Response.kt deleted file mode 100644 index 7320d9467c..0000000000 --- a/app/src/main/java/com/tangem/tap/network/coinmarketcap/Response.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.tangem.tap.network.coinmarketcap - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import java.math.BigDecimal - -@JsonClass(generateAdapter = true) -class RateInfoResponse : CoinMarketResponse() - -@JsonClass(generateAdapter = true) -data class RateData( - val quote: Map -) { - fun getRate(): BigDecimal = quote.values.first().price -} - -@JsonClass(generateAdapter = true) -data class CurrencyRate( - val price: BigDecimal -) - - -@JsonClass(generateAdapter = true) -data class Status( - val timestamp: String, - @Json(name = "error_code") - val errorCode: Int, - @Json(name = "error_message") - val errorMessage: String?, - val elapsed: Int, - @Json(name = "credit_count") - val creditCount: Int, - val notice: String? -) - -@JsonClass(generateAdapter = true) -class FiatMapResponse : CoinMarketResponse>() - -@JsonClass(generateAdapter = true) -open class CoinMarketResponse { - lateinit var status: Status - lateinit var data: T -} - -@JsonClass(generateAdapter = true) -data class FiatCurrency( - val id: Int, - val name: String, - val sign: String, - val symbol: String -)