diff --git a/core/res/src/main/res/values-pt-rBR/strings.xml b/core/res/src/main/res/values-pt-rBR/strings.xml
index e931a23bdc..e011607a12 100644
--- a/core/res/src/main/res/values-pt-rBR/strings.xml
+++ b/core/res/src/main/res/values-pt-rBR/strings.xml
@@ -80,6 +80,10 @@
Adicionar tokens
Escolha o token que deseja receber.
Escolha o token que deseja trocar.
+ Adicione ao seu portfólio
+ Adicionar tokens
+ Classificação e agrupamento
+ Organizar tokens
Escolha a rede
Adicionar token personalizado
Gerenciar tokens
@@ -584,6 +588,8 @@
Escolha qual token usar para pagar a taxa de rede. %s
Escolha o token
Mercado e Notícias
+ Mostrar tudo
+ Mostrar menos
Em alta agora
As informações a seguir são opcionais. Você pode apagá-las se não quiser compartilhá-las.
Diga-nos quais funções estão faltando e tentaremos ajudá-lo.
@@ -719,6 +725,7 @@
Limite de mana
A rede Koinos exige Mana para o pagamento das taxas de rede. Você tem %1$s/%2$s Mana
Nível de mana
+ Adicionar e gerenciar
Para começar a rastrear seus criptoativos e transações, adicione tokens.
Gerenciar tokens
Leia o código QR para enviar fundos ou conectar-se a um aplicativo
@@ -1534,6 +1541,7 @@
Negociação em grande escala
Teremos todo o prazer em receber seu feedback.
O Tangem Pay agora está em versão beta.
+ Não foi possível renomear o cartão.
Cartão bloqueado
Pagamento com cartão
Depósito
@@ -1567,7 +1575,9 @@
Seu cartão foi desbloqueado.
Retirada
Não é possível usar em dispositivos com root.
+ Saldo disponível
Ocultar KYC da tela principal
+ Cartão Tangem Pay 1
Adicionar fundos
Opções de recarga
Adicionar ao Google Wallet
@@ -1600,10 +1610,13 @@
Compartilhe seu endereço ou mostre o código QR.
Problemas técnicos detectados. Tente novamente mais tarde ou entre em contato com o suporte.
Receber indisponível agora
+ Somente letras e números são permitidos.
+ Caracteres inválidos
Revelar
Mostrar detalhes
Troque qualquer ativo da sua carteira por um cartão.
Detalhes do cartão
+ Por favor, tente novamente mais tarde.
Descongelar cartão
Volte ao aplicativo se você se esquecer.
Seu código PIN
@@ -1611,12 +1624,16 @@
Retirada indisponível agora
Você não pode iniciar uma troca ou um novo saque até que o atual seja concluído.
Retirada em andamento
+ Configurações do cartão
Alterar código PIN
Volte ao aplicativo se você se esquecer.
+ Cartão digital
Entendo que perderei completamente o acesso ao meu cartão Tangem Pay e a todos os fundos nele contidos, sem possibilidade de recuperação.
Falha na emissão do cartão
Ocorreu um erro técnico. Tente novamente clicando no botão abaixo.
Ocorreu um erro técnico. Por favor, entre em contato com o suporte.
+ O recurso estará disponível em breve.
+ Você poderá emitir cartões adicionais para sua conta de pagamento.
Obtenha seu cartão virtual Visa Tangem grátis.
Obtenha o Tangem Pay
Acesse o Suporte
@@ -1648,6 +1665,7 @@
Uma conta de pagamento separada será criada sem divulgar seus endereços e bens.
Privacidade incomparável
Obtenha seu cartão Tangem Pay gratuito em minutos.
+ Suporte de pagamento
Conta de pagamento
A conta de pagamento não está sincronizada.
PIN inválido: evite sequências ou repetições.
@@ -1660,6 +1678,7 @@
Use USDC para pagamentos do dia a dia.
O serviço Tangem Pay está temporariamente inacessível.
Tangem Pay
+ USDC na rede Polygon
Clique no botão abaixo para restaurar o acesso.
O saldo do seu Polygon em USDC na blockchain é diferente do saldo do seu cartão e é atualizado em até 2 dias úteis após uma compra. Os fundos de compras reembolsadas não serão devolvidos ao seu saldo na blockchain nem estarão disponíveis para saque, mas permanecerão no saldo do seu cartão para compras futuras.
Observe
@@ -1690,6 +1709,10 @@
Serviço de staking
%1$s token em %%imagem%% %2$s rede
Token em %%imagem%% %1$s rede
+ %s rede
+ %1$s em %2$s rede
+ %1$s em %%imagem%% %2$s
+ %1$s em %2$s %%imagem%%
O %1$s (%2$sO token ) é a principal moeda da plataforma. %3$s rede e não pode ser ocultada enquanto você tiver outros tokens dessa rede na lista
Não foi possível ocultar %s
N/A
diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index dd706dc203..1ec9514666 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -80,6 +80,10 @@
Add tokens
Choose the token you want to receive
Choose the token you want to swap
+ Add to your portfolio
+ Add tokens
+ Sorting and grouping
+ Organize tokens
Choose network
Add custom token
Manage tokens
@@ -477,6 +481,11 @@
Send funds using only
Dynamic addresses
Dynamic Addresses is enabled. Custom \"change\" and \"index\" are unavailable.
+ You are turning off Dynamic Addresses. Funds will now be received on your fixed address.
+ A network fee applies to consolidate funds into the fixed address.
+ Disable Dynamic Addresses
+ Disable dynamic addresses
+ Dynamic addresses disabled
Dynamic addresses enabled
Use a new address for each transaction to reduce traceability and improve on-chain privacy.
Enhanced Privacy
@@ -584,6 +593,8 @@
Choose which token to use to pay\nthe network fee. %s
Choose token
Market & News
+ Show All
+ Show less
Tangem AI
Trending Now
The following information is optional. You can erase it if you don\'t want to share it.
@@ -720,6 +731,7 @@
Mana limit
The Koinos network requires Mana for network fees. You have %1$s/%2$s Mana
Mana level
+ Add & Manage
To begin tracking your crypto assets and transactions, add tokens
Manage tokens
Scan QR code to send funds or connect to an app
@@ -1569,7 +1581,9 @@
Your card is unfrozen.
Withdrawal
Unable to use on rooted device
+ Available balance
Hide KYC from main screen
+ Tangem Pay Card 1
Add funds
Top-up options
Add to Google Wallet
@@ -1619,10 +1633,13 @@
Card settings
Change PIN-code
Come back to the app if you forget it.
+ Digital card
I understand that I will completely lose access to my Tangem Pay Card and all funds on it without the possibility of recovery
Failed to issue card
A technical error has occurred, please try again by clicking the button below.
A technical error has occurred, please contact support.
+ Feature will be availible soon
+ You will be available to issue additional cards for your payment account
Get your free Tangem Visa virtual card
Get Tangem Pay
Go to Support
@@ -1654,6 +1671,7 @@
A separate payment account will be created without disclosing your addresses and assets
Unrivaled privacy
Get your free Tangem Pay Card in minutes
+ Pay Support
Payment account
Payment account is not synced
Invalid PIN: avoid sequences or repeats
@@ -1666,6 +1684,7 @@
Use USDC for everyday payments
Tangem Pay is temporarily unreachable
Tangem Pay
+ USDC on Polygon network
Click the button below to restore access
Your on-chain USDC Polygon balance differs from your card balance and updates within 2 business days after a purchase. Funds from refunded purchases won’t be returned your on-chain balance or be available for withdrawal, but will stay on your card balance for purchases.
Please note
diff --git a/data/dynamic-addresses/src/main/java/com/tangem/data/dynamicaddresses/DefaultConsolidationRepository.kt b/data/dynamic-addresses/src/main/java/com/tangem/data/dynamicaddresses/DefaultConsolidationRepository.kt
index 0860a7b9ce..c1b88d69d2 100644
--- a/data/dynamic-addresses/src/main/java/com/tangem/data/dynamicaddresses/DefaultConsolidationRepository.kt
+++ b/data/dynamic-addresses/src/main/java/com/tangem/data/dynamicaddresses/DefaultConsolidationRepository.kt
@@ -1,10 +1,12 @@
package com.tangem.data.dynamicaddresses
import arrow.core.Either
+import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.DynamicAddressesManager
import com.tangem.blockchain.common.TransactionData
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.common.transaction.Fee
+import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.blockchain.extensions.Result
import com.tangem.domain.dynamicaddresses.repository.ConsolidationRepository
import com.tangem.domain.models.network.Network
@@ -33,13 +35,25 @@ internal class DefaultConsolidationRepository(
when (val result = dynamicAddressesManager.createConsolidationTransaction(fee)) {
is Result.Success -> result.data
- is Result.Failure -> error("Failed to create consolidation tx: ${result.error}")
+ is Result.Failure -> throw result.error
}
}
}
- @Suppress("UnusedParameter")
- private fun getNormalFee(walletManager: WalletManager): Fee {
- TODO("Fee calculation for consolidation from multiple addresses is not yet implemented")
+ private suspend fun getNormalFee(walletManager: WalletManager): Fee {
+ val coinAmount = walletManager.wallet.amounts[AmountType.Coin]
+ ?: error("Coin amount not found")
+ val feeResult = walletManager.getFee(
+ amount = coinAmount,
+ destination = walletManager.wallet.address,
+ )
+
+ return when (feeResult) {
+ is Result.Success -> when (val txFee = feeResult.data) {
+ is TransactionFee.Single -> txFee.normal
+ is TransactionFee.Choosable -> txFee.normal
+ }
+ is Result.Failure -> throw feeResult.error
+ }
}
}
\ No newline at end of file
diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/DynamicAddressesDelegate.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/DynamicAddressesDelegate.kt
index e9fe02cce1..a6a548aa4e 100644
--- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/DynamicAddressesDelegate.kt
+++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/DynamicAddressesDelegate.kt
@@ -1,42 +1,65 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.model
import com.tangem.common.core.TangemSdkError
+import com.tangem.core.decompose.di.GlobalUiMessageSender
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.res.R
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.SnackbarMessage
+import com.tangem.common.ui.amountScreen.utils.getFiatString
+import com.tangem.domain.appcurrency.model.AppCurrency
+import com.tangem.domain.dynamicaddresses.CreateConsolidationTransactionUseCase
+import com.tangem.domain.dynamicaddresses.DisableDynamicAddressesUseCase
import com.tangem.domain.dynamicaddresses.EnableDynamicAddressesError
import com.tangem.domain.dynamicaddresses.EnableDynamicAddressesUseCase
import com.tangem.domain.dynamicaddresses.IsXpubDerivedUseCase
+import com.tangem.domain.dynamicaddresses.model.DynamicAddressesStatus
import com.tangem.domain.dynamicaddresses.repository.DynamicAddressesRepository
+import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.network.Network
-import com.tangem.domain.models.wallet.UserWalletId
+import com.tangem.domain.models.wallet.UserWallet
+import com.tangem.utils.Provider
+import com.tangem.domain.transaction.error.SendTransactionError
+import com.tangem.domain.transaction.usecase.GetFeeUseCase
+import com.tangem.domain.transaction.usecase.SendTransactionUseCase
import com.tangem.domain.wallets.usecase.GetExtendedPublicKeyForCurrencyUseCase
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.dynamicaddresses.DynamicAddressesBottomSheetConfig
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
+import com.tangem.utils.coroutines.runSuspendCatching
import com.tangem.utils.logging.TangemLogger
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
@Suppress("LongParameterList")
-internal class DynamicAddressesDelegate(
+internal class DynamicAddressesDelegate @AssistedInject constructor(
private val enableDynamicAddressesUseCase: EnableDynamicAddressesUseCase,
+ private val disableDynamicAddressesUseCase: DisableDynamicAddressesUseCase,
+ private val createConsolidationTransactionUseCase: CreateConsolidationTransactionUseCase,
+ private val getFeeUseCase: GetFeeUseCase,
+ private val sendTransactionUseCase: SendTransactionUseCase,
private val isXpubDerivedUseCase: IsXpubDerivedUseCase,
private val dynamicAddressesRepository: DynamicAddressesRepository,
private val getExtendedPublicKeyUseCase: GetExtendedPublicKeyForCurrencyUseCase,
- private val uiMessageSender: UiMessageSender,
- private val userWalletId: UserWalletId,
- private val network: Network,
- private val coroutineScope: CoroutineScope,
+ @GlobalUiMessageSender private val uiMessageSender: UiMessageSender,
private val dispatchers: CoroutineDispatcherProvider,
- private val showBottomSheet: () -> Unit,
- private val dismissBottomSheet: () -> Unit,
- private val onDynamicAddressesEnabled: () -> Unit,
+ @Assisted private val userWallet: UserWallet,
+ @Assisted private val cryptoCurrencyStatusProvider: Provider,
+ @Assisted private val appCurrencyProvider: Provider,
+ @Assisted private val coroutineScope: CoroutineScope,
+ @Assisted("showBottomSheet") private val showBottomSheet: () -> Unit,
+ @Assisted("dismissBottomSheet") private val dismissBottomSheet: () -> Unit,
+ @Assisted("onDynamicAddressesStateChanged") private val onDynamicAddressesStateChanged: () -> Unit,
) {
+ private val userWalletId get() = userWallet.walletId
+
private val _bottomSheetConfig = MutableStateFlow(
DynamicAddressesBottomSheetConfig.Enable(
isCardScanRequired = false,
@@ -45,27 +68,45 @@ internal class DynamicAddressesDelegate(
)
val bottomSheetConfig: StateFlow = _bottomSheetConfig.asStateFlow()
- fun onDynamicAddressesClick() {
- coroutineScope.launch(dispatchers.main) {
- val hasConflicts = dynamicAddressesRepository.hasConflictingCustomTokens(userWalletId, network)
- if (hasConflicts) {
- _bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.Unavailable(
- onGotItClick = dismissBottomSheet,
- )
- showBottomSheet()
- return@launch
- }
+ // region Entry point
- val isCardScanRequired = !isXpubAlreadyDerived()
- _bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.Enable(
- isCardScanRequired = isCardScanRequired,
- onEnableClick = ::onEnableClick,
- )
- showBottomSheet()
+ fun onDynamicAddressesClick() {
+ val network = cryptoCurrencyStatusProvider()?.currency?.network ?: return
+ coroutineScope.launch(dispatchers.main) {
+ val status = dynamicAddressesRepository.getStatus(userWalletId, network).first()
+ when (status) {
+ DynamicAddressesStatus.ENABLED,
+ DynamicAddressesStatus.ENABLED_REQUIRES_SETUP,
+ -> onDisableFlow(network)
+ DynamicAddressesStatus.DISABLED -> onEnableFlow(network)
+ }
}
}
+ // endregion
+
+ // region Enable flow
+
+ private suspend fun onEnableFlow(network: Network) {
+ val hasConflicts = dynamicAddressesRepository.hasConflictingCustomTokens(userWalletId, network)
+ if (hasConflicts) {
+ _bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.ConflictingCustomTokens(
+ onDismissClick = dismissBottomSheet,
+ )
+ showBottomSheet()
+ return
+ }
+
+ val isCardScanRequired = !isXpubAlreadyDerived(network)
+ _bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.Enable(
+ isCardScanRequired = isCardScanRequired,
+ onEnableClick = ::onEnableClick,
+ )
+ showBottomSheet()
+ }
+
private fun onEnableClick() {
+ val network = cryptoCurrencyStatusProvider()?.currency?.network ?: return
coroutineScope.launch(dispatchers.main) {
_bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.Enable(
isCardScanRequired = false,
@@ -80,7 +121,7 @@ internal class DynamicAddressesDelegate(
} else {
TangemLogger.e("Failed to get XPUB: ${error.message}")
_bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.ServiceUnavailable(
- onGotItClick = dismissBottomSheet,
+ onDismissClick = dismissBottomSheet,
)
}
return@launch
@@ -92,21 +133,21 @@ internal class DynamicAddressesDelegate(
ifLeft = { error ->
when (error) {
is EnableDynamicAddressesError.ConflictingCustomTokens -> {
- _bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.Unavailable(
- onGotItClick = dismissBottomSheet,
+ _bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.ConflictingCustomTokens(
+ onDismissClick = dismissBottomSheet,
)
}
is EnableDynamicAddressesError.ServiceError -> {
TangemLogger.e("Failed to enable DA: ${error.cause.message}")
_bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.ServiceUnavailable(
- onGotItClick = dismissBottomSheet,
+ onDismissClick = dismissBottomSheet,
)
}
}
},
ifRight = {
dismissBottomSheet()
- onDynamicAddressesEnabled()
+ onDynamicAddressesStateChanged()
uiMessageSender.send(
SnackbarMessage(message = resourceReference(R.string.dynamic_addresses_enabled_toast_title)),
)
@@ -115,11 +156,208 @@ internal class DynamicAddressesDelegate(
}
}
- private suspend fun isXpubAlreadyDerived(): Boolean {
+ // endregion
+
+ // region Disable flow
+
+ private fun onDisableFlow(network: Network) {
+ coroutineScope.launch(dispatchers.main) {
+ disableDynamicAddressesUseCase(userWalletId, network).fold(
+ ifLeft = { error ->
+ TangemLogger.e("Failed to check disable: ${error.message}")
+ _bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.ServiceUnavailable(
+ onDismissClick = dismissBottomSheet,
+ )
+ showBottomSheet()
+ },
+ ifRight = { isConsolidationRequired ->
+ if (!isConsolidationRequired) {
+ showSimpleDisableSheet()
+ } else {
+ showDisableSheetAndLoadFee()
+ }
+ },
+ )
+ }
+ }
+
+ private fun showSimpleDisableSheet() {
+ _bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.DisableWithoutConsolidation(
+ onDisableClick = ::onSimpleDisableClick,
+ onReadMoreClick = ::onReadMoreClick,
+ )
+ showBottomSheet()
+ }
+
+ private fun onSimpleDisableClick() {
+ val network = cryptoCurrencyStatusProvider()?.currency?.network ?: return
+ coroutineScope.launch(dispatchers.main) {
+ runSuspendCatching { dynamicAddressesRepository.disable(userWalletId, network) }
+ .onSuccess {
+ dismissBottomSheet()
+ onDynamicAddressesStateChanged()
+ uiMessageSender.send(
+ SnackbarMessage(message = resourceReference(R.string.dynamic_addresses_disabled_popup_title)),
+ )
+ }
+ .onFailure { e ->
+ TangemLogger.e("Failed to disable DA: ${e.message}")
+ _bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.ServiceUnavailable(
+ onDismissClick = dismissBottomSheet,
+ )
+ }
+ }
+ }
+
+ private fun showDisableSheetAndLoadFee() {
+ _bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.DisableWithConsolidation(
+ feeState = DynamicAddressesBottomSheetConfig.DisableFeeState.Loading,
+ onDisableClick = ::onDisableClick,
+ onRefreshFee = ::loadDisableFee,
+ onReadMoreClick = ::onReadMoreClick,
+ )
+ showBottomSheet()
+ loadDisableFee()
+ }
+
+ private fun loadDisableFee() {
+ coroutineScope.launch(dispatchers.main) {
+ _bottomSheetConfig.value = disableWithConsolidationConfig().copy(
+ feeState = DynamicAddressesBottomSheetConfig.DisableFeeState.Loading,
+ )
+
+ val status = cryptoCurrencyStatusProvider()
+ val currency = status?.currency
+ val balance = status?.value?.amount
+ val address = status?.value?.networkAddress?.defaultAddress?.value
+
+ if (currency == null || balance == null || address == null) {
+ _bottomSheetConfig.value = disableWithConsolidationConfig().copy(
+ feeState = DynamicAddressesBottomSheetConfig.DisableFeeState.Error,
+ )
+ return@launch
+ }
+
+ getFeeUseCase(
+ amount = balance,
+ destination = address,
+ userWallet = userWallet,
+ cryptoCurrency = currency,
+ ).fold(
+ ifLeft = {
+ _bottomSheetConfig.value = disableWithConsolidationConfig().copy(
+ feeState = DynamicAddressesBottomSheetConfig.DisableFeeState.Error,
+ )
+ },
+ ifRight = { txFee ->
+ val fee = txFee.normal
+ val fiatFormatted = getFiatString(
+ value = fee.amount.value,
+ rate = status.value.fiatRate,
+ appCurrency = appCurrencyProvider(),
+ approximate = true,
+ )
+ _bottomSheetConfig.value = disableWithConsolidationConfig().copy(
+ feeState = DynamicAddressesBottomSheetConfig.DisableFeeState.Content(
+ feeSymbol = currency.symbol,
+ fiatFormatted = fiatFormatted,
+ ),
+ )
+ },
+ )
+ }
+ }
+
+ private fun disableWithConsolidationConfig(): DynamicAddressesBottomSheetConfig.DisableWithConsolidation {
+ return _bottomSheetConfig.value as? DynamicAddressesBottomSheetConfig.DisableWithConsolidation
+ ?: DynamicAddressesBottomSheetConfig.DisableWithConsolidation(
+ onDisableClick = ::onDisableClick,
+ onRefreshFee = ::loadDisableFee,
+ onReadMoreClick = ::onReadMoreClick,
+ )
+ }
+
+ // TODO: Replace with actual DA documentation URL
+ private fun onReadMoreClick() {
+ // Stub: open documentation about Dynamic Addresses consolidation
+ }
+
+ private fun onDisableClick() {
+ val network = cryptoCurrencyStatusProvider()?.currency?.network ?: return
+ coroutineScope.launch(dispatchers.main) {
+ _bottomSheetConfig.value = disableWithConsolidationConfig().copy(
+ isSending = true,
+ )
+
+ val txData = createConsolidationTransactionUseCase(userWalletId, network).fold(
+ ifLeft = { error ->
+ TangemLogger.e("Failed to create consolidation tx: ${error.message}")
+ _bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.ServiceUnavailable(
+ onDismissClick = dismissBottomSheet,
+ )
+ return@launch
+ },
+ ifRight = { it },
+ )
+
+ sendTransactionUseCase(
+ txData = txData,
+ userWallet = userWallet,
+ network = network,
+ ).fold(
+ ifLeft = { error ->
+ if (error is SendTransactionError.UserCancelledError) {
+ dismissBottomSheet()
+ } else {
+ TangemLogger.e("Failed to send consolidation tx: $error")
+ _bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.ServiceUnavailable(
+ onDismissClick = dismissBottomSheet,
+ )
+ }
+ },
+ ifRight = {
+ try {
+ dynamicAddressesRepository.disable(userWalletId, network)
+ } catch (e: Exception) {
+ TangemLogger.e("Failed to disable DA after consolidation: ${e.message}")
+ }
+ dismissBottomSheet()
+ onDynamicAddressesStateChanged()
+ uiMessageSender.send(
+ SnackbarMessage(
+ message = resourceReference(R.string.dynamic_addresses_disabled_popup_title),
+ ),
+ )
+ },
+ )
+ }
+ }
+
+ // endregion
+
+ // region Common
+
+ private suspend fun isXpubAlreadyDerived(network: Network): Boolean {
return isXpubDerivedUseCase(userWalletId, network)
}
private fun isUserCancellation(error: Throwable): Boolean {
return error is TangemSdkError.UserCancelled || error.cause is TangemSdkError.UserCancelled
}
+
+ // endregion
+
+ @AssistedFactory
+ interface Factory {
+ @Suppress("LongParameterList")
+ fun create(
+ userWallet: UserWallet,
+ cryptoCurrencyStatusProvider: Provider,
+ appCurrencyProvider: Provider,
+ coroutineScope: CoroutineScope,
+ @Assisted("showBottomSheet") showBottomSheet: () -> Unit,
+ @Assisted("dismissBottomSheet") dismissBottomSheet: () -> Unit,
+ @Assisted("onDynamicAddressesStateChanged") onDynamicAddressesStateChanged: () -> Unit,
+ ): DynamicAddressesDelegate
+ }
}
\ No newline at end of file
diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt
index f5def6e3a1..bb946cf2a8 100644
--- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt
+++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt
@@ -11,10 +11,7 @@ import com.tangem.blockchain.common.address.AddressType
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.dynamicaddresses.DynamicAddressesFeatureToggles
import com.tangem.domain.dynamicaddresses.DynamicAddressesSupportedBlockchains
-import com.tangem.domain.dynamicaddresses.EnableDynamicAddressesUseCase
-import com.tangem.domain.dynamicaddresses.IsXpubDerivedUseCase
import com.tangem.domain.dynamicaddresses.IsXpubSupportedUseCase
-import com.tangem.domain.dynamicaddresses.repository.DynamicAddressesRepository
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.common.ui.bottomsheet.receive.AddressModel
@@ -170,11 +167,9 @@ internal class TokenDetailsModel @Inject constructor(
private val manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase,
private val yieldSupplyGetRewardsBalanceUseCase: YieldSupplyGetRewardsBalanceUseCase,
private val signCloreMessageUseCase: SignCloreMessageUseCase,
- private val enableDynamicAddressesUseCase: EnableDynamicAddressesUseCase,
private val isXpubSupportedUseCase: IsXpubSupportedUseCase,
- private val isXpubDerivedUseCase: IsXpubDerivedUseCase,
- private val dynamicAddressesRepository: DynamicAddressesRepository,
private val dynamicAddressesFeatureToggles: DynamicAddressesFeatureToggles,
+ private val dynamicAddressesDelegateFactory: DynamicAddressesDelegate.Factory,
private val dialogFactory: TokenDetailsDialogFactory,
private val userWalletsListRepository: UserWalletsListRepository,
private val singleAccountListSupplier: SingleAccountListSupplier,
@@ -246,21 +241,16 @@ internal class TokenDetailsModel @Inject constructor(
// region Dynamic Addresses
val dynamicAddressesDelegate by lazy(mode = LazyThreadSafetyMode.NONE) {
- DynamicAddressesDelegate(
- enableDynamicAddressesUseCase = enableDynamicAddressesUseCase,
- isXpubDerivedUseCase = isXpubDerivedUseCase,
- dynamicAddressesRepository = dynamicAddressesRepository,
- getExtendedPublicKeyUseCase = getExtendedPublicKeyForCurrencyUseCase,
- uiMessageSender = uiMessageSender,
- userWalletId = userWalletId,
- network = cryptoCurrency.network,
+ dynamicAddressesDelegateFactory.create(
+ userWallet = userWallet,
+ cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
+ appCurrencyProvider = Provider { selectedAppCurrencyFlow.value },
coroutineScope = modelScope,
- dispatchers = dispatchers,
showBottomSheet = {
bottomSheetNavigation.activate(TokenDetailsBottomSheetConfig.DynamicAddresses)
},
dismissBottomSheet = bottomSheetNavigation::dismiss,
- onDynamicAddressesEnabled = ::onDynamicAddressesEnabled,
+ onDynamicAddressesStateChanged = ::onDynamicAddressesStateChanged,
)
}
// endregion Dynamic Addresses
@@ -707,7 +697,7 @@ internal class TokenDetailsModel @Inject constructor(
override fun onDynamicAddressesClick() = dynamicAddressesDelegate.onDynamicAddressesClick()
- private fun onDynamicAddressesEnabled() {
+ private fun onDynamicAddressesStateChanged() {
updateTopBarMenu()
modelScope.launch(dispatchers.main) {
cryptoCurrencyBalanceFetcher.invokeAndAwait(userWalletId = userWalletId, currency = cryptoCurrency)
diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/dynamicaddresses/DynamicAddressesBottomSheet.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/dynamicaddresses/DynamicAddressesBottomSheet.kt
index a12557f768..556fd79f30 100644
--- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/dynamicaddresses/DynamicAddressesBottomSheet.kt
+++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/dynamicaddresses/DynamicAddressesBottomSheet.kt
@@ -4,12 +4,14 @@ import androidx.compose.runtime.Composable
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
+import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.R as CoreR
@Composable
internal fun DynamicAddressesBottomSheet(config: TangemBottomSheetConfig) {
TangemModalBottomSheet(
config = config,
+ containerColor = TangemTheme.colors.background.tertiary,
title = {
TangemModalBottomSheetTitle(
endIconRes = CoreR.drawable.ic_close_24,
@@ -19,10 +21,18 @@ internal fun DynamicAddressesBottomSheet(config: TangemBottomSheetConfig) {
) { content ->
when (content) {
is DynamicAddressesBottomSheetConfig.Enable -> DynamicAddressesEnableContent(content = content)
- is DynamicAddressesBottomSheetConfig.Unavailable -> DynamicAddressesUnavailableContent(content = content)
- is DynamicAddressesBottomSheetConfig.ServiceUnavailable -> DynamicAddressesServiceUnavailableContent(
- content = content,
- )
+ is DynamicAddressesBottomSheetConfig.DisableWithoutConsolidation -> {
+ DynamicAddressesDisableWithoutConsolidationContent(content = content)
+ }
+ is DynamicAddressesBottomSheetConfig.DisableWithConsolidation -> {
+ DynamicAddressesDisableWithConsolidationContent(content = content)
+ }
+ is DynamicAddressesBottomSheetConfig.ConflictingCustomTokens -> {
+ DynamicAddressesConflictingCustomTokensContent(content = content)
+ }
+ is DynamicAddressesBottomSheetConfig.ServiceUnavailable -> {
+ DynamicAddressesServiceUnavailableContent(content = content)
+ }
}
}
}
\ No newline at end of file
diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/dynamicaddresses/DynamicAddressesBottomSheetConfig.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/dynamicaddresses/DynamicAddressesBottomSheetConfig.kt
index a228fb29b2..7270a74d1e 100644
--- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/dynamicaddresses/DynamicAddressesBottomSheetConfig.kt
+++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/dynamicaddresses/DynamicAddressesBottomSheetConfig.kt
@@ -12,11 +12,33 @@ internal sealed class DynamicAddressesBottomSheetConfig : TangemBottomSheetConfi
val onEnableClick: () -> Unit,
) : DynamicAddressesBottomSheetConfig()
- data class Unavailable(
- val onGotItClick: () -> Unit,
+ data class DisableWithoutConsolidation(
+ val onDisableClick: () -> Unit,
+ val onReadMoreClick: () -> Unit,
+ ) : DynamicAddressesBottomSheetConfig()
+
+ data class DisableWithConsolidation(
+ val feeState: DisableFeeState = DisableFeeState.Loading,
+ val isSending: Boolean = false,
+ val onDisableClick: () -> Unit,
+ val onRefreshFee: () -> Unit,
+ val onReadMoreClick: () -> Unit,
+ ) : DynamicAddressesBottomSheetConfig()
+
+ sealed interface DisableFeeState {
+ data object Loading : DisableFeeState
+ data class Content(
+ val feeSymbol: String,
+ val fiatFormatted: String,
+ ) : DisableFeeState
+ data object Error : DisableFeeState
+ }
+
+ data class ConflictingCustomTokens(
+ val onDismissClick: () -> Unit,
) : DynamicAddressesBottomSheetConfig()
data class ServiceUnavailable(
- val onGotItClick: () -> Unit,
+ val onDismissClick: () -> Unit,
) : DynamicAddressesBottomSheetConfig()
}
\ No newline at end of file
diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/dynamicaddresses/DynamicAddressesBottomSheetContent.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/dynamicaddresses/DynamicAddressesBottomSheetContent.kt
index fb1eaa7605..1b40922d1d 100644
--- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/dynamicaddresses/DynamicAddressesBottomSheetContent.kt
+++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/dynamicaddresses/DynamicAddressesBottomSheetContent.kt
@@ -1,5 +1,7 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.dynamicaddresses
+import android.content.res.Configuration
+import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -14,12 +16,28 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.LinkAnnotation
+import androidx.compose.ui.text.SpanStyle
+import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.withLink
+import androidx.compose.ui.text.withStyle
+import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.PrimaryButton
+import com.tangem.core.ui.components.PrimaryButtonIconEnd
+import com.tangem.core.ui.components.TextShimmer
+import com.tangem.core.ui.components.audits.AuditLabel
+import com.tangem.core.ui.components.audits.AuditLabelUM
+import com.tangem.core.ui.components.notifications.Notification
+import com.tangem.core.ui.components.notifications.NotificationConfig
+import com.tangem.core.ui.extensions.resourceReference
+import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
+import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.res.R
import com.tangem.core.ui.R as CoreR
+import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.dynamicaddresses.DynamicAddressesBottomSheetConfig.DisableFeeState
@Composable
internal fun DynamicAddressesEnableContent(content: DynamicAddressesBottomSheetConfig.Enable) {
@@ -72,13 +90,13 @@ internal fun DynamicAddressesEnableContent(content: DynamicAddressesBottomSheetC
Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing24))
- PrimaryButton(
+ PrimaryButtonIconEnd(
text = stringResourceSafe(id = R.string.dynamic_addresses_enter_main_button_title),
+ iconResId = if (content.isCardScanRequired) CoreR.drawable.ic_tangem_24 else null,
onClick = content.onEnableClick,
modifier = Modifier.fillMaxWidth(),
showProgress = content.isLoading,
enabled = !content.isLoading,
- // TODO add card icon when isCardScanRequired
)
Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing16))
@@ -86,12 +104,205 @@ internal fun DynamicAddressesEnableContent(content: DynamicAddressesBottomSheetC
}
@Composable
-internal fun DynamicAddressesUnavailableContent(content: DynamicAddressesBottomSheetConfig.Unavailable) {
+internal fun DynamicAddressesDisableWithoutConsolidationContent(
+ content: DynamicAddressesBottomSheetConfig.DisableWithoutConsolidation,
+) {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = TangemTheme.dimens.spacing16),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ DisableHeader()
+
+ Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing24))
+
+ PrimaryButtonIconEnd(
+ text = stringResourceSafe(id = R.string.dynamic_addresses_disable_main_button_title),
+ iconResId = null,
+ onClick = content.onDisableClick,
+ modifier = Modifier.fillMaxWidth(),
+ )
+
+ Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing16))
+ }
+}
+
+@Composable
+internal fun DynamicAddressesDisableWithConsolidationContent(
+ content: DynamicAddressesBottomSheetConfig.DisableWithConsolidation,
+) {
+ val isConfirmEnabled = content.feeState is DisableFeeState.Content && !content.isSending
+
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = TangemTheme.dimens.spacing16),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ DisableHeader()
+
+ Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing16))
+
+ DisableFeeBlock(
+ feeState = content.feeState,
+ onReadMoreClick = content.onReadMoreClick,
+ )
+
+ if (content.feeState is DisableFeeState.Error) {
+ Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing12))
+
+ Notification(
+ config = NotificationConfig(
+ title = resourceReference(R.string.send_fee_unreachable_error_title),
+ subtitle = resourceReference(R.string.send_fee_unreachable_error_text),
+ iconResId = CoreR.drawable.ic_alert_24,
+ iconTint = NotificationConfig.IconTint.Warning,
+ buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
+ text = resourceReference(R.string.warning_button_refresh),
+ onClick = content.onRefreshFee,
+ ),
+ ),
+ containerColor = TangemTheme.colors.background.action,
+ )
+ }
+
+ Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing24))
+
+ PrimaryButtonIconEnd(
+ text = stringResourceSafe(id = R.string.dynamic_addresses_disable_main_button_title),
+ iconResId = CoreR.drawable.ic_tangem_24,
+ onClick = content.onDisableClick,
+ modifier = Modifier.fillMaxWidth(),
+ showProgress = content.isSending,
+ enabled = isConfirmEnabled,
+ )
+
+ Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing16))
+ }
+}
+
+@Composable
+private fun DisableHeader() {
+ Icon(
+ painter = painterResource(id = CoreR.drawable.ic_dynamic_addresses_bottomsheet_enable_unavailable),
+ contentDescription = null,
+ modifier = Modifier.size(TangemTheme.dimens.size44),
+ tint = TangemTheme.colors.icon.attention,
+ )
+
+ Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing12))
+
+ Text(
+ text = stringResourceSafe(id = R.string.dynamic_addresses_disable_title),
+ style = TangemTheme.typography.h3,
+ color = TangemTheme.colors.text.primary1,
+ textAlign = TextAlign.Center,
+ )
+
+ Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing8))
+
+ Text(
+ text = stringResourceSafe(id = R.string.dynamic_addresses_disable_description),
+ style = TangemTheme.typography.body2,
+ color = TangemTheme.colors.text.secondary,
+ textAlign = TextAlign.Center,
+ )
+}
+
+@Composable
+private fun DisableFeeBlock(feeState: DisableFeeState, onReadMoreClick: () -> Unit) {
+ Column(modifier = Modifier.fillMaxWidth()) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(
+ color = TangemTheme.colors.background.action,
+ shape = TangemTheme.shapes.roundedCornersXMedium,
+ )
+ .padding(TangemTheme.dimens.spacing12),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Text(
+ text = stringResourceSafe(id = R.string.common_network_fee_title),
+ style = TangemTheme.typography.body2,
+ color = TangemTheme.colors.text.primary1,
+ )
+ when (feeState) {
+ is DisableFeeState.Loading -> TextShimmer(
+ style = TangemTheme.typography.body2,
+ modifier = Modifier.size(
+ width = TangemTheme.dimens.size80,
+ height = TangemTheme.dimens.spacing16,
+ ),
+ )
+ is DisableFeeState.Content -> Row(
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ AuditLabel(
+ state = AuditLabelUM(
+ text = stringReference(feeState.feeSymbol),
+ type = AuditLabelUM.Type.General,
+ ),
+ )
+ Text(
+ text = feeState.fiatFormatted,
+ style = TangemTheme.typography.body2,
+ color = TangemTheme.colors.text.tertiary,
+ modifier = Modifier.padding(start = TangemTheme.dimens.spacing4),
+ )
+ }
+ is DisableFeeState.Error -> Text(
+ text = "\u2014",
+ style = TangemTheme.typography.body2,
+ color = TangemTheme.colors.text.primary1,
+ )
+ }
+ }
+
+ Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing8))
+ DisableFeeDescription(onReadMoreClick = onReadMoreClick)
+ }
+}
+
+@Composable
+private fun DisableFeeDescription(onReadMoreClick: () -> Unit) {
+ val readMoreText = stringResourceSafe(id = R.string.common_read_more)
+ val fullText = stringResourceSafe(id = R.string.dynamic_addresses_disable_fee_description)
+
+ val annotatedString = buildAnnotatedString {
+ withStyle(SpanStyle(color = TangemTheme.colors.text.tertiary)) {
+ append(fullText)
+ append(" ")
+ }
+ withLink(
+ link = LinkAnnotation.Clickable(
+ tag = "read_more",
+ linkInteractionListener = { onReadMoreClick() },
+ ),
+ ) {
+ withStyle(SpanStyle(color = TangemTheme.colors.text.accent)) {
+ append(readMoreText)
+ }
+ }
+ }
+
+ Text(
+ text = annotatedString,
+ style = TangemTheme.typography.caption2,
+ )
+}
+
+@Composable
+internal fun DynamicAddressesConflictingCustomTokensContent(
+ content: DynamicAddressesBottomSheetConfig.ConflictingCustomTokens,
+) {
ErrorContent(
titleRes = R.string.dynamic_addresses_error_has_custom_token_title,
descriptionRes = R.string.dynamic_addresses_error_has_custom_token_description,
buttonTextRes = R.string.common_got_it,
- onButtonClick = content.onGotItClick,
+ onButtonClick = content.onDismissClick,
)
}
@@ -101,7 +312,7 @@ internal fun DynamicAddressesServiceUnavailableContent(content: DynamicAddresses
titleRes = R.string.dynamic_addresses_error_service_unavailable_title,
descriptionRes = R.string.dynamic_addresses_error_service_unavailable_description,
buttonTextRes = R.string.common_got_it,
- onButtonClick = content.onGotItClick,
+ onButtonClick = content.onDismissClick,
)
}
@@ -117,7 +328,7 @@ private fun ErrorContent(titleRes: Int, descriptionRes: Int, buttonTextRes: Int,
painter = painterResource(id = CoreR.drawable.ic_dynamic_addresses_bottomsheet_enable_unavailable),
contentDescription = null,
modifier = Modifier.size(TangemTheme.dimens.size44),
- tint = TangemTheme.colors.icon.warning,
+ tint = TangemTheme.colors.icon.attention,
)
Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing12))
@@ -176,4 +387,143 @@ private fun FeatureItem(iconRes: Int, title: String, description: String) {
)
}
}
-}
\ No newline at end of file
+}
+
+// region Previews
+
+@Preview(showBackground = true)
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun Preview_Enable() {
+ TangemThemePreview {
+ DynamicAddressesEnableContent(
+ content = DynamicAddressesBottomSheetConfig.Enable(
+ isCardScanRequired = false,
+ onEnableClick = {},
+ ),
+ )
+ }
+}
+
+@Preview(showBackground = true)
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun Preview_EnableWithCardScan() {
+ TangemThemePreview {
+ DynamicAddressesEnableContent(
+ content = DynamicAddressesBottomSheetConfig.Enable(
+ isCardScanRequired = true,
+ onEnableClick = {},
+ ),
+ )
+ }
+}
+
+@Preview(showBackground = true)
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun Preview_DisableWithoutConsolidation() {
+ TangemThemePreview {
+ DynamicAddressesDisableWithoutConsolidationContent(
+ content = DynamicAddressesBottomSheetConfig.DisableWithoutConsolidation(
+ onDisableClick = {},
+ onReadMoreClick = {},
+ ),
+ )
+ }
+}
+
+@Preview(showBackground = true)
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun Preview_DisableFeeLoading() {
+ TangemThemePreview {
+ DynamicAddressesDisableWithConsolidationContent(
+ content = DynamicAddressesBottomSheetConfig.DisableWithConsolidation(
+ feeState = DisableFeeState.Loading,
+ onDisableClick = {},
+ onRefreshFee = {},
+ onReadMoreClick = {},
+ ),
+ )
+ }
+}
+
+@Preview(showBackground = true)
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun Preview_DisableFeeLoaded() {
+ TangemThemePreview {
+ DynamicAddressesDisableWithConsolidationContent(
+ content = DynamicAddressesBottomSheetConfig.DisableWithConsolidation(
+ feeState = DisableFeeState.Content(
+ feeSymbol = "BTC",
+ fiatFormatted = "~$0.12",
+ ),
+ onDisableClick = {},
+ onRefreshFee = {},
+ onReadMoreClick = {},
+ ),
+ )
+ }
+}
+
+@Preview(showBackground = true)
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun Preview_DisableFeeError() {
+ TangemThemePreview {
+ DynamicAddressesDisableWithConsolidationContent(
+ content = DynamicAddressesBottomSheetConfig.DisableWithConsolidation(
+ feeState = DisableFeeState.Error,
+ onDisableClick = {},
+ onRefreshFee = {},
+ onReadMoreClick = {},
+ ),
+ )
+ }
+}
+
+@Preview(showBackground = true)
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun Preview_DisableSending() {
+ TangemThemePreview {
+ DynamicAddressesDisableWithConsolidationContent(
+ content = DynamicAddressesBottomSheetConfig.DisableWithConsolidation(
+ feeState = DisableFeeState.Content(
+ feeSymbol = "BTC",
+ fiatFormatted = "~$0.12",
+ ),
+ isSending = true,
+ onDisableClick = {},
+ onRefreshFee = {},
+ onReadMoreClick = {},
+ ),
+ )
+ }
+}
+
+@Preview(showBackground = true)
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun Preview_ConflictingCustomTokens() {
+ TangemThemePreview {
+ DynamicAddressesConflictingCustomTokensContent(
+ content = DynamicAddressesBottomSheetConfig.ConflictingCustomTokens(onDismissClick = {}),
+ )
+ }
+}
+
+@Preview(showBackground = true)
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun Preview_ServiceUnavailable() {
+ TangemThemePreview {
+ DynamicAddressesServiceUnavailableContent(
+ content = DynamicAddressesBottomSheetConfig.ServiceUnavailable(onDismissClick = {}),
+ )
+ }
+}
+
+// endregion
\ No newline at end of file
diff --git a/gradle/tangem_dependencies.toml b/gradle/tangem_dependencies.toml
index 165d042410..d9b87d6b95 100644
--- a/gradle/tangem_dependencies.toml
+++ b/gradle/tangem_dependencies.toml
@@ -5,7 +5,7 @@
# https://github.com/tangem/tangem-sdk-android/
# https://github.com/tangem/vico
-tangemBlockchainSdk = "develop-1482"
+tangemBlockchainSdk = "develop-1486"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
tangemCardSdk = "develop-602"
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^