Updated on 2026-08-14

This commit is contained in:
Tangem 2026-05-18 16:58:15 +03:00
parent 3e70beab28
commit 4c9b2e4862
8 changed files with 368 additions and 85 deletions

View file

@ -2197,6 +2197,8 @@
<string name="warning_no_backup_title">Missing backup</string>
<string name="warning_number_of_signed_hashes_incorrect_message">This card has been previously used for transactions. If received from an untrusted source, consider withdrawing all funds. If it\'s your card, no action is required.</string>
<string name="warning_number_of_signed_hashes_incorrect_title">Card has already signed transactions</string>
<string name="warning_outdated_data_message">Will be updated as soon as possible</string>
<string name="warning_outdated_data_title">Missing some token balances</string>
<string name="warning_rate_app_message">Your review keeps us motivated to make Tangem Wallet even better</string>
<string name="warning_rate_app_title">Enjoying Tangem?</string>
<string name="warning_receive_blocked_hedera_token_association_required_message">You must associate your token before receiving tokens</string>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:pathData="M18.485,6.667C19.683,6.667 20.795,6.885 21.822,7.322C22.857,7.752 23.759,8.366 24.529,9.164C25.307,9.954 25.91,10.89 26.338,11.972C26.766,13.046 26.979,14.224 26.979,15.505C27.742,15.819 28.399,16.252 28.951,16.805C29.503,17.349 29.928,17.979 30.224,18.692C30.519,19.398 30.667,20.154 30.667,20.959C30.667,21.749 30.507,22.489 30.188,23.18C29.87,23.87 29.422,24.476 28.847,24.998C28.279,25.52 27.614,25.926 26.852,26.218C26.089,26.517 25.268,26.667 24.39,26.667H8.322C7.327,26.667 6.405,26.502 5.558,26.172C4.71,25.842 3.966,25.393 3.328,24.825C2.698,24.25 2.208,23.587 1.858,22.835C1.508,22.083 1.333,21.285 1.333,20.441C1.333,19.506 1.504,18.642 1.847,17.852C2.197,17.062 2.69,16.405 3.328,15.884C3.974,15.362 4.736,15.033 5.615,14.894C5.631,14.089 5.818,13.364 6.176,12.72C6.534,12.068 7.004,11.53 7.587,11.108C8.17,10.679 8.817,10.395 9.524,10.257C10.24,10.111 10.955,10.142 11.671,10.349C12.138,9.674 12.698,9.06 13.352,8.508C14.013,7.948 14.767,7.503 15.615,7.173C16.471,6.835 17.428,6.667 18.485,6.667ZM18.485,8.52C17.482,8.52 16.607,8.685 15.86,9.015C15.121,9.337 14.487,9.774 13.958,10.326C13.429,10.878 12.974,11.485 12.593,12.144C12.531,12.259 12.433,12.298 12.301,12.26C11.702,12.068 11.107,11.995 10.516,12.041C9.933,12.079 9.4,12.252 8.918,12.559C8.436,12.858 8.047,13.299 7.751,13.882C7.463,14.457 7.319,15.19 7.319,16.08C7.319,16.233 7.245,16.309 7.098,16.31C6.273,16.31 5.569,16.494 4.985,16.862C4.41,17.23 3.97,17.729 3.667,18.358C3.364,18.98 3.212,19.674 3.212,20.441C3.212,21.224 3.434,21.949 3.877,22.616C4.32,23.276 4.927,23.809 5.697,24.216C6.467,24.615 7.342,24.813 8.322,24.813H24.39C25.214,24.813 25.957,24.641 26.618,24.296C27.287,23.951 27.816,23.486 28.205,22.903C28.594,22.32 28.788,21.672 28.788,20.959C28.788,19.916 28.469,19.034 27.831,18.313C27.201,17.591 26.358,17.116 25.3,16.886C25.16,16.855 25.097,16.767 25.112,16.621C25.136,16.353 25.151,16.072 25.159,15.78C25.175,15.481 25.175,15.197 25.159,14.929C25.097,13.793 24.778,12.739 24.202,11.765C23.627,10.79 22.849,10.008 21.869,9.417C20.889,8.819 19.761,8.52 18.485,8.52ZM16,20.119C16.342,20.119 16.638,20.234 16.887,20.464C17.136,20.694 17.26,20.978 17.26,21.315C17.26,21.653 17.132,21.937 16.875,22.167C16.626,22.397 16.334,22.513 16,22.513C15.658,22.513 15.362,22.397 15.113,22.167C14.864,21.929 14.74,21.645 14.74,21.315C14.74,20.986 14.864,20.706 15.113,20.476C15.362,20.238 15.658,20.119 16,20.119ZM16,12.628C16.311,12.628 16.568,12.727 16.771,12.927C16.98,13.126 17.081,13.383 17.073,13.697L16.898,17.921C16.875,18.535 16.58,18.842 16.012,18.842C15.42,18.842 15.113,18.535 15.09,17.921L14.939,13.686C14.923,13.372 15.016,13.118 15.218,12.927C15.42,12.727 15.681,12.628 16,12.628Z"
android:fillColor="#000000"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:pathData="M15.987,2.667C23.281,2.667 29.333,8.706 29.333,16C29.333,23.281 23.294,29.333 16,29.333C8.719,29.333 2.667,23.281 2.667,16C2.667,8.706 8.706,2.667 15.987,2.667ZM16.014,20.271C15.219,20.271 14.555,20.896 14.555,21.677C14.555,22.445 15.232,23.07 16.014,23.07C16.782,23.07 17.459,22.458 17.459,21.677C17.459,20.883 16.795,20.271 16.014,20.271ZM16,9.359C15.271,9.359 14.764,9.854 14.776,10.544L14.945,17.628C14.958,18.318 15.337,18.708 16.014,18.708C16.651,18.708 17.029,18.331 17.042,17.628L17.237,10.558C17.25,9.868 16.703,9.359 16,9.359Z"
android:fillColor="#000000"/>
</vector>

View file

@ -1,5 +1,6 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.state.transformer
import com.tangem.blockchain.common.Blockchain
import com.tangem.core.ui.R
import com.tangem.core.ui.ds.button.TangemButtonType
import com.tangem.core.ui.ds.image.TangemIconUM
@ -10,6 +11,8 @@ import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.format.bigdecimal.shorted
import com.tangem.core.ui.res.TangemTheme
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
import com.tangem.domain.tokens.model.warnings.DynamicAddressesWarnings
@ -17,30 +20,36 @@ import com.tangem.domain.tokens.model.warnings.HederaWarnings
import com.tangem.domain.tokens.model.warnings.KaspaWarnings
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsUM
import com.tangem.utils.logging.TangemLogger
import com.tangem.utils.transformer.Transformer
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import java.math.BigDecimal
import com.tangem.core.res.R as CoreResR
@Suppress("LargeClass")
internal class UpdateNotificationsTransformer(
private val warnings: Set<CryptoCurrencyWarning>,
private val clickIntents: TokenDetailsClickIntents,
) : Transformer<TokenDetailsUM> {
override fun transform(prevState: TokenDetailsUM): TokenDetailsUM {
val notifications = warnings.mapNotNull(::mapWarning).toImmutableList()
val notifications = warnings.map(::mapWarning).toImmutableList()
return prevState.copy(notifications = notifications)
}
@Suppress("LongMethod")
private fun mapWarning(warning: CryptoCurrencyWarning): TangemMessageUM? {
@Suppress("LongMethod", "CyclomaticComplexMethod")
private fun mapWarning(warning: CryptoCurrencyWarning): TangemMessageUM {
return when (warning) {
is CryptoCurrencyWarning.SomeNetworksUnreachable -> TangemMessageUM(
id = "networks_unreachable",
title = resourceReference(CoreResR.string.warning_network_unreachable_title),
subtitle = resourceReference(CoreResR.string.warning_network_unreachable_message),
messageEffect = TangemMessageEffect.None,
iconUM = TangemIconUM.Icon(R.drawable.ic_attention_default_24),
iconUM = TangemIconUM.Icon(
iconRes = R.drawable.ic_attention_default_24,
tintReference = { TangemTheme.colors2.graphic.status.attention },
),
)
is CryptoCurrencyWarning.BalanceNotEnoughForFee -> createFeeWarning(
FeeWarningParams(
@ -50,6 +59,7 @@ internal class UpdateNotificationsTransformer(
feeCurrencyName = warning.coinCurrency.name,
feeCurrencySymbol = warning.coinCurrency.symbol,
buyCurrency = warning.coinCurrency,
iconResId = R.drawable.ic_attention_default_24,
),
)
is CryptoCurrencyWarning.CustomTokenNotEnoughForFee -> createFeeWarning(
@ -60,6 +70,7 @@ internal class UpdateNotificationsTransformer(
feeCurrencyName = warning.feeCurrencyName,
feeCurrencySymbol = warning.feeCurrencySymbol,
buyCurrency = warning.feeCurrency,
iconResId = R.drawable.ic_attention_default_24,
),
)
is CryptoCurrencyWarning.BeaconChainShutdown -> TangemMessageUM(
@ -67,18 +78,24 @@ internal class UpdateNotificationsTransformer(
title = resourceReference(CoreResR.string.warning_beacon_chain_retirement_title),
subtitle = resourceReference(CoreResR.string.warning_beacon_chain_retirement_content),
messageEffect = TangemMessageEffect.None,
iconUM = TangemIconUM.Icon(R.drawable.ic_attention_default_24),
iconUM = TangemIconUM.Icon(
iconRes = R.drawable.ic_attention_default_24,
tintReference = { TangemTheme.colors2.graphic.status.attention },
),
)
is HederaWarnings.AssociateWarning -> TangemMessageUM(
id = "hedera_associate",
title = resourceReference(CoreResR.string.warning_hedera_missing_token_association_title),
subtitle = resourceReference(CoreResR.string.warning_hedera_missing_token_association_message_brief),
messageEffect = TangemMessageEffect.None,
iconUM = TangemIconUM.Icon(R.drawable.ic_attention_default_24),
iconUM = TangemIconUM.Icon(
iconRes = R.drawable.ic_attention_default_24,
tintReference = { TangemTheme.colors2.graphic.neutral.primary },
),
buttonsUM = persistentListOf(
TangemMessageButtonUM(
text = resourceReference(CoreResR.string.warning_hedera_missing_token_association_button_title),
type = TangemButtonType.Primary,
type = TangemButtonType.PrimaryInverse,
onClick = clickIntents::onAssociateClick,
),
),
@ -94,11 +111,14 @@ internal class UpdateNotificationsTransformer(
),
),
messageEffect = TangemMessageEffect.None,
iconUM = TangemIconUM.Icon(R.drawable.ic_attention_default_24),
iconUM = TangemIconUM.Icon(
iconRes = R.drawable.ic_attention_default_24,
tintReference = { TangemTheme.colors2.graphic.neutral.primary },
),
buttonsUM = persistentListOf(
TangemMessageButtonUM(
text = resourceReference(CoreResR.string.warning_hedera_missing_token_association_button_title),
type = TangemButtonType.Primary,
type = TangemButtonType.PrimaryInverse,
onClick = clickIntents::onAssociateClick,
),
),
@ -114,11 +134,14 @@ internal class UpdateNotificationsTransformer(
),
),
messageEffect = TangemMessageEffect.None,
iconUM = TangemIconUM.Icon(R.drawable.ic_attention_default_24),
iconUM = TangemIconUM.Icon(
iconRes = R.drawable.ic_attention_default_24,
tintReference = { TangemTheme.colors2.graphic.neutral.primary },
),
buttonsUM = persistentListOf(
TangemMessageButtonUM(
text = resourceReference(CoreResR.string.warning_token_trustline_button_title),
type = TangemButtonType.Primary,
type = TangemButtonType.PrimaryInverse,
onClick = clickIntents::onOpenTrustlineClick,
),
),
@ -133,34 +156,51 @@ internal class UpdateNotificationsTransformer(
warning.currencySymbol,
),
),
messageEffect = TangemMessageEffect.None,
iconUM = TangemIconUM.Icon(R.drawable.ic_attention_default_24),
messageEffect = TangemMessageEffect.Warning,
iconUM = TangemIconUM.Icon(
iconRes = R.drawable.ic_attention_default_24,
tintReference = { TangemTheme.colors2.graphic.neutral.primary },
),
buttonsUM = persistentListOf(
TangemMessageButtonUM(
text = resourceReference(CoreResR.string.common_cancel),
type = TangemButtonType.Secondary,
onClick = clickIntents::onDismissIncompleteTransactionClick,
),
TangemMessageButtonUM(
text = resourceReference(CoreResR.string.alert_button_try_again),
type = TangemButtonType.Primary,
tangemIconUM = TangemIconUM.Icon(
R.drawable.ic_tangem_24,
tintReference = { TangemTheme.colors2.graphic.neutral.primaryInverted },
),
onClick = clickIntents::onRetryIncompleteTransactionClick,
),
),
onCloseClick = clickIntents::onDismissIncompleteTransactionClick,
)
is CryptoCurrencyWarning.MigrationMaticToPol -> TangemMessageUM(
id = "migration_matic_pol",
title = resourceReference(CoreResR.string.warning_matic_migration_title),
subtitle = resourceReference(CoreResR.string.warning_matic_migration_message),
messageEffect = TangemMessageEffect.None,
iconUM = TangemIconUM.Icon(R.drawable.ic_attention_default_24),
iconUM = TangemIconUM.Icon(
iconRes = R.drawable.ic_attention_default_24,
tintReference = { TangemTheme.colors2.graphic.status.attention },
),
)
is CryptoCurrencyWarning.MigrationClore -> TangemMessageUM(
id = "migration_clore",
title = resourceReference(CoreResR.string.warning_clore_migration_title),
subtitle = resourceReference(CoreResR.string.warning_clore_migration_description),
messageEffect = TangemMessageEffect.None,
iconUM = TangemIconUM.Icon(R.drawable.ic_attention_default_24),
messageEffect = TangemMessageEffect.Warning,
iconUM = TangemIconUM.Icon(
iconRes = R.drawable.ic_attention_default_24,
tintReference = { TangemTheme.colors2.graphic.neutral.primary },
),
buttonsUM = persistentListOf(
TangemMessageButtonUM(
text = resourceReference(CoreResR.string.warning_clore_migration_button),
type = TangemButtonType.Primary,
type = TangemButtonType.PrimaryInverse,
onClick = clickIntents::onCloreMigrationClick,
),
),
@ -170,23 +210,114 @@ internal class UpdateNotificationsTransformer(
title = resourceReference(CoreResR.string.dynamic_addresses_notification_funds_found_title),
subtitle = resourceReference(CoreResR.string.dynamic_addresses_notification_funds_found_description),
messageEffect = TangemMessageEffect.None,
iconUM = TangemIconUM.Icon(R.drawable.ic_attention_default_24),
iconUM = TangemIconUM.Icon(
iconRes = R.drawable.ic_attention_default_24,
tintReference = { TangemTheme.colors2.graphic.status.attention },
),
buttonsUM = persistentListOf(
TangemMessageButtonUM(
text = resourceReference(CoreResR.string.common_learn_more),
type = TangemButtonType.Primary,
type = TangemButtonType.PrimaryInverse,
onClick = clickIntents::onDynamicAddressesFundsFoundLearnMoreClick,
),
),
)
// Non-warning types — skip for redesign
is CryptoCurrencyWarning.ExistentialDeposit,
is CryptoCurrencyWarning.Rent,
is CryptoCurrencyWarning.SomeNetworksNoAccount,
is CryptoCurrencyWarning.TopUpWithoutReserve,
is CryptoCurrencyWarning.FeeResourceInfo,
is CryptoCurrencyWarning.UsedOutdatedDataWarning,
-> null
is CryptoCurrencyWarning.ExistentialDeposit -> TangemMessageUM(
id = "existential_deposit",
title = resourceReference(CoreResR.string.warning_existential_deposit_title),
subtitle = resourceReference(
CoreResR.string.warning_existential_deposit_message,
wrappedList(warning.currencyName, warning.edStringValueWithSymbol),
),
messageEffect = TangemMessageEffect.None,
iconUM = TangemIconUM.Icon(
iconRes = R.drawable.ic_warning_default_32,
tintReference = { TangemTheme.colors2.graphic.neutral.primary },
),
)
is CryptoCurrencyWarning.Rent -> TangemMessageUM(
id = "rent_info",
title = resourceReference(CoreResR.string.warning_rent_fee_title),
subtitle = resourceReference(
CoreResR.string.warning_solana_rent_fee_message,
wrappedList(
warning.rent,
warning.exemptionAmount.format { crypto(warning.cryptoCurrency) },
),
),
messageEffect = TangemMessageEffect.None,
iconUM = TangemIconUM.Icon(
iconRes = R.drawable.ic_warning_default_32,
tintReference = { TangemTheme.colors2.graphic.neutral.primary },
),
buttonsUM = persistentListOf(
TangemMessageButtonUM(
text = resourceReference(CoreResR.string.common_later),
type = TangemButtonType.Secondary,
onClick = clickIntents::onCloseRentInfoNotification,
),
),
)
is CryptoCurrencyWarning.SomeNetworksNoAccount -> TangemMessageUM(
id = "networks_no_account",
title = resourceReference(CoreResR.string.warning_no_account_title),
subtitle = resourceReference(
CoreResR.string.no_account_generic,
wrappedList(
warning.amountCurrency.network.name,
warning.amountToCreateAccount.format {
crypto(symbol = "", decimals = warning.amountCurrency.decimals)
}.trim(),
warning.amountCurrency.network.currencySymbol,
),
),
messageEffect = TangemMessageEffect.None,
iconUM = TangemIconUM.Icon(
iconRes = R.drawable.ic_warning_default_32,
tintReference = { TangemTheme.colors2.graphic.neutral.primary },
),
)
is CryptoCurrencyWarning.TopUpWithoutReserve -> TangemMessageUM(
id = "top_up_without_reserve",
title = resourceReference(CoreResR.string.warning_no_account_title),
subtitle = resourceReference(CoreResR.string.no_account_send_to_create),
messageEffect = TangemMessageEffect.None,
iconUM = TangemIconUM.Icon(
iconRes = R.drawable.ic_warning_default_32,
tintReference = { TangemTheme.colors2.graphic.neutral.primary },
),
)
is CryptoCurrencyWarning.FeeResourceInfo -> TangemMessageUM(
id = "fee_resource_info",
title = resourceReference(CoreResR.string.koinos_mana_level_title),
subtitle = resourceReference(
CoreResR.string.koinos_mana_level_description,
wrappedList(
formatMana(warning.amount),
warning.maxAmount?.let(::formatMana) ?: run {
TangemLogger.e(
"FeeResource maxAmount cannot be null in Koinos. Check KoinosWalletManager",
)
""
},
),
),
messageEffect = TangemMessageEffect.None,
iconUM = TangemIconUM.Icon(
iconRes = R.drawable.ic_warning_default_32,
tintReference = { TangemTheme.colors2.graphic.neutral.primary },
),
)
is CryptoCurrencyWarning.UsedOutdatedDataWarning -> TangemMessageUM(
id = "used_outdated_data",
title = resourceReference(CoreResR.string.warning_outdated_data_title),
subtitle = resourceReference(CoreResR.string.warning_outdated_data_message),
messageEffect = TangemMessageEffect.None,
iconUM = TangemIconUM.Icon(
iconRes = R.drawable.ic_error_sync_default_32,
tintReference = { TangemTheme.colors2.graphic.status.attention },
),
)
}
}
@ -198,7 +329,7 @@ internal class UpdateNotificationsTransformer(
CoreResR.string.common_buy_currency,
wrappedList(params.feeCurrencySymbol),
),
type = TangemButtonType.Primary,
type = TangemButtonType.PrimaryInverse,
onClick = { clickIntents.onBuyCoinClick(params.buyCurrency) },
),
)
@ -223,17 +354,25 @@ internal class UpdateNotificationsTransformer(
),
),
messageEffect = TangemMessageEffect.None,
iconUM = TangemIconUM.Icon(R.drawable.ic_attention_default_24),
iconUM = TangemIconUM.Icon(
iconRes = params.iconResId,
tintReference = { TangemTheme.colors2.graphic.neutral.primary },
),
buttonsUM = buttons,
)
}
private data class FeeWarningParams(
val id: String,
val currency: CryptoCurrency,
val networkName: String,
val feeCurrencyName: String,
val feeCurrencySymbol: String,
val buyCurrency: CryptoCurrency?,
)
}
private fun formatMana(amount: BigDecimal): String {
return amount.format { crypto(symbol = "", decimals = Blockchain.Koinos.decimals()).shorted() }
}
}
private data class FeeWarningParams(
val id: String,
val currency: CryptoCurrency,
val networkName: String,
val feeCurrencyName: String,
val feeCurrencySymbol: String,
val buyCurrency: CryptoCurrency?,
val iconResId: Int,
)

View file

@ -45,7 +45,6 @@ import com.tangem.core.ui.components.haze.hazeSourceTangem
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.themedColor
import com.tangem.core.ui.res.LocalRootBackgroundColor
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreviewRedesign
import com.tangem.domain.models.account.CryptoPortfolioIcon
@ -86,14 +85,14 @@ internal fun TokenDetailsScreen(
val statusBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getTop(this).toDp() }
val topBarTotalHeight = TopBarHeight + statusBarHeight
val rootBackground by LocalRootBackgroundColor.current
val rootBackground = TangemTheme.colors2.surface.level2
var marketBlockHeight by remember { mutableStateOf(0.dp) }
val effectiveBottomPadding = marketBlockHeight + TangemTheme.dimens2.x4
Box(
modifier = modifier
.fillMaxSize()
.background(TangemTheme.colors2.surface.level2),
.background(rootBackground),
) {
Box(
modifier = Modifier

View file

@ -223,7 +223,7 @@ class UpdateNotificationsTransformerTest {
}
@Test
fun `GIVEN KaspaIncompleteTransaction WHEN transform THEN notification with button and close is created`() {
fun `GIVEN KaspaIncompleteTransaction WHEN transform THEN notification with cancel and try again buttons is created`() {
// GIVEN
val currency: CryptoCurrency = mockk(relaxed = true)
val transformer = createTransformer(
@ -243,16 +243,17 @@ class UpdateNotificationsTransformerTest {
// THEN
assertThat(result.notifications).hasSize(1)
assertThat(result.notifications.first().id).isEqualTo("kaspa_incomplete")
assertThat(result.notifications.first().buttonsUM).hasSize(1)
assertThat(result.notifications.first().onCloseClick).isNotNull()
assertThat(result.notifications.first().buttonsUM).hasSize(2)
assertThat(result.notifications.first().onCloseClick).isNull()
assertThat(result.notifications.first().messageEffect).isEqualTo(TangemMessageEffect.Warning)
}
// endregion
// region Skipped warnings
// region Newly added warnings
@Test
fun `GIVEN ExistentialDeposit WHEN transform THEN notification is skipped`() {
fun `GIVEN ExistentialDeposit WHEN transform THEN notification with id existential_deposit is created`() {
// GIVEN
val transformer = createTransformer(
warnings = setOf(
@ -267,11 +268,13 @@ class UpdateNotificationsTransformerTest {
val result = transformer.transform(initialState())
// THEN
assertThat(result.notifications).isEmpty()
assertThat(result.notifications).hasSize(1)
assertThat(result.notifications.first().id).isEqualTo("existential_deposit")
assertThat(result.notifications.first().buttonsUM).isEmpty()
}
@Test
fun `GIVEN Rent WHEN transform THEN notification is skipped`() {
fun `GIVEN Rent WHEN transform THEN notification with id rent_info and later button is created`() {
// GIVEN
val transformer = createTransformer(
warnings = setOf(
@ -287,11 +290,120 @@ class UpdateNotificationsTransformerTest {
val result = transformer.transform(initialState())
// THEN
assertThat(result.notifications).isEmpty()
assertThat(result.notifications).hasSize(1)
assertThat(result.notifications.first().id).isEqualTo("rent_info")
assertThat(result.notifications.first().onCloseClick).isNull()
assertThat(result.notifications.first().buttonsUM).hasSize(1)
}
@Test
fun `GIVEN UsedOutdatedDataWarning WHEN transform THEN notification is skipped`() {
fun `GIVEN Rent WHEN later clicked THEN onCloseRentInfoNotification is called`() {
// GIVEN
val transformer = createTransformer(
warnings = setOf(
CryptoCurrencyWarning.Rent(
rent = BigDecimal("0.00001"),
exemptionAmount = BigDecimal("0.01"),
cryptoCurrency = mockk(relaxed = true),
),
),
)
// WHEN
val result = transformer.transform(initialState())
result.notifications.first().buttonsUM.first().onClick()
// THEN
verify(exactly = 1) { clickIntents.onCloseRentInfoNotification() }
}
@Test
fun `GIVEN SomeNetworksNoAccount WHEN transform THEN notification with id networks_no_account is created`() {
// GIVEN
val amountCurrency: CryptoCurrency = mockk(relaxed = true) {
io.mockk.every { decimals } returns 7
io.mockk.every { network } returns mockk(relaxed = true) {
io.mockk.every { name } returns "Stellar"
io.mockk.every { currencySymbol } returns "XLM"
}
}
val transformer = createTransformer(
warnings = setOf(
CryptoCurrencyWarning.SomeNetworksNoAccount(
amountToCreateAccount = BigDecimal("1.0"),
amountCurrency = amountCurrency,
),
),
)
// WHEN
val result = transformer.transform(initialState())
// THEN
assertThat(result.notifications).hasSize(1)
assertThat(result.notifications.first().id).isEqualTo("networks_no_account")
assertThat(result.notifications.first().buttonsUM).isEmpty()
}
@Test
fun `GIVEN TopUpWithoutReserve WHEN transform THEN notification with id top_up_without_reserve is created`() {
// GIVEN
val transformer = createTransformer(
warnings = setOf(CryptoCurrencyWarning.TopUpWithoutReserve),
)
// WHEN
val result = transformer.transform(initialState())
// THEN
assertThat(result.notifications).hasSize(1)
assertThat(result.notifications.first().id).isEqualTo("top_up_without_reserve")
assertThat(result.notifications.first().buttonsUM).isEmpty()
}
@Test
fun `GIVEN FeeResourceInfo WHEN transform THEN notification with id fee_resource_info is created`() {
// GIVEN
val transformer = createTransformer(
warnings = setOf(
CryptoCurrencyWarning.FeeResourceInfo(
amount = BigDecimal("50.0"),
maxAmount = BigDecimal("100.0"),
),
),
)
// WHEN
val result = transformer.transform(initialState())
// THEN
assertThat(result.notifications).hasSize(1)
assertThat(result.notifications.first().id).isEqualTo("fee_resource_info")
assertThat(result.notifications.first().buttonsUM).isEmpty()
}
@Test
fun `GIVEN FeeResourceInfo with null maxAmount WHEN transform THEN notification is still created`() {
// GIVEN
val transformer = createTransformer(
warnings = setOf(
CryptoCurrencyWarning.FeeResourceInfo(
amount = BigDecimal("50.0"),
maxAmount = null,
),
),
)
// WHEN
val result = transformer.transform(initialState())
// THEN
assertThat(result.notifications).hasSize(1)
assertThat(result.notifications.first().id).isEqualTo("fee_resource_info")
}
@Test
fun `GIVEN UsedOutdatedDataWarning WHEN transform THEN notification with id used_outdated_data is created`() {
// GIVEN
val transformer = createTransformer(
warnings = setOf(CryptoCurrencyWarning.UsedOutdatedDataWarning),
@ -301,7 +413,8 @@ class UpdateNotificationsTransformerTest {
val result = transformer.transform(initialState())
// THEN
assertThat(result.notifications).isEmpty()
assertThat(result.notifications).hasSize(1)
assertThat(result.notifications.first().id).isEqualTo("used_outdated_data")
}
// endregion
@ -309,7 +422,7 @@ class UpdateNotificationsTransformerTest {
// region Message effect
@Test
fun `GIVEN any mapped warning WHEN transform THEN messageEffect is None`() {
fun `GIVEN SomeNetworksUnreachable WHEN transform THEN messageEffect is None`() {
// GIVEN
val transformer = createTransformer(
warnings = setOf(CryptoCurrencyWarning.SomeNetworksUnreachable),
@ -322,6 +435,20 @@ class UpdateNotificationsTransformerTest {
assertThat(result.notifications.first().messageEffect).isEqualTo(TangemMessageEffect.None)
}
@Test
fun `GIVEN MigrationClore WHEN transform THEN messageEffect is Warning`() {
// GIVEN
val transformer = createTransformer(
warnings = setOf(CryptoCurrencyWarning.MigrationClore),
)
// WHEN
val result = transformer.transform(initialState())
// THEN
assertThat(result.notifications.first().messageEffect).isEqualTo(TangemMessageEffect.Warning)
}
// endregion
// region Icon
@ -345,7 +472,7 @@ class UpdateNotificationsTransformerTest {
// region Multiple warnings
@Test
fun `GIVEN multiple warnings with some skipped WHEN transform THEN only mapped warnings are in notifications`() {
fun `GIVEN multiple warnings WHEN transform THEN all are mapped to notifications`() {
// GIVEN
val transformer = createTransformer(
warnings = setOf(
@ -360,10 +487,12 @@ class UpdateNotificationsTransformerTest {
val result = transformer.transform(initialState())
// THEN
assertThat(result.notifications).hasSize(2)
assertThat(result.notifications).hasSize(4)
assertThat(result.notifications.map { it.id }).containsExactly(
"networks_unreachable",
"beacon_chain_shutdown",
"used_outdated_data",
"top_up_without_reserve",
)
}
@ -410,7 +539,29 @@ class UpdateNotificationsTransformerTest {
}
@Test
fun `GIVEN KaspaIncompleteTransaction WHEN retry clicked THEN onRetryIncompleteTransactionClick is called`() {
fun `GIVEN KaspaIncompleteTransaction WHEN try again clicked THEN onRetryIncompleteTransactionClick is called`() {
// GIVEN
val transformer = createTransformer(
warnings = setOf(
KaspaWarnings.IncompleteTransaction(
currency = mockk(relaxed = true),
amount = BigDecimal("100"),
currencySymbol = "KAS",
currencyDecimals = 8,
),
),
)
// WHEN
val result = transformer.transform(initialState())
result.notifications.first().buttonsUM[1].onClick()
// THEN
verify(exactly = 1) { clickIntents.onRetryIncompleteTransactionClick() }
}
@Test
fun `GIVEN KaspaIncompleteTransaction WHEN cancel clicked THEN onDismissIncompleteTransactionClick is called`() {
// GIVEN
val transformer = createTransformer(
warnings = setOf(
@ -427,28 +578,6 @@ class UpdateNotificationsTransformerTest {
val result = transformer.transform(initialState())
result.notifications.first().buttonsUM.first().onClick()
// THEN
verify(exactly = 1) { clickIntents.onRetryIncompleteTransactionClick() }
}
@Test
fun `GIVEN KaspaIncompleteTransaction WHEN close clicked THEN onDismissIncompleteTransactionClick is called`() {
// GIVEN
val transformer = createTransformer(
warnings = setOf(
KaspaWarnings.IncompleteTransaction(
currency = mockk(relaxed = true),
amount = BigDecimal("100"),
currencySymbol = "KAS",
currencyDecimals = 8,
),
),
)
// WHEN
val result = transformer.transform(initialState())
result.notifications.first().onCloseClick!!.invoke()
// THEN
verify(exactly = 1) { clickIntents.onDismissIncompleteTransactionClick() }
}

View file

@ -50,8 +50,8 @@ internal sealed class WalletNotificationUM(val messageUM: TangemMessageUM, val t
data object UsedOutdatedData : WalletNotificationUM(
messageUM = TangemMessageUM(
id = "UsedOutdatedDataNotification",
title = stringReference("Missing some token balances"), // todo redesign main lokalise
subtitle = stringReference("Will be updated as soon as possible"), // todo redesign main lokalise
title = resourceReference(com.tangem.core.res.R.string.warning_outdated_data_title),
subtitle = resourceReference(com.tangem.core.res.R.string.warning_outdated_data_message),
iconUM = TangemIconUM.Icon(
iconRes = R.drawable.ic_error_sync_default_24,
tintReference = { TangemTheme.colors2.graphic.status.attention },

View file

@ -54,11 +54,7 @@ internal class YieldSupplyToEarnBlockConverter : Converter<YieldSupplyUM, EarnBl
backgroundUM = EarnBlockUM.BackgroundUM.Surface,
iconUM = EarnBlockUM.IconUM.Glowing(iconRes = CoreUiR.drawable.ic_yield_40),
titleUM = EarnBlockUM.TitleUM(
text = if (value.showWarningIcon) {
resourceReference(CoreResR.string.common_yield_mode)
} else {
resourceReference(CoreResR.string.yield_module_transaction_enter)
},
text = resourceReference(CoreResR.string.yield_module_transaction_enter),
style = EarnBlockUM.TitleUM.Style.Large,
tone = EarnBlockUM.TitleUM.Tone.Primary,
iconUM = buildTitleIcon(value),