Updated on 2026-08-14

This commit is contained in:
Tangem 2025-12-17 14:05:58 +03:00
commit 3cbc2dadfb
822 changed files with 12838 additions and 5366 deletions

View file

@ -24,20 +24,17 @@
<ID>MultilineLambdaItParameter:TokenDetailsModel.kt$TokenDetailsModel${ internalUiState.value = stateFactory.getStateWithUpdatedHidden( isBalanceHidden = it.isBalanceHidden, ) }</ID>
<ID>MultilineLambdaItParameter:TokenDetailsModel.kt$TokenDetailsModel${ sendButtonsEvents(it.states) internalUiState.value = stateFactory.getManageButtonsState(actions = it.states) }</ID>
<ID>MultilineLambdaItParameter:TokenDetailsModel.kt$TokenDetailsModel${ val updatedState = stateFactory.getStateWithNotifications(it) notificationsAnalyticsSender.send(internalUiState.value, updatedState.notifications) internalUiState.value = updatedState }</ID>
<ID>MultilineLambdaItParameter:TokenDetailsOnrampTransactionStateConverter.kt$TokenDetailsOnrampTransactionStateConverter${ analyticsEventHandler.send(TokenOnrampAnalyticsEvent.GoToProvider) clickIntents.onGoToProviderClick(it) }</ID>
<ID>MultilineLambdaItParameter:TokenDetailsScreen.kt${ Notification( modifier = itemModifier.animateItem(), config = it.config, iconTint = when (it) { is TokenDetailsNotification.Informational -&gt; TangemTheme.colors.icon.accent is TokenDetailsNotification.UsedOutdatedData -&gt; TangemTheme.colors.text.attention else -&gt; null }, ) }</ID>
<ID>MultilineLambdaItParameter:TokenDetailsTopAppBar.kt${ TangemDropdownItem( item = it, dismissParent = { showDropdownMenu = false }, ) }</ID>
<ID>MultilineLambdaItParameter:TokenStakingBlock.kt${ when (it) { is StakingBlockUM.TemporaryUnavailable -&gt; StakingTemporaryUnavailableBlock() is StakingBlockUM.Loading -&gt; StakingLoading() is StakingBlockUM.Staked -&gt; StakingBalanceBlock( state = it, isBalanceHidden = isBalanceHidden, ) is StakingBlockUM.StakeAvailable -&gt; StakingAvailableContent( state = it, ) } }</ID>
<ID>NamedArguments:TokenDetailsLoadedBalanceConverter.kt$TokenDetailsLoadedBalanceConverter$formatFiatAmount( status.value, stakingFiatAmount, currentState.selectedBalanceType, appCurrencyProvider(), )</ID>
<ID>NamedArguments:TokenDetailsSwapTransactionsStateConverter.kt$TokenDetailsSwapTransactionsStateConverter$createStateInfo( transaction, toCryptoCurrency, fromCryptoCurrency, toFiatAmount, fromFiatAmount, )</ID>
<ID>NestedScopeFunctions:TokenDetailsBalanceSelectStateConverter.kt$TokenDetailsBalanceSelectStateConverter$let { cryptoCurrencyStatus.value.fiatRate?.multiply(it) }</ID>
<ID>NonBooleanPropertyPrefixedWithIs:TokenDetailsModel.kt$TokenDetailsModel$private val isDemoCardUseCase: IsDemoCardUseCase</ID>
<ID>NullableBooleanCheck:TokenDetailsSwapTransactionsStateConverter.kt$TokenDetailsSwapTransactionsStateConverter$transaction.status?.hasLongTime ?: false</ID>
<ID>NullableToStringCall:DefaultTokenDetailsDeepLinkHandler.kt$DefaultTokenDetailsDeepLinkHandler$$networkId</ID>
<ID>NullableToStringCall:DefaultTokenDetailsDeepLinkHandler.kt$DefaultTokenDetailsDeepLinkHandler$$tokenId</ID>
<ID>NullableToStringCall:TokenDetailsStakingInfoConverter.kt$TokenDetailsStakingInfoConverter$$stakingCryptoAmount</ID>
<ID>NullableToStringCall:TokenDetailsStakingInfoConverter.kt$TokenDetailsStakingInfoConverter$$stakingEntryInfo</ID>
<ID>NullableToStringCall:TokenDetailsStakingInfoConverter.kt$TokenDetailsStakingInfoConverter$$yieldBalance</ID>
<ID>PropertyUsedBeforeDeclaration:ExpressStatusBottomSheetStateProvider.kt$ExpressStatusBottomSheetStateProvider$network</ID>
<ID>PropertyUsedBeforeDeclaration:ExpressStatusBottomSheetStateProvider.kt$ExpressStatusBottomSheetStateProvider$token</ID>
<ID>PropertyUsedBeforeDeclaration:TokenDetailsModel.kt$TokenDetailsModel$uiState</ID>
@ -49,7 +46,6 @@
<ID>UseEmptyCounterpart:TokenDetailsAnalyticsEvent.kt$TokenDetailsAnalyticsEvent$mapOf()</ID>
<ID>UseEmptyCounterpart:TokenDetailsAnalyticsEvent.kt$TokenDetailsAnalyticsEvent.Notice$mapOf()</ID>
<ID>UseOrEmpty:ExchangeStatusFactory.kt$ExchangeStatusFactory$savedTransactions ?.flatMap { setOf(it.fromCryptoCurrency.id, it.toCryptoCurrency.id) } ?.toSet() ?.getQuotesOrEmpty() ?: emptySet()</ID>
<ID>UseOrEmpty:TokenDetailsStakingInfoConverter.kt$TokenDetailsStakingInfoConverter$yieldBalance?.balance?.items ?: emptyList()</ID>
<ID>VarCouldBeVal:TokenDetailsModel.kt$TokenDetailsModel$private var expressTxStatusTaskScheduler = SingleTaskScheduler&lt;PersistentList&lt;ExpressTransactionStateUM&gt;&gt;()</ID>
</CurrentIssues>
</SmellBaseline>

View file

@ -535,6 +535,7 @@ internal class TokenDetailsModel @Inject constructor(
token = cryptoCurrency.symbol,
blockchain = cryptoCurrency.network.name,
status = unavailabilityReason.toReasonAnalyticsText(),
derivationIndex = getAccountIndexOrNull(),
),
)
@ -560,6 +561,7 @@ internal class TokenDetailsModel @Inject constructor(
token = cryptoCurrency.symbol,
blockchain = cryptoCurrency.network.name,
status = null,
derivationIndex = getAccountIndexOrNull(),
),
)
router.openTokenDetails(userWalletId = userWalletId, currency = cryptoCurrency)
@ -581,6 +583,7 @@ internal class TokenDetailsModel @Inject constructor(
token = cryptoCurrency.symbol,
blockchain = cryptoCurrency.network.name,
status = unavailabilityReason.toReasonAnalyticsText(),
derivationIndex = getAccountIndexOrNull(),
),
)
@ -701,6 +704,7 @@ internal class TokenDetailsModel @Inject constructor(
token = cryptoCurrency.symbol,
blockchain = cryptoCurrency.network.name,
status = unavailabilityReason.toReasonAnalyticsText(),
derivationIndex = getAccountIndexOrNull(),
),
)
@ -769,6 +773,12 @@ internal class TokenDetailsModel @Inject constructor(
showErrorIfDemoModeOrElse(action = ::openExplorer)
}
private fun getAccountIndexOrNull(): Int? {
val account = account
val isNotMainAccount = account != null && !account.isMainAccount
return if (isNotMainAccount) account.derivationIndex.value else null
}
private fun openExplorer() {
val currencyStatus = cryptoCurrencyStatus ?: return
@ -881,7 +891,7 @@ internal class TokenDetailsModel @Inject constructor(
PromoAnalyticsEvent.PromotionBannerClicked(
source = AnalyticsParam.ScreensSources.Token,
program = PromoAnalyticsEvent.Program.Empty, // Use it on new promo action
action = PromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Closed,
action = PromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Closed(),
),
)
}
@ -894,7 +904,7 @@ internal class TokenDetailsModel @Inject constructor(
PromoAnalyticsEvent.PromotionBannerClicked(
source = AnalyticsParam.ScreensSources.Token,
program = PromoAnalyticsEvent.Program.Empty, // Use it on new promo action
action = PromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Clicked,
action = PromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Clicked(),
),
)
}

View file

@ -5,7 +5,7 @@ import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance
import com.tangem.feature.tokendetails.presentation.tokendetails.state.*
import com.tangem.feature.tokendetails.presentation.tokendetails.state.utils.getBalance
@ -29,8 +29,8 @@ internal class TokenDetailsBalanceSelectStateConverter(
return this
}
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
val stakingCryptoAmount = yieldBalance?.getTotalWithRewardsStakingBalance(
val stakingBalance = cryptoCurrencyStatus.value.stakingBalance as? StakingBalance.Data
val stakingCryptoAmount = stakingBalance?.getTotalWithRewardsStakingBalance(
cryptoCurrencyStatus.currency.network.rawId,
)
val stakingFiatAmount = stakingCryptoAmount?.let { cryptoCurrencyStatus.value.fiatRate?.multiply(it) }

View file

@ -9,7 +9,7 @@ import com.tangem.core.ui.format.bigdecimal.*
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
@ -73,7 +73,7 @@ internal class TokenDetailsLoadedBalanceConverter(
status: CryptoCurrencyStatus,
): TokenDetailsBalanceBlockState {
val stakingCryptoAmount =
(status.value.yieldBalance as? YieldBalance.Data)?.getTotalWithRewardsStakingBalance(
(status.value.stakingBalance as? StakingBalance.Data)?.getTotalWithRewardsStakingBalance(
status.currency.network.rawId,
)
val stakingFiatAmount = stakingCryptoAmount?.let { status.value.fiatRate?.multiply(it) }

View file

@ -83,9 +83,9 @@ internal class TokenDetailsOnrampTransactionStateConverter(
fallbackResId = R.drawable.ic_currency_24,
),
iconState = getIconState(value.status),
onGoToProviderClick = {
analyticsEventHandler.send(TokenOnrampAnalyticsEvent.GoToProvider)
clickIntents.onGoToProviderClick(it)
onGoToProviderClick = { url ->
analyticsEventHandler.send(TokenOnrampAnalyticsEvent.GoToProvider())
clickIntents.onGoToProviderClick(url)
},
onClick = { clickIntents.onExpressTransactionClick(value.txId) },
onDisposeExpressStatus = clickIntents::onConfirmDisposeExpressStatus,
@ -117,7 +117,7 @@ internal class TokenDetailsOnrampTransactionStateConverter(
private fun onProviderClick(externalTxUrl: String?) = if (externalTxUrl != null) {
{
analyticsEventHandler.send(TokenOnrampAnalyticsEvent.GoToProvider)
analyticsEventHandler.send(TokenOnrampAnalyticsEvent.GoToProvider())
clickIntents.onGoToProviderClick(externalTxUrl)
}
} else {
@ -269,7 +269,7 @@ internal class TokenDetailsOnrampTransactionStateConverter(
icon = R.drawable.ic_arrow_top_right_24,
text = resourceReference(R.string.common_go_to_provider),
onClick = {
analyticsEventHandler.send(TokenOnrampAnalyticsEvent.GoToProvider)
analyticsEventHandler.send(TokenOnrampAnalyticsEvent.GoToProvider())
clickIntents.onGoToProviderClick(externalTxUrl)
},
)

View file

@ -10,7 +10,7 @@ import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.staking.RewardBlockType
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.staking.model.StakingAvailability
import com.tangem.domain.staking.model.StakingEntryInfo
import com.tangem.domain.staking.utils.getRewardStakingBalance
@ -54,18 +54,29 @@ internal class TokenDetailsStakingInfoConverter(
}
}
@Suppress("CyclomaticComplexMethod")
private fun getStakingInfoBlock(status: CryptoCurrencyStatus, state: TokenDetailsState): StakingBlockUM? {
val yieldBalance = status.value.yieldBalance as? YieldBalance.Data
val stakingBalance = status.value.stakingBalance as? StakingBalance.Data
val stakingCryptoAmount = yieldBalance?.getTotalStakingBalance(status.currency.network.rawId)
val pendingBalances = yieldBalance?.balance?.items ?: emptyList()
val stakingCryptoAmount = stakingBalance?.getTotalStakingBalance(status.currency.network.rawId)
val hasPendingBalances = when (stakingBalance) {
is StakingBalance.Data.StakeKit -> stakingBalance.balance.items.isNotEmpty()
is StakingBalance.Data.P2P -> !stakingBalance.unstakingAmount.isNullOrZero()
null -> false
}
val pendingAmount = when (stakingBalance) {
is StakingBalance.Data.StakeKit -> stakingBalance.balance.items.sumOf { it.amount }
is StakingBalance.Data.P2P -> stakingBalance.unstakingAmount
null -> BigDecimal.ZERO
}
val iconState = state.tokenInfoBlockState.iconState
Timber.i(
"""
getStakingInfoBlock:
yieldBalance: $yieldBalance
stakingBalance: ${stakingBalance ?: "null"}
stakingCryptoAmount: $stakingCryptoAmount
stakingEntryInfo: $stakingEntryInfo
""".trimIndent(),
@ -73,16 +84,24 @@ internal class TokenDetailsStakingInfoConverter(
return when {
stakingCryptoAmount.isNullOrZero() && stakingEntryInfo != null -> {
if (pendingBalances.isEmpty()) {
if (!hasPendingBalances) {
getStakeAvailableState(stakingEntryInfo, iconState, isStakingButtonEnabled(status))
} else {
getStakedBlockWithFiatAmount(status, pendingBalances.sumOf { it.amount }, null)
getStakedBlockWithFiatAmount(status, pendingAmount, null)
}
}
stakingCryptoAmount.isNullOrZero() && stakingEntryInfo == null -> {
null
}
else -> getStakedBlockWithFiatAmount(status, stakingCryptoAmount, yieldBalance?.getRewardStakingBalance())
else -> getStakedBlockWithFiatAmount(
status = status,
stakingAmount = stakingCryptoAmount,
rewardAmount = when (stakingBalance) {
is StakingBalance.Data.StakeKit -> stakingBalance.getRewardStakingBalance()
is StakingBalance.Data.P2P -> stakingBalance.totalRewards
else -> BigDecimal.ZERO
},
)
}
}