Updated on 2026-08-14

This commit is contained in:
Tangem 2025-06-01 15:24:59 +07:00
parent ab2f0f58bb
commit 22ad2ddc67
22 changed files with 190 additions and 15 deletions

View file

@ -51,6 +51,7 @@ internal class TokenDetailsNotificationsAnalyticsSender(
is TokenDetailsNotification.RentInfo,
is TokenDetailsNotification.NetworkShutdown,
is TokenDetailsNotification.HederaAssociateWarning,
is TokenDetailsNotification.RequiredTrustlineWarning,
is TokenDetailsNotification.KoinosMana,
is TokenDetailsNotification.MigrationMaticToPol,
is TokenDetailsNotification.UsedOutdatedData,

View file

@ -62,6 +62,8 @@ interface TokenDetailsClickIntents {
fun onRetryIncompleteTransactionClick()
fun onOpenTrustlineClick()
fun onDismissIncompleteTransactionClick()
fun onConfirmDismissIncompleteTransactionClick()

View file

@ -56,9 +56,11 @@ import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent.Compan
import com.tangem.domain.tokens.model.analytics.TokenSwapPromoAnalyticsEvent
import com.tangem.domain.transaction.error.AssociateAssetError
import com.tangem.domain.transaction.error.IncompleteTransactionError
import com.tangem.domain.transaction.error.OpenTrustlineError
import com.tangem.domain.transaction.error.SendTransactionError
import com.tangem.domain.transaction.usecase.AssociateAssetUseCase
import com.tangem.domain.transaction.usecase.DismissIncompleteTransactionUseCase
import com.tangem.domain.transaction.usecase.OpenTrustlineUseCase
import com.tangem.domain.transaction.usecase.RetryIncompleteTransactionUseCase
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
@ -116,6 +118,7 @@ internal class TokenDetailsModel @Inject constructor(
private val isDemoCardUseCase: IsDemoCardUseCase,
private val associateAssetUseCase: AssociateAssetUseCase,
private val retryIncompleteTransactionUseCase: RetryIncompleteTransactionUseCase,
private val openTrustlineUseCase: OpenTrustlineUseCase,
private val dismissIncompleteTransactionUseCase: DismissIncompleteTransactionUseCase,
private val reduxStateHolder: ReduxStateHolder,
private val analyticsEventsHandler: AnalyticsEventHandler,
@ -893,6 +896,37 @@ internal class TokenDetailsModel @Inject constructor(
}
}
override fun onOpenTrustlineClick() {
analyticsEventsHandler.send(
TokenScreenAnalyticsEvent.Associate(
tokenSymbol = cryptoCurrency.symbol,
blockchain = cryptoCurrency.network.name,
),
)
modelScope.launch(dispatchers.mainImmediate) {
openTrustlineUseCase(
userWalletId = userWalletId,
currency = cryptoCurrency,
).fold(
ifLeft = { e ->
Timber.e(e.message)
internalUiState.value = when (e) {
is OpenTrustlineError.SomeError ->
stateFactory.getStateWithErrorDialog(stringReference(e.message.orEmpty()))
is OpenTrustlineError.NotEnoughCoin -> {
val text = resourceReference(
id = R.string.warning_token_required_min_coin_reserve,
formatArgs = wrappedList(e.amount),
)
stateFactory.getStateWithErrorDialog(text)
}
}
},
ifRight = { internalUiState.value = stateFactory.getStateWithRemovedRequiredTrustlineNotification() },
)
}
}
override fun onDismissIncompleteTransactionClick() {
analyticsEventsHandler.send(
TokenScreenAnalyticsEvent.RevealCancel(

View file

@ -193,6 +193,24 @@ internal sealed class TokenDetailsNotification(val config: NotificationConfig) {
),
)
data class RequiredTrustlineWarning(
private val currency: CryptoCurrency,
private val amount: String,
private val currencySymbol: String,
private val onOpenClick: () -> Unit,
) : Warning(
title = resourceReference(id = R.string.warning_token_trustline_title),
subtitle = resourceReference(
id = R.string.warning_token_trustline_subtitle,
formatArgs = wrappedList(amount, currencySymbol),
),
iconResId = currency.networkIconResId,
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
text = resourceReference(R.string.warning_token_trustline_button_title),
onClick = onOpenClick,
),
)
data class KaspaIncompleteTransactionWarning(
private val currency: CryptoCurrency,
private val amount: String,

View file

@ -42,6 +42,12 @@ internal class TokenDetailsNotificationConverter(
return newNotifications.toImmutableList()
}
fun removeRequiredTrustlineWarning(currentState: TokenDetailsState): ImmutableList<TokenDetailsNotification> {
val newNotifications = currentState.notifications.toMutableList()
newNotifications.removeBy { it is RequiredTrustlineWarning }
return newNotifications.toImmutableList()
}
fun removeKaspaIncompleteTransactionWarning(
currentState: TokenDetailsState,
): ImmutableList<TokenDetailsNotification> {
@ -118,6 +124,12 @@ internal class TokenDetailsNotificationConverter(
feeCurrencySymbol = warning.feeCurrencySymbol,
onAssociateClick = clickIntents::onAssociateClick,
)
is CryptoCurrencyWarning.RequiredTrustline -> RequiredTrustlineWarning(
currency = warning.currency,
amount = warning.requiredAmount.format { crypto(symbol = "", warning.currencyDecimals) },
currencySymbol = warning.currencySymbol,
onOpenClick = clickIntents::onOpenTrustlineClick,
)
is KaspaWarnings.IncompleteTransaction -> KaspaIncompleteTransactionWarning(
currency = warning.currency,
amount = warning.amount.format { crypto(symbol = "", decimals = warning.currencyDecimals) },

View file

@ -334,6 +334,11 @@ internal class TokenDetailsStateFactory(
return state.copy(notifications = notificationConverter.removeHederaAssociateWarning(state))
}
fun getStateWithRemovedRequiredTrustlineNotification(): TokenDetailsState {
val state = currentStateProvider()
return state.copy(notifications = notificationConverter.removeRequiredTrustlineWarning(state))
}
fun getStateWithRemovedKaspaIncompleteTransactionNotification(): TokenDetailsState {
val state = currentStateProvider()
return state.copy(