From 598fa3b3ea8a5c14525b20c62869770801448bf3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 20 May 2022 17:26:32 +0300 Subject: [PATCH 1/4] Updated on 2026-08-14 --- .../tangem/tap/common/extensions/Specific.kt | 54 ++- .../redux/reducers/OnWalletLoadedReducer.kt | 2 +- .../wallet/redux/reducers/WalletReducer.kt | 11 +- .../wallet/ui/adapters/WalletAdapter.kt | 47 +-- .../main/res/layout/item_currency_wallet.xml | 312 ++++++++---------- app/src/main/res/values/styles.xml | 7 +- 6 files changed, 215 insertions(+), 218 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 2d0253eb8c..638995ef6f 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 @@ -39,10 +39,23 @@ fun BigDecimal.toFormattedCurrencyString( return "$formattedAmount $currency" } -fun BigDecimal.toFiatString(rateValue: BigDecimal, fiatCurrencyName: String): String { +fun BigDecimal.toFiatRateString( + fiatCurrencyName: String +): String { + val value = this + .setScale(2, RoundingMode.HALF_UP) + .formatWithSpaces() + return "$value $fiatCurrencyName" +} + +fun BigDecimal.toFiatString( + rateValue: BigDecimal, + fiatCurrencyName: String, + formatWithSpaces: Boolean = false +): String { var fiatValue = rateValue.multiply(this) fiatValue = fiatValue.setScale(2, RoundingMode.HALF_UP) - return "≈ ${fiatCurrencyName}  $fiatValue" + return fiatValue.toFormattedFiatValue(fiatCurrencyName, formatWithSpaces) } fun BigDecimal.toFiatValue(rateValue: BigDecimal): BigDecimal { @@ -50,8 +63,12 @@ fun BigDecimal.toFiatValue(rateValue: BigDecimal): BigDecimal { return fiatValue.setScale(2, RoundingMode.HALF_UP) } -fun BigDecimal.toFormattedFiatValue(fiatCurrencyName: String): String { - return "≈ ${fiatCurrencyName}  $this" +fun BigDecimal.toFormattedFiatValue( + fiatCurrencyName: String, + formatWithSpaces: Boolean = false +): String { + val fiatValue = if (formatWithSpaces) this.formatWithSpaces() else this + return " ${fiatValue}  $fiatCurrencyName" } fun BigDecimal.stripZeroPlainString(): String = this.stripTrailingZeros().toPlainString() @@ -111,4 +128,33 @@ fun BigDecimal.formatAmountAsSpannedString( append(' ') append(currencySymbol) } +} + +fun BigDecimal.formatWithSpaces(): String { + val str = this.toString() + var integerStr = str.substringBefore('.') + val reminderStr = str.substringAfter('.') + val packets = arrayListOf() + + var index: Int = integerStr.length + while (0 < index) { + if (index <= 3) { + packets.add(0, integerStr) + break + } + index -= 3 + packets.add(integerStr.substring(startIndex = index)) + integerStr = integerStr.substring(startIndex = 0, endIndex = index) + } + + return buildString { + packets.forEachIndexed { index, packet -> + append(packet) + if (index != packets.lastIndex) append(' ') + } + if (reminderStr.isNotBlank()) { + append('.') + append(reminderStr) + } + } } \ No newline at end of file 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 4b4d1cce5d..8f73d616d4 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 @@ -65,7 +65,7 @@ class OnWalletLoadedReducer { amount = coinAmountValue, amountFormatted = formattedAmount, fiatAmount = fiatAmount, - fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(fiatCurrency.code) + fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(fiatCurrency.symbol) ), pendingTransactions = pendingTransactions.removeUnknownTransactions(), mainButton = WalletMainButton.SendButton(coinSendButton), 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 eb079d3116..34aaba509d 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 @@ -5,9 +5,9 @@ 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.toFiatRateString 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.domain.TapError @@ -20,7 +20,6 @@ import com.tangem.tap.features.wallet.ui.BalanceWidgetData import com.tangem.tap.store import org.rekotlin.Action import java.math.BigDecimal -import java.math.RoundingMode class WalletReducer { companion object { @@ -383,10 +382,8 @@ private fun setNewFiatRate( state: WalletState ): WalletState { val rate = fiatRate.second ?: return state - val rateFormatted = rate.toFormattedCurrencyString( - decimals = 2, - currency = appCurrency.code, - roundingMode = RoundingMode.HALF_UP + val rateFormatted = rate.toFiatRateString( + fiatCurrencyName = appCurrency.symbol ) val currency = fiatRate.first @@ -415,7 +412,7 @@ private fun setMultiWalletFiatRate( is Currency.Token -> wallet?.getTokenAmount(currency.token)?.value?.toFiatValue(rate) } - val fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(appCurrency.code) + val fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(appCurrency.symbol) val newWalletData = state.getWalletData(currency)?.copy( currencyData = walletData.currencyData.copy( fiatAmountFormatted = fiatAmountFormatted, 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 244c223194..8328274be6 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 @@ -10,7 +10,6 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token import com.tangem.domain.common.TapWorkarounds.derivationStyle import com.tangem.tap.common.extensions.getString -import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.loadCurrenciesIcon import com.tangem.tap.common.extensions.show import com.tangem.tap.features.wallet.redux.Currency @@ -63,9 +62,7 @@ class WalletAdapter fun bind(wallet: WalletData) = with(binding) { tvCurrency.text = wallet.currencyData.currency - tvAmount.text = wallet.currencyData.amountFormatted?.takeWhile { !it.isWhitespace() } - tvCurrencySymbol.text = - wallet.currencyData.amountFormatted?.takeLastWhile { !it.isWhitespace() } + tvAmount.text = wallet.currencyData.amountFormatted.orEmpty() tvAmountFiat.text = wallet.currencyData.fiatAmountFormatted tvExchangeRate.text = wallet.fiatRateString cardWallet.setOnClickListener { @@ -74,8 +71,8 @@ class WalletAdapter val blockchain = wallet.currency.blockchain val token = (wallet.currency as? Currency.Token)?.token - val isCustom = - wallet.currency.isCustomCurrency(store.state.globalState.scanResponse?.card?.derivationStyle) + val isCustom = wallet.currency + .isCustomCurrency(store.state.globalState.scanResponse?.card?.derivationStyle) tvExchangeRate.show(!isCustom) tvCustomCurrency.show(isCustom) @@ -86,43 +83,35 @@ class WalletAdapter ) when (wallet.currencyData.status) { - BalanceStatus.VerifiedOnline, BalanceStatus.SameCurrencyTransactionInProgress -> hideWarning(isCustom) - BalanceStatus.Loading -> { - hideWarning(isCustom) - if (wallet.currencyData.amountFormatted == null) { - tvExchangeRate.text = root.getString(R.string.wallet_balance_loading) - } + BalanceStatus.VerifiedOnline, + BalanceStatus.SameCurrencyTransactionInProgress -> hideMessage() + BalanceStatus.Loading -> if (wallet.currencyData.amountFormatted == null) { + showMessage(root.getString(R.string.wallet_balance_loading)) } BalanceStatus.TransactionInProgress -> - showWarning(root.getString(R.string.wallet_balance_tx_in_progress), isCustom) + showMessage(root.getString(R.string.wallet_balance_tx_in_progress)) BalanceStatus.Unreachable -> - showWarning(root.getString(R.string.wallet_balance_blockchain_unreachable), isCustom) + showMessage(root.getString(R.string.wallet_balance_blockchain_unreachable)) BalanceStatus.NoAccount -> - showWarning(root.getString(R.string.wallet_error_no_account), isCustom) + showMessage(root.getString(R.string.wallet_error_no_account)) else -> { } } } - private fun showWarning(message: String, isCustom: Boolean = false) { - toggleWarning(true, isCustom) - binding.tvStatusErrorMessage.text = message + private fun showMessage(message: String) { + toggleMessage(true) + binding.tvStatus.text = message } - private fun hideWarning(isCustom: Boolean = false) { - toggleWarning(false, isCustom) + private fun hideMessage() { + toggleMessage(false) } - private fun toggleWarning(show: Boolean, isCustom: Boolean = false) { - if (!show) { - binding.tvExchangeRate.show(!isCustom) - binding.tvCustomCurrency.show(isCustom) - } else { - binding.tvExchangeRate.hide() - binding.tvCustomCurrency.hide() - } - binding.tvStatusErrorMessage.show(show) + private fun toggleMessage(show: Boolean) { + binding.tvAmount.show(!show) + binding.tvStatus.show(show) } } } \ No newline at end of file diff --git a/app/src/main/res/layout/item_currency_wallet.xml b/app/src/main/res/layout/item_currency_wallet.xml index a2ebdd3909..aba8fd02f1 100644 --- a/app/src/main/res/layout/item_currency_wallet.xml +++ b/app/src/main/res/layout/item_currency_wallet.xml @@ -1,205 +1,165 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content"> - - + android:layout_marginStart="16dp" + android:layout_marginEnd="16dp" + android:layout_marginBottom="8dp" + android:background="@android:color/white" + android:elevation="3dp"> - - - - - + android:layout_marginTop="12dp" + android:layout_marginBottom="12dp" + android:layout_marginStart="16dp" + android:layout_marginEnd="16dp"> + + + android:id="@+id/tv_token_letter" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginBottom="2dp" + android:textColor="@android:color/white" + android:textSize="25sp" + android:textStyle="bold" + app:layout_constraintBottom_toBottomOf="@id/iv_currency" + app:layout_constraintEnd_toEndOf="@id/iv_currency" + app:layout_constraintStart_toStartOf="@id/iv_currency" + app:layout_constraintTop_toTopOf="@id/iv_currency" + tools:text="J" /> + android:id="@+id/guideline" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + app:layout_constraintGuide_percent="0.5" /> + android:id="@+id/tv_currency" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:ellipsize="end" + android:maxLines="1" + android:textColor="@color/darkGray6" + android:textSize="16sp" + android:textStyle="bold" + app:lineHeight="24dp" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toTopOf="@id/guideline" + app:layout_constraintStart_toEndOf="@id/iv_currency" + app:layout_constraintEnd_toStartOf="@+id/tv_amount" + tools:text="Binance Smart Chain Optimal" /> + + + android:id="@+id/tv_amount_fiat" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:maxEms="12" + android:maxLength="12" + android:maxLines="1" + android:textColor="@color/darkGray6" + android:textSize="16sp" + android:textStyle="bold" + android:textAlignment="viewEnd" + app:lineHeight="24dp" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toTopOf="@id/guideline" + app:layout_constraintStart_toEndOf="@+id/guideline2" + app:layout_constraintEnd_toEndOf="parent" + tools:text="12300.43 $" /> + android:id="@+id/tv_amount" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:maxEms="12" + android:maxLength="12" + android:maxLines="1" + android:textColor="@color/darkGray2" + android:textSize="12sp" + android:textAlignment="viewEnd" + app:lineHeight="20dp" + app:layout_constraintTop_toBottomOf="@id/guideline" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toEndOf="@id/guideline2" + app:layout_constraintEnd_toEndOf="parent" + tools:text="2.002134 BTC" /> + android:id="@+id/tv_status" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:visibility="gone" + android:maxLines="1" + android:ellipsize="end" + android:textColor="@color/darkGray2" + android:textSize="12sp" + android:textAlignment="viewEnd" + app:lineHeight="20dp" + app:layout_constraintTop_toBottomOf="@id/guideline" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toEndOf="@id/guideline2" + app:layout_constraintEnd_toEndOf="parent" + tools:text="Unreachable..." /> + android:id="@+id/tv_exchange_rate" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:maxLines="1" + android:textColor="@color/darkGray2" + android:textSize="12sp" + app:lineHeight="20dp" + app:layout_constraintTop_toBottomOf="@id/guideline" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toEndOf="@id/iv_currency" + app:layout_constraintEnd_toStartOf="@id/guideline2" + tools:text="46 908 $" /> - - + android:id="@+id/tv_custom_currency" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:textColor="@color/darkGray2" + android:textSize="14sp" + android:text="@string/common_custom" + android:visibility="gone" + app:layout_constraintTop_toBottomOf="@id/guideline" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toEndOf="@id/iv_currency" + app:layout_constraintEnd_toStartOf="@id/guideline2" /> - - diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 10c1c9cb80..49c8fefbbb 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -11,6 +11,7 @@ @null @color/menu_accent_color + @style/Widget.AppTheme.CardView + + @@ -163,4 +168,4 @@ - \ No newline at end of file + From 27880a5804e7921cbefc406ca83a77139d0a012b Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 23 May 2022 19:20:41 +0400 Subject: [PATCH 2/4] Updated on 2026-08-14 --- .../java/com/tangem/tap/domain/TapWalletManager.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt index 1bfa9eee08..3349096d24 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -34,17 +34,17 @@ class TapWalletManager { store.state.globalState.configManager?.config?.blockchainSdkConfig ?: BlockchainSdkConfig() } - private val walletManagersThrottler = ThrottlerWithValues>(10000) + private val walletManagersThrottler = ThrottlerWithValues>(10000) suspend fun loadWalletData(walletManager: WalletManager) { val blockchain = walletManager.wallet.blockchain val blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager) - val result = if (walletManagersThrottler.isStillThrottled(blockchain)) { - walletManagersThrottler.geValue(blockchain)!! + val result = if (walletManagersThrottler.isStillThrottled(blockchainNetwork)) { + walletManagersThrottler.geValue(blockchainNetwork)!! } else { val safeUpdateResult = walletManager.safeUpdate() - walletManagersThrottler.updateThrottlingTo(blockchain) - walletManagersThrottler.setValue(blockchain, safeUpdateResult) + walletManagersThrottler.updateThrottlingTo(blockchainNetwork) + walletManagersThrottler.setValue(blockchainNetwork, safeUpdateResult) safeUpdateResult } when (result) { From 7f4854eee32844dff07b561112abc095c0523c6f Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 19 May 2022 12:54:21 +0300 Subject: [PATCH 3/4] Updated on 2026-08-14 --- .../com/tangem/tap/common/DialogManager.kt | 13 ++- .../tangem/tap/common/redux/StateDialog.kt | 13 ++- .../features/details/redux/DetailsAction.kt | 12 -- .../details/redux/DetailsMiddleware.kt | 70 +----------- .../features/details/redux/DetailsReducer.kt | 32 ------ .../features/details/redux/DetailsState.kt | 10 +- .../details/ui/CurrencySelectionDialog.kt | 52 --------- .../features/details/ui/DetailsFragment.kt | 17 --- .../tap/features/wallet/redux/WalletAction.kt | 19 +++- .../tap/features/wallet/redux/WalletState.kt | 10 +- .../middlewares/AppCurrencyMiddleware.kt | 78 +++++++++++++ .../redux/middlewares/WalletMiddleware.kt | 36 +++++- .../redux/reducers/AppCurrencyReducer.kt | 16 +++ .../redux/reducers/OnWalletLoadedReducer.kt | 56 +-------- .../redux/reducers/TotalBalanceOperations.kt | 62 ++++++++++ .../wallet/redux/reducers/WalletReducer.kt | 107 ++++++++++++------ .../tap/features/wallet/ui/WalletFragment.kt | 2 - .../ui/wallet/CurrencySelectionDialog.kt | 41 +++++++ .../wallet/ui/wallet/MultiWalletView.kt | 2 +- app/src/main/res/layout/fragment_details.xml | 51 ++------- 20 files changed, 368 insertions(+), 331 deletions(-) delete mode 100644 app/src/main/java/com/tangem/tap/features/details/ui/CurrencySelectionDialog.kt create mode 100644 app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/AppCurrencyMiddleware.kt create mode 100644 app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/AppCurrencyReducer.kt create mode 100644 app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/TotalBalanceOperations.kt create mode 100644 app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/CurrencySelectionDialog.kt diff --git a/app/src/main/java/com/tangem/tap/common/DialogManager.kt b/app/src/main/java/com/tangem/tap/common/DialogManager.kt index ac00ee4f88..19dd6168cb 100644 --- a/app/src/main/java/com/tangem/tap/common/DialogManager.kt +++ b/app/src/main/java/com/tangem/tap/common/DialogManager.kt @@ -5,7 +5,12 @@ import android.content.Context import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.global.GlobalState import com.tangem.tap.features.details.redux.walletconnect.WalletConnectDialog -import com.tangem.tap.features.details.ui.walletconnect.dialogs.* +import com.tangem.tap.features.details.ui.walletconnect.dialogs.ApproveWcSessionDialog +import com.tangem.tap.features.details.ui.walletconnect.dialogs.BnbTransactionDialog +import com.tangem.tap.features.details.ui.walletconnect.dialogs.ClipboardOrScanQrDialog +import com.tangem.tap.features.details.ui.walletconnect.dialogs.PersonalSignDialog +import com.tangem.tap.features.details.ui.walletconnect.dialogs.SimpleAlertDialog +import com.tangem.tap.features.details.ui.walletconnect.dialogs.TransactionDialog import com.tangem.tap.features.onboarding.AddressInfoBottomSheetDialog import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction import com.tangem.tap.features.onboarding.products.twins.ui.dialog.CreateWalletInterruptDialog @@ -16,6 +21,7 @@ import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.ConfirmDisc import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.UnfinishedBackupFoundDialog import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog import com.tangem.tap.features.wallet.ui.dialogs.SimpleOkDialog +import com.tangem.tap.features.wallet.ui.wallet.CurrencySelectionDialog import com.tangem.tap.store import com.tangem.wallet.R import org.rekotlin.StoreSubscriber @@ -54,7 +60,10 @@ class DialogManager : StoreSubscriber { is AppDialog.ScanFailsDialog -> ScanFailsDialog.create(context) is AppDialog.AddressInfoDialog -> AddressInfoBottomSheetDialog(state.dialog, context) is AppDialog.TestActionsDialog -> TestActionsBottomSheetDialog(state.dialog, context) - is TwinCardsAction.Wallet.ShowInterruptDialog -> CreateWalletInterruptDialog.create(state.dialog, context) + is AppDialog.CurrencySelectionDialog -> + CurrencySelectionDialog.create(state.dialog, context) + is TwinCardsAction.Wallet.ShowInterruptDialog -> + CreateWalletInterruptDialog.create(state.dialog, context) is WalletConnectDialog.UnsupportedCard -> SimpleAlertDialog.create( titleRes = R.string.wallet_connect, 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 77f99b84b5..6181bbbc44 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,6 +2,7 @@ package com.tangem.tap.common.redux import com.tangem.common.extensions.VoidCallback import com.tangem.tap.common.TestAction +import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.features.wallet.redux.AddressData import com.tangem.tap.features.wallet.redux.Currency @@ -12,7 +13,12 @@ interface StateDialog sealed class AppDialog : StateDialog { data class SimpleOkDialog(val header: String, val message: String, val onOk: VoidCallback? = null) : AppDialog() - data class SimpleOkDialogRes(val headerId: Int, val messageId: Int, val onOk: VoidCallback? = null) : AppDialog() + data class SimpleOkDialogRes( + val headerId: Int, + val messageId: Int, + val onOk: VoidCallback? = null + ) : AppDialog() + object ScanFailsDialog : AppDialog() data class AddressInfoDialog( val currency: Currency, @@ -22,4 +28,9 @@ sealed class AppDialog : StateDialog { data class TestActionsDialog( val actionsList: List ) : AppDialog() + + data class CurrencySelectionDialog( + val currenciesList: List, + val currentAppCurrency: FiatCurrency, + ) : AppDialog() } \ No newline at end of file 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 16c5bbd1c8..0739effc65 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,9 +4,7 @@ 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.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.domain.termsOfUse.CardTou import com.tangem.wallet.R @@ -18,9 +16,6 @@ sealed class DetailsAction : Action { val scanResponse: ScanResponse, val wallets: List, val cardTou: CardTou, - val fiatCurrencyName: FiatCurrency, - val fiatCurrencies: List? = null, - val tangemTechService: TangemTechService, ) : DetailsAction() object ShowDisclaimer : DetailsAction() @@ -46,13 +41,6 @@ sealed class DetailsAction : Action { object CreateBackup : DetailsAction() - sealed class AppCurrencyAction : DetailsAction() { - data class SetCurrencies(val currencies: List) : AppCurrencyAction() - object ChooseAppCurrency : AppCurrencyAction() - object Cancel : AppCurrencyAction() - data class SelectAppCurrency(val fiatCurrency: FiatCurrency) : AppCurrencyAction() - } - sealed class ManageSecurity : DetailsAction() { data class CheckCurrentSecurityOption(val card: Card) : ManageSecurity() data class SetCurrentOption(val userCodes: CheckUserCodesResponse) : ManageSecurity() 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 4dba1b6bd6..efed01f9c3 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 @@ -3,24 +3,23 @@ package com.tangem.tap.features.details.redux 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 import com.tangem.tap.common.redux.global.GlobalAction 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.disclaimer.redux.DisclaimerAction import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction import com.tangem.tap.features.wallet.models.hasSendableAmountsOrPendingTransactions -import com.tangem.tap.features.wallet.redux.WalletAction +import com.tangem.tap.scope +import com.tangem.tap.store +import com.tangem.tap.tangemSdkManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -28,15 +27,12 @@ import org.rekotlin.Middleware class DetailsMiddleware { private val eraseWalletMiddleware = EraseWalletMiddleware() - private val appCurrencyMiddleware = AppCurrencyMiddleware() private val manageSecurityMiddleware = ManageSecurityMiddleware() val detailsMiddleware: Middleware = { _, _ -> { next -> { action -> when (action) { - is DetailsAction.PrepareScreen -> prepareData(action) is DetailsAction.ResetToFactory -> eraseWalletMiddleware.handle(action) - is DetailsAction.AppCurrencyAction -> appCurrencyMiddleware.handle(action) is DetailsAction.ManageSecurity -> manageSecurityMiddleware.handle(action) is DetailsAction.ShowDisclaimer -> { store.dispatch(DisclaimerAction.ShowAcceptedDisclaimer) @@ -69,48 +65,6 @@ class DetailsMiddleware { } } - private fun prepareData(action: DetailsAction.PrepareScreen) { - val fiatCurrenciesPrefStorage = preferencesStorage.fiatCurrenciesPrefStorage - val storedFiatCurrencies = fiatCurrenciesPrefStorage.restore() - if (storedFiatCurrencies.isNotEmpty()) { - store.dispatch( - DetailsAction.AppCurrencyAction.SetCurrencies( - currencies = storedFiatCurrencies.mapToUiModel() - ) - ) - } - - scope.launch { - val tangemTechService = action.tangemTechService - when (val result = tangemTechService.currencies()) { - is Result.Success -> { - val currenciesList = result.data.currencies - if (currenciesList.isNotEmpty() && - currenciesList.toSet() != storedFiatCurrencies.toSet() - ) { - fiatCurrenciesPrefStorage.save(currenciesList) - dispatchOnMain( - DetailsAction.AppCurrencyAction.SetCurrencies( - currencies = currenciesList.mapToUiModel() - ) - ) - } - } - is Result.Failure -> {} - } - } - } - - 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) { @@ -158,22 +112,6 @@ class DetailsMiddleware { } } - class AppCurrencyMiddleware { - fun handle(action: DetailsAction.AppCurrencyAction) { - when (action) { - is DetailsAction.AppCurrencyAction.SelectAppCurrency -> { - store.state.globalState.tapWalletManager.rates.clear() - preferencesStorage.fiatCurrenciesPrefStorage - .saveAppCurrency(action.fiatCurrency) - store.dispatch(GlobalAction.ChangeAppCurrency(action.fiatCurrency)) - store.dispatch(WalletAction.LoadFiatRate()) - } - else -> { /* no-op */ - } - } - } - } - class ManageSecurityMiddleware { fun handle(action: DetailsAction.ManageSecurity) { when (action) { 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 a000db965e..5b6eaa198f 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 @@ -29,9 +29,6 @@ private fun internalReduce(action: Action, state: AppState): DetailsState { is DetailsAction.ResetToFactory -> { handleEraseWallet(action, detailsState) } - is DetailsAction.AppCurrencyAction -> { - handleAppCurrencyAction(action, detailsState) - } is DetailsAction.ManageSecurity -> { handleSecurityAction(action, detailsState) } @@ -48,10 +45,6 @@ private fun handlePrepareScreen( scanResponse = action.scanResponse, wallets = action.wallets, cardInfo = action.scanResponse.card.toCardInfo(), - appCurrencyState = state.appCurrencyState.copy( - currentFiatCurrency = action.fiatCurrencyName, - showAppCurrencyDialog = false, - ), cardTermsOfUseUrl = action.cardTou.getUrl(action.scanResponse.card), createBackupAllowed = action.scanResponse.card.backupStatus == Card.BackupStatus.NoBackup, ) @@ -93,31 +86,6 @@ private fun handleEraseWallet( } } -private fun handleAppCurrencyAction( - action: DetailsAction.AppCurrencyAction, state: DetailsState, -): DetailsState { - return when (action) { - is DetailsAction.AppCurrencyAction.SetCurrencies -> { - state.copy(appCurrencyState = state.appCurrencyState.copy(fiatCurrencies = action.currencies)) - } - DetailsAction.AppCurrencyAction.ChooseAppCurrency -> { - state.copy(appCurrencyState = state.appCurrencyState.copy(showAppCurrencyDialog = true)) - } - DetailsAction.AppCurrencyAction.Cancel -> { - state.copy(appCurrencyState = state.appCurrencyState.copy(showAppCurrencyDialog = false)) - } - is DetailsAction.AppCurrencyAction.SelectAppCurrency -> { - state.copy( - appCurrencyState = state.appCurrencyState.copy( - currentFiatCurrency = action.fiatCurrency, - showAppCurrencyDialog = false - ) - ) - } - else -> state - } -} - private fun handleSecurityAction( action: DetailsAction.ManageSecurity, state: DetailsState, ): DetailsState { 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 1315f43e15..eca730c5e2 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 @@ -4,7 +4,6 @@ import android.net.Uri import com.tangem.blockchain.common.Wallet import com.tangem.domain.common.ScanResponse import com.tangem.tap.common.entities.Button -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 @@ -15,7 +14,6 @@ data class DetailsState( val scanResponse: ScanResponse? = null, val wallets: List = emptyList(), val cardInfo: CardInfo? = null, - val appCurrencyState: AppCurrencyState = AppCurrencyState(), val eraseWalletState: EraseWalletState? = null, val confirmScreenState: ConfirmScreenState? = null, val securityScreenState: SecurityScreenState? = null, @@ -48,10 +46,4 @@ data class SecurityScreenState( val buttonProceed: Button = Button(true), ) -enum class SecurityOption { LongTap, PassCode, AccessCode } - -data class AppCurrencyState( - val currentFiatCurrency: FiatCurrency = FiatCurrency.Default, - val showAppCurrencyDialog: Boolean = false, - val fiatCurrencies: List? = null, -) \ No newline at end of file +enum class SecurityOption { LongTap, PassCode, AccessCode } \ 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 deleted file mode 100644 index 23e79bd846..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/ui/CurrencySelectionDialog.kt +++ /dev/null @@ -1,52 +0,0 @@ -package com.tangem.tap.features.details.ui - -import android.content.Context -import androidx.appcompat.app.AlertDialog -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 - -class CurrencySelectionDialog { - - var dialog: AlertDialog? = null - - fun show( - currenciesList: List, - currentAppCurrency: FiatCurrency, - context: Context - ) { - if (dialog == null) { - 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( - fiatCurrency = selectedCurrency - ) - ) - } - .setOnDismissListener { - store.dispatch(DetailsAction.AppCurrencyAction.Cancel) - } - .setSingleChoiceItems(currenciesToShow, currentSelection) { _, which -> - currentSelection = which - }.show() - } - } - - fun clear() { - dialog = null - } - -} \ No newline at end of file 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 2dbab2ee0b..d4ad890b8b 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 @@ -24,7 +24,6 @@ import org.rekotlin.StoreSubscriber class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber { - private var currencySelectionDialog = CurrencySelectionDialog() private val binding: FragmentDetailsBinding by viewBinding(FragmentDetailsBinding::bind) override fun onCreate(savedInstanceState: Bundle?) { @@ -115,11 +114,6 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber null } currentSecurity?.let { tvSecurity.text = getString(it) } - - if (state.appCurrencyState.showAppCurrencyDialog && - !state.appCurrencyState.fiatCurrencies.isNullOrEmpty()) { - currencySelectionDialog.show( - currenciesList = state.appCurrencyState.fiatCurrencies, - currentAppCurrency = state.appCurrencyState.currentFiatCurrency, - context = requireContext() - ) - } else { - currencySelectionDialog.clear() - } } } \ No newline at end of file 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 c4e722e6c3..ce2ce4b63d 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 @@ -1,17 +1,22 @@ package com.tangem.tap.features.wallet.redux import android.content.Context -import com.tangem.blockchain.common.* +import com.tangem.blockchain.common.Amount +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.Token +import com.tangem.blockchain.common.Wallet +import com.tangem.blockchain.common.WalletManager import com.tangem.blockchain.common.address.AddressType import com.tangem.common.card.Card +import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.common.redux.ErrorAction 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.tokens.BlockchainNetwork import com.tangem.wallet.R -import org.rekotlin.Action import java.math.BigDecimal +import org.rekotlin.Action sealed class WalletAction : Action { @@ -98,7 +103,10 @@ sealed class WalletAction : Action { data class LoadFiatRate( val wallet: Wallet? = null, val coinsList: List? = null, ) : WalletAction() { - data class Success(val fiatRate: Pair) : WalletAction() + data class Success( + val fiatRates: Map + ) : WalletAction() + object Failure : WalletAction() } @@ -163,4 +171,9 @@ sealed class WalletAction : Action { ) : WalletAction() data class RemoveWalletRent(val wallet: Wallet) : WalletAction() + + sealed class AppCurrencyAction : WalletAction() { + object ChooseAppCurrency : AppCurrencyAction() + data class SelectAppCurrency(val fiatCurrency: FiatCurrency) : AppCurrencyAction() + } } \ 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 6df77f4a30..eda197d115 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 @@ -1,7 +1,13 @@ package com.tangem.tap.features.wallet.redux import android.graphics.Bitmap -import com.tangem.blockchain.common.* +import com.tangem.blockchain.common.Amount +import com.tangem.blockchain.common.AmountType +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.DerivationStyle +import com.tangem.blockchain.common.Token +import com.tangem.blockchain.common.Wallet +import com.tangem.blockchain.common.WalletManager import com.tangem.blockchain.common.address.AddressType import com.tangem.blockchain.extensions.isAboveZero import com.tangem.common.extensions.isZero @@ -25,8 +31,8 @@ import com.tangem.tap.features.wallet.ui.BalanceStatus import com.tangem.tap.features.wallet.ui.BalanceWidgetData import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager import com.tangem.tap.store -import org.rekotlin.StateType import java.math.BigDecimal +import org.rekotlin.StateType import kotlin.properties.ReadOnlyProperty data class WalletState( diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/AppCurrencyMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/AppCurrencyMiddleware.kt new file mode 100644 index 0000000000..cac8319b82 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/AppCurrencyMiddleware.kt @@ -0,0 +1,78 @@ +package com.tangem.tap.features.wallet.redux.middlewares + +import com.tangem.common.services.Result +import com.tangem.network.api.tangemTech.CurrenciesResponse +import com.tangem.network.api.tangemTech.TangemTechService +import com.tangem.tap.common.entities.FiatCurrency +import com.tangem.tap.common.extensions.dispatchDialogShow +import com.tangem.tap.common.redux.AppDialog +import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.domain.TapWalletManager +import com.tangem.tap.features.wallet.redux.WalletAction +import com.tangem.tap.persistence.FiatCurrenciesPrefStorage +import com.tangem.tap.scope +import com.tangem.tap.store +import kotlinx.coroutines.launch + +class AppCurrencyMiddleware( + private val tangemTechService: TangemTechService, + private val tapWalletManager: TapWalletManager, + private val fiatCurrenciesPrefStorage: FiatCurrenciesPrefStorage, + private val appCurrencyProvider: () -> FiatCurrency, +) { + fun handle(action: WalletAction.AppCurrencyAction) { + when (action) { + is WalletAction.AppCurrencyAction.ChooseAppCurrency -> showSelector() + is WalletAction.AppCurrencyAction.SelectAppCurrency -> selectCurrency(action) + } + } + + private fun showSelector() { + val storedFiatCurrencies = fiatCurrenciesPrefStorage.restore() + if (storedFiatCurrencies.isNotEmpty()) { + store.dispatchDialogShow( + AppDialog.CurrencySelectionDialog( + currenciesList = storedFiatCurrencies.mapToUiModel(), + currentAppCurrency = appCurrencyProvider.invoke() + ) + ) + } + + scope.launch { + when (val result = tangemTechService.currencies()) { + is Result.Success -> { + val currenciesList = result.data.currencies + if (currenciesList.isNotEmpty() && + currenciesList.toSet() != storedFiatCurrencies.toSet() + ) { + fiatCurrenciesPrefStorage.save(currenciesList) + store.dispatchDialogShow( + AppDialog.CurrencySelectionDialog( + currenciesList = storedFiatCurrencies.mapToUiModel(), + currentAppCurrency = appCurrencyProvider.invoke() + ) + ) + } + } + is Result.Failure -> {} + } + } + } + + private fun selectCurrency(action: WalletAction.AppCurrencyAction.SelectAppCurrency) { + tapWalletManager.rates.clear() + fiatCurrenciesPrefStorage.saveAppCurrency(action.fiatCurrency) + store.dispatch(GlobalAction.ChangeAppCurrency(action.fiatCurrency)) + store.dispatch(WalletAction.LoadFiatRate()) + } + + private fun List.mapToUiModel(): List { + return this.map { + FiatCurrency( + code = it.code, + name = it.name, + symbol = it.unit + ) + } + } +} \ No newline at end of file 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 3f3c285830..e2d7c6d4b5 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 @@ -15,7 +15,13 @@ import com.tangem.domain.common.extensions.withMainContext import com.tangem.operations.attestation.Attestation import com.tangem.operations.attestation.OnlineCardVerifier import com.tangem.tap.common.analytics.Analytics -import com.tangem.tap.common.extensions.* +import com.tangem.tap.common.extensions.copyToClipboard +import com.tangem.tap.common.extensions.dispatchDebugErrorNotification +import com.tangem.tap.common.extensions.dispatchDialogShow +import com.tangem.tap.common.extensions.dispatchOnMain +import com.tangem.tap.common.extensions.onCardScanned +import com.tangem.tap.common.extensions.shareText +import com.tangem.tap.common.extensions.stripZeroPlainString import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction @@ -29,11 +35,17 @@ import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.send.redux.PrepareSendScreen import com.tangem.tap.features.wallet.models.PendingTransactionType import com.tangem.tap.features.wallet.models.getPendingTransactions -import com.tangem.tap.features.wallet.redux.* +import com.tangem.tap.features.wallet.redux.Currency +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.network.NetworkStateChanged +import com.tangem.tap.preferencesStorage import com.tangem.tap.scope import com.tangem.tap.store import com.tangem.tap.tangemSdkManager +import java.math.BigDecimal import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.launch @@ -41,12 +53,19 @@ import org.rekotlin.Action import org.rekotlin.DispatchFunction import org.rekotlin.Middleware import timber.log.Timber -import java.math.BigDecimal class WalletMiddleware { private val tradeCryptoMiddleware = TradeCryptoMiddleware() private val warningsMiddleware = WarningsMiddleware() private val multiWalletMiddleware = MultiWalletMiddleware() + private val appCurrencyMiddleware by lazy(mode = LazyThreadSafetyMode.NONE) { + AppCurrencyMiddleware( + tangemTechService = store.state.domainNetworks.tangemTechService, + tapWalletManager = store.state.globalState.tapWalletManager, + fiatCurrenciesPrefStorage = preferencesStorage.fiatCurrenciesPrefStorage, + appCurrencyProvider = { store.state.globalState.appCurrency }, + ) + } val walletMiddleware: Middleware = { dispatch, state -> { next -> @@ -71,6 +90,7 @@ class WalletMiddleware { walletState, globalState ) + is WalletAction.AppCurrencyAction -> appCurrencyMiddleware.handle(action) is WalletAction.LoadWallet -> { scope.launch { if (action.blockchain == null) { @@ -121,11 +141,15 @@ class WalletMiddleware { ) when (ratesResult) { is Result.Success -> { - ratesResult.data.loadedRates.forEach { - dispatchOnMain(WalletAction.LoadFiatRate.Success(it.toPair())) + ratesResult.data.loadedRates.let { + dispatchOnMain(WalletAction.LoadFiatRate.Success(it)) } ratesResult.data.failedRates.forEach { (currency, throwable) -> - Timber.e(throwable, "Loading rates failed for [%s]", currency.currencySymbol) + Timber.e( + throwable, + "Loading rates failed for [%s]", + currency.currencySymbol + ) } } is Result.Failure -> { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/AppCurrencyReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/AppCurrencyReducer.kt new file mode 100644 index 0000000000..a098368d06 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/AppCurrencyReducer.kt @@ -0,0 +1,16 @@ +package com.tangem.tap.features.wallet.redux.reducers + +import com.tangem.tap.features.wallet.redux.WalletAction +import com.tangem.tap.features.wallet.redux.WalletState + +class AppCurrencyReducer { + fun reduce( + action: WalletAction.AppCurrencyAction, + state: WalletState, + ): WalletState { + return when (action) { + is WalletAction.AppCurrencyAction.SelectAppCurrency, + is WalletAction.AppCurrencyAction.ChooseAppCurrency -> state + } + } +} \ No newline at end of file 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 8f73d616d4..ce9b5cf80a 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,7 +9,6 @@ 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.* @@ -17,7 +16,6 @@ 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 { @@ -109,12 +107,6 @@ class OnWalletLoadedReducer { val newWallets = tokens + newWalletData val wallets = walletState.replaceSomeWallets((newWallets)) - val totalBalance = TotalBalance( - state = wallets.findTotalBalanceState(), - fiatAmount = wallets.calculateTotalFiatAmount(), - fiatCurrency = fiatCurrency, - ) - val state = if (wallets.any { it.currencyData.status == BalanceStatus.Loading }) { ProgressState.Loading } else { @@ -122,7 +114,9 @@ class OnWalletLoadedReducer { } return walletState .updateWalletsData(wallets) - .updateTotalBalance(totalBalance) + .updateTotalBalance( + totalBalance = obtainTotalBalance(wallets, fiatCurrency) + ) .copy( state = state, error = null @@ -192,48 +186,4 @@ 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.SomeTokensFailed - 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.SomeTokensFailed, - TotalBalance.State.Success -> newState - } - TotalBalance.State.Success, - TotalBalance.State.SomeTokensFailed -> when (newState) { - TotalBalance.State.Loading, - TotalBalance.State.SomeTokensFailed -> newState - TotalBalance.State.Success -> this - } - } - } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/TotalBalanceOperations.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/TotalBalanceOperations.kt new file mode 100644 index 0000000000..c7169452ee --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/TotalBalanceOperations.kt @@ -0,0 +1,62 @@ +package com.tangem.tap.features.wallet.redux.reducers + +import com.tangem.tap.common.entities.FiatCurrency +import com.tangem.tap.features.wallet.models.TotalBalance +import com.tangem.tap.features.wallet.redux.WalletData +import com.tangem.tap.features.wallet.ui.BalanceStatus +import java.math.BigDecimal + +fun obtainTotalBalance( + wallets: List, + appCurrency: FiatCurrency +): TotalBalance { + return TotalBalance( + state = wallets.findTotalBalanceState(), + fiatAmount = wallets.calculateTotalFiatAmount(), + fiatCurrency = appCurrency + ) +} + +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.SomeTokensFailed + 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.SomeTokensFailed, + TotalBalance.State.Success -> newState + } + TotalBalance.State.Success, + TotalBalance.State.SomeTokensFailed -> when (newState) { + TotalBalance.State.Loading, + TotalBalance.State.SomeTokensFailed -> newState + TotalBalance.State.Success -> this + } + } +} \ 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 f419a43d59..8f9fd630ca 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 @@ -3,6 +3,7 @@ package com.tangem.tap.features.wallet.redux.reducers import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Wallet +import com.tangem.common.extensions.mapNotNullValues import com.tangem.domain.common.TwinCardNumber import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.common.extensions.toFiatRateString @@ -14,13 +15,26 @@ import com.tangem.tap.domain.TapError import com.tangem.tap.domain.extensions.getArtworkUrl import com.tangem.tap.domain.getFirstToken import com.tangem.tap.domain.tokens.BlockchainNetwork -import com.tangem.tap.features.wallet.models.WalletRent -import com.tangem.tap.features.wallet.redux.* +import com.tangem.tap.features.wallet.redux.AddressData +import com.tangem.tap.features.wallet.redux.Artwork +import com.tangem.tap.features.wallet.redux.Currency +import com.tangem.tap.features.wallet.redux.ErrorType +import com.tangem.tap.features.wallet.redux.ProgressState +import com.tangem.tap.features.wallet.redux.TradeCryptoState +import com.tangem.tap.features.wallet.redux.WalletAction +import com.tangem.tap.features.wallet.redux.WalletAddresses +import com.tangem.tap.features.wallet.redux.WalletData +import com.tangem.tap.features.wallet.redux.WalletDialog +import com.tangem.tap.features.wallet.redux.WalletMainButton +import com.tangem.tap.features.wallet.redux.WalletRent +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.store -import org.rekotlin.Action import java.math.BigDecimal +import java.math.RoundingMode +import org.rekotlin.Action class WalletReducer { companion object { @@ -32,6 +46,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState { val multiWalletReducer = MultiWalletReducer() val onWalletLoadedReducer = OnWalletLoadedReducer() + val appCurrencyReducer = AppCurrencyReducer() if (action !is WalletAction) return state.walletState @@ -260,7 +275,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState { } is WalletAction.LoadFiatRate.Success -> - newState = setNewFiatRate(action.fiatRate, state.globalState.appCurrency, newState) + newState = setNewFiatRate(action.fiatRates, state.globalState.appCurrency, newState) is WalletAction.LoadArtwork -> { val artworkUrl = action.card.getArtworkUrl(action.artworkId) ?: when (state.twinCardsState.cardNumber) { @@ -314,6 +329,9 @@ private fun internalReduce(action: Action, state: AppState): WalletState { val walletsData = walletStore.walletsData.map { it.copy(walletRent = null) } newState = newState.updateWalletsData(walletsData) } + is WalletAction.AppCurrencyAction -> { + newState = appCurrencyReducer.reduce(action, newState) + } else -> { /* no-op */ } } @@ -369,50 +387,71 @@ private fun handleCheckSignedHashesActions( private fun setNewFiatRate( - fiatRate: Pair, + fiatRates: Map, appCurrency: FiatCurrency, state: WalletState ): WalletState { - val rate = fiatRate.second ?: return state - val rateFormatted = rate.toFiatRateString( - fiatCurrencyName = appCurrency.symbol - ) - val currency = fiatRate.first + val rateFormatter: (BigDecimal) -> String = { rate: BigDecimal -> + rate.toFiatRateString( + fiatCurrencyName = appCurrency.symbol + ) + } - return if (!state.isMultiwalletAllowed) { - setSingleWalletFiatRate(rate, rateFormatted, currency, appCurrency, state) + return if (state.isMultiwalletAllowed) { + setMultiWalletFiatRate( + fiatRates = fiatRates.mapNotNullValues { it.value }, + rateFormatter = rateFormatter, + appCurrency = appCurrency, + state = state + ) } else { - setMultiWalletFiatRate(rate, rateFormatted, currency, appCurrency, state) + val fiatRate = fiatRates.entries.firstOrNull() + val currency = fiatRate?.key ?: return state + val rate = fiatRate.value ?: return state + + setSingleWalletFiatRate( + rate = rate, + rateFormatted = rateFormatter(rate), + currency = currency, + appCurrency = appCurrency, + state = state + ) } } private fun setMultiWalletFiatRate( - rate: BigDecimal, - rateFormatted: String, - currency: Currency, + fiatRates: Map, + rateFormatter: (BigDecimal) -> String, appCurrency: FiatCurrency, state: WalletState ): WalletState { + val newWalletsData = fiatRates.mapNotNull { (currency, rate) -> + val walletStore = state.getWalletStore(currency) ?: return state + val wallet = walletStore.walletManager?.wallet + val walletData = state.getWalletData(currency) ?: return state - val walletStore = state.getWalletStore(currency) ?: return state - val wallet = walletStore.walletManager?.wallet - val walletData = state.getWalletData(currency) ?: return state - - val fiatAmount = when (currency) { - is Currency.Blockchain -> - wallet?.amounts?.get(AmountType.Coin)?.value?.toFiatValue(rate) - is Currency.Token -> - wallet?.getTokenAmount(currency.token)?.value?.toFiatValue(rate) + val fiatAmount = when (currency) { + is Currency.Blockchain -> + wallet?.amounts?.get(AmountType.Coin)?.value?.toFiatValue(rate) + is Currency.Token -> + wallet?.getTokenAmount(currency.token)?.value?.toFiatValue(rate) + } + val fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(appCurrency.symbol) + state.getWalletData(currency)?.copy( + currencyData = walletData.currencyData.copy( + fiatAmountFormatted = fiatAmountFormatted, + fiatAmount = fiatAmount + ), + fiatRate = rate, + fiatRateString = rateFormatter(rate) + ) } - val fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(appCurrency.symbol) - val newWalletData = state.getWalletData(currency)?.copy( - currencyData = walletData.currencyData.copy( - fiatAmountFormatted = fiatAmountFormatted, - fiatAmount = fiatAmount - ), - fiatRate = rate, fiatRateString = rateFormatted - ) - return state.updateWalletData(newWalletData) + + return state + .updateWalletsData(newWalletsData) + .updateTotalBalance( + totalBalance = obtainTotalBalance(newWalletsData, appCurrency) + ) } private fun setSingleWalletFiatRate( 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 acb6309e60..9c4661dbb8 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 @@ -178,8 +178,6 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber /* no-op */ } + .setPositiveButton(context.getString(R.string.common_done)) { _, _ -> + val selectedCurrency = dialog.currenciesList[currentSelection] + store.dispatch( + WalletAction.AppCurrencyAction.SelectAppCurrency( + fiatCurrency = selectedCurrency + ) + ) + } + .setOnDismissListener { + store.dispatchDialogHide() + } + .setSingleChoiceItems(currenciesToShow, currentSelection) { _, which -> + currentSelection = which + } + .create() + } +} \ 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 e5d39024ac..63be6cb214 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 @@ -158,7 +158,7 @@ class MultiWalletView : WalletView { tvCurrencyName.text = totalBalance.fiatCurrency.code tvCurrencyName.setOnClickListener { - // TODO: Open app currency selector + store.dispatch(WalletAction.AppCurrencyAction.ChooseAppCurrency) } } diff --git a/app/src/main/res/layout/fragment_details.xml b/app/src/main/res/layout/fragment_details.xml index 58ac038846..e62b7edc5d 100644 --- a/app/src/main/res/layout/fragment_details.xml +++ b/app/src/main/res/layout/fragment_details.xml @@ -216,47 +216,20 @@ android:background="@color/separatorGrey2" app:layout_constraintTop_toBottomOf="@id/tv_settings_title" /> - - - - - + android:layout_height="wrap_content" + android:drawablePadding="15dp" + android:paddingTop="7dp" + android:paddingBottom="7dp" + android:text="@string/disclaimer_title" + android:textColor="@color/darkGray6" + android:textSize="16sp" + app:drawableEndCompat="@drawable/ic_angle_bracket_right" + app:drawableTint="@color/darkGray1" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/tv_settings_title" /> - \ No newline at end of file + From a00215b12910e87d67be87bd793645a46a37d324 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 19 May 2022 22:58:34 +0300 Subject: [PATCH 4/4] Updated on 2026-08-14 --- .../com/tangem/tap/common/extensions/UI.kt | 44 +++++-------------- .../wallet/ui/wallet/MultiWalletView.kt | 32 ++++++-------- 2 files changed, 26 insertions(+), 50 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/extensions/UI.kt b/app/src/main/java/com/tangem/tap/common/extensions/UI.kt index 4e4ec7260a..8c2184ae50 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/UI.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/UI.kt @@ -1,7 +1,5 @@ package com.tangem.tap.common.extensions -import android.animation.Animator -import android.animation.AnimatorListenerAdapter import android.app.Activity import android.content.* import android.content.res.Resources @@ -192,45 +190,27 @@ fun View.getString(resId: Int, vararg formatArgs: Any?): String { return context.getString(resId, formatArgs) } -fun View.showAnimated(durationMillis: Long = 300) { - this.animateVisibility( - show = true, - durationMillis = durationMillis - ) -} - -fun View.hideAnimated( - durationMillis: Long = 300, - hiddenVisibility: Int = View.GONE -) { - this.animateVisibility( - show = false, - durationMillis = durationMillis, - hiddenVisibility = hiddenVisibility - ) -} - -private fun View.animateVisibility( +fun View.animateVisibility( show: Boolean, - durationMillis: Long = 300, + durationMillis: Long = SHORT_ANIMATION_DURATION, hiddenVisibility: Int = View.GONE ) { - if (this.isVisible == show) return if (show) { - this.alpha = 0f - this.isVisible = true this.animate() .alpha(1f) .setDuration(durationMillis) - .setListener(null) + .withStartAction { + this.alpha = 0f + this.isVisible = true + } } else { this.animate() .alpha(0f) .setDuration(durationMillis) - .setListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator?) { - this@animateVisibility.visibility = hiddenVisibility - } - }) + .withStartAction { + this.visibility = hiddenVisibility + } } -} \ No newline at end of file +} + +private const val SHORT_ANIMATION_DURATION = 80L \ 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 63be6cb214..271f6bb67b 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 @@ -6,7 +6,10 @@ 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.* +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.redux.StateDialog import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction @@ -134,23 +137,16 @@ class MultiWalletView : WalletView { binding: FragmentWalletBinding, totalBalance: TotalBalance, ) = with(binding.lCardTotalBalance) { - when (totalBalance.state) { - TotalBalance.State.Loading -> { - pbLoading.showAnimated() - tvBalance.hideAnimated(hiddenVisibility = View.INVISIBLE) - tvProcessing.hideAnimated() - } - TotalBalance.State.SomeTokensFailed -> { - tvBalance.showAnimated() - pbLoading.hideAnimated() - tvProcessing.showAnimated() - } - TotalBalance.State.Success -> { - tvBalance.showAnimated() - pbLoading.hideAnimated() - tvProcessing.hideAnimated() - } - } + tvBalance.animateVisibility( + show = totalBalance.state != TotalBalance.State.Loading, + hiddenVisibility = View.INVISIBLE + ) + pbLoading.animateVisibility( + show = totalBalance.state == TotalBalance.State.Loading + ) + tvProcessing.animateVisibility( + show = totalBalance.state == TotalBalance.State.SomeTokensFailed + ) tvBalance.text = totalBalance.fiatAmount.formatAmountAsSpannedString( currencySymbol = totalBalance.fiatCurrency.symbol