Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-03 13:08:53 +05:00
parent 665d56c9e8
commit ba664f1557
12 changed files with 119 additions and 130 deletions

View file

@ -0,0 +1,63 @@
package com.tangem.feature.wallet.child.tokenActions
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.components.SimpleSettingsRow
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
import com.tangem.core.ui.components.getDefaultRowColors
import com.tangem.core.ui.components.getWarningRowColors
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.wallet.presentation.wallet.state.model.TokenActionButtonUM
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
internal class TokenActionsComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
@Assisted private val params: Params,
) : ComposableBottomSheetComponent, AppComponentContext by appComponentContext {
override fun dismiss() {
params.onDismiss()
}
@Composable
override fun BottomSheet() {
TangemBottomSheet<TangemBottomSheetConfigContent.Empty>(
containerColor = TangemTheme.colors.background.primary,
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = ::dismiss,
content = TangemBottomSheetConfigContent.Empty,
),
) {
Column {
params.actions.forEach { action ->
if (action.isEnabled) {
val rowColors = if (action.isWarning) {
getWarningRowColors()
} else {
getDefaultRowColors()
}
SimpleSettingsRow(
title = action.text.resolveReference(),
icon = action.iconResId,
enabled = action.isEnabled,
rowColors = rowColors,
onItemsClick = action.onClick,
)
}
}
}
}
}
data class Params(
val actions: List<TokenActionButtonUM>,
val onDismiss: () -> Unit,
)
}

View file

@ -21,6 +21,7 @@ import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.decompose.ComposableDialogComponent
import com.tangem.domain.tokens.model.details.TokenAction
import com.tangem.feature.wallet.child.tokenActions.TokenActionsComponent
import com.tangem.feature.wallet.child.wallet.model.WalletModel
import com.tangem.feature.wallet.navigation.WalletRoute
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig
@ -136,6 +137,15 @@ internal class WalletComponent @AssistedInject constructor(
),
)
}
is WalletDialogConfig.TokenActionList -> {
TokenActionsComponent(
appComponentContext = childByContext(componentContext),
params = TokenActionsComponent.Params(
actions = dialogConfig.actionList,
onDismiss = model.innerWalletRouter.dialogNavigation::dismiss,
),
)
}
}
},
)

View file

@ -19,7 +19,6 @@ import com.tangem.domain.nft.analytics.NFTAnalyticsEvent
import com.tangem.domain.settings.ShouldShowMarketsTooltipUseCase
import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.model.TokenActionsState
import com.tangem.domain.tokens.model.details.NavigationAction
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
@ -29,7 +28,6 @@ import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAn
import com.tangem.feature.wallet.presentation.wallet.domain.OnrampStatusFactory
import com.tangem.feature.wallet.presentation.wallet.domain.unwrap
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.model.ActionsBottomSheetConfig
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAlertUM
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNFTItemUM
@ -142,8 +140,15 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
getCryptoCurrencyActionsUseCase(userWallet = userWallet, cryptoCurrencyStatus = cryptoCurrencyStatus)
.take(count = 1)
.collectLatest {
showActionsBottomSheet(it, userWallet, accountId)
.collectLatest { actionsState ->
router.openTokenActionSheet(
userWallet = userWallet,
tokenActionList = MultiWalletCurrencyActionsConverter(
userWallet = userWallet,
accountId = accountId,
clickIntents = currencyActionsClickIntents,
).convert(actionsState),
)
}
}
}
@ -259,23 +264,6 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
analyticsEventHandler.send(event)
}
private fun showActionsBottomSheet(
tokenActionsState: TokenActionsState,
userWallet: UserWallet,
accountId: AccountId,
) {
stateHolder.showBottomSheet(
ActionsBottomSheetConfig(
actions = MultiWalletCurrencyActionsConverter(
userWallet = userWallet,
accountId = accountId,
clickIntents = currencyActionsClickIntents,
).convert(tokenActionsState),
),
userWallet.walletId,
)
}
override fun onTransactionClick(txHash: String) {
modelScope.launch(dispatchers.main) {
val currency = getSingleCryptoCurrencyStatusUseCase.unwrap(

View file

@ -3,9 +3,10 @@ package com.tangem.feature.wallet.presentation.common
import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.*
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAdditionalInfo
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTopBarConfig
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
@Suppress("LargeClass")
internal object WalletPreviewDataLegacy {
@ -54,15 +55,4 @@ internal object WalletPreviewDataLegacy {
UserWalletId(stringValue = "24") to walletCardErrorState,
)
}
val actionsBottomSheet = ActionsBottomSheetConfig(
actions = listOf(
TokenActionButtonConfig(
text = TextReference.Str("Send"),
iconResId = R.drawable.ic_share_24,
isWarning = false,
onClick = {},
),
).toImmutableList(),
)
}

View file

@ -19,7 +19,9 @@ import com.tangem.domain.redux.StateDialog
import com.tangem.domain.tokens.model.details.NavigationAction
import com.tangem.domain.tokens.model.details.TokenAction
import com.tangem.feature.wallet.navigation.WalletRoute
import com.tangem.feature.wallet.presentation.wallet.state.model.TokenActionButtonUM
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import javax.inject.Inject
@ -151,4 +153,12 @@ internal class DefaultWalletRouter @Inject constructor(
),
)
}
override fun openTokenActionSheet(userWallet: UserWallet, tokenActionList: ImmutableList<TokenActionButtonUM>) {
dialogNavigation.activate(
configuration = WalletDialogConfig.TokenActionList(
actionList = tokenActionList,
),
)
}
}

View file

@ -14,7 +14,9 @@ import com.tangem.domain.pay.TangemPayDetailsConfig
import com.tangem.domain.tokens.model.details.NavigationAction
import com.tangem.domain.tokens.model.details.TokenAction
import com.tangem.feature.wallet.navigation.WalletRoute
import com.tangem.feature.wallet.presentation.wallet.state.model.TokenActionButtonUM
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.flow.SharedFlow
/**
@ -81,4 +83,7 @@ internal interface InnerWalletRouter {
/** Open yield supply entry screen */
fun openYieldSupplyEntryScreen(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, apy: String)
/** Open token action sheet */
fun openTokenActionSheet(userWallet: UserWallet, tokenActionList: ImmutableList<TokenActionButtonUM>)
}

View file

@ -1,13 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.state.model
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import kotlinx.collections.immutable.ImmutableList
/**
* Config for the token actions bottom sheet
*
* @property actions actions
*/
internal data class ActionsBottomSheetConfig(
val actions: ImmutableList<TokenActionButtonConfig>,
) : TangemBottomSheetConfigContent

View file

@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.model
import androidx.annotation.DrawableRes
import com.tangem.core.ui.extensions.TextReference
import kotlinx.serialization.Serializable
/**
* Action button config
@ -10,12 +11,13 @@ import com.tangem.core.ui.extensions.TextReference
* @property iconResId icon resource id
* @property onClick lambda be invoked when action component is clicked
* @property isWarning if warning row
* @property enabled enabled
* @property isEnabled enabled
*/
data class TokenActionButtonConfig(
@Serializable
data class TokenActionButtonUM(
val text: TextReference,
@DrawableRes val iconResId: Int,
val onClick: () -> Unit,
val isWarning: Boolean,
val enabled: Boolean = true,
val isEnabled: Boolean = true,
)

View file

@ -4,6 +4,7 @@ import com.tangem.domain.models.TokenReceiveConfig
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.tokens.model.details.TokenAction
import kotlinx.collections.immutable.ImmutableList
import kotlinx.serialization.Serializable
/**
@ -26,6 +27,11 @@ internal sealed interface WalletDialogConfig {
@Serializable
data class TokenReceive(val tokenReceiveConfig: TokenReceiveConfig) : WalletDialogConfig
@Serializable
data class TokenActionList(
val actionList: ImmutableList<TokenActionButtonUM>,
) : WalletDialogConfig
@Serializable
data class YieldSupplyWarning(
val cryptoCurrency: CryptoCurrency,

View file

@ -2,7 +2,6 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers.convert
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
@ -10,7 +9,8 @@ import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
import com.tangem.domain.tokens.model.TokenActionsState
import com.tangem.feature.wallet.child.wallet.model.intents.WalletCurrencyActionsClickIntents
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.TokenActionButtonConfig
import com.tangem.feature.wallet.presentation.wallet.state.model.TokenActionButtonUM
import com.tangem.feature.wallet.presentation.wallet.state.utils.isSingleWalletWithToken
import com.tangem.utils.converter.Converter
import com.tangem.utils.isNullOrZero
import kotlinx.collections.immutable.ImmutableList
@ -20,9 +20,9 @@ internal class MultiWalletCurrencyActionsConverter(
private val userWallet: UserWallet,
private val accountId: AccountId,
private val clickIntents: WalletCurrencyActionsClickIntents,
) : Converter<TokenActionsState, ImmutableList<TokenActionButtonConfig>> {
) : Converter<TokenActionsState, ImmutableList<TokenActionButtonUM>> {
override fun convert(value: TokenActionsState): ImmutableList<TokenActionButtonConfig> {
override fun convert(value: TokenActionsState): ImmutableList<TokenActionButtonUM> {
return value.states
.filterIfSingleWithToken()
.mapNotNull {
@ -32,9 +32,7 @@ internal class MultiWalletCurrencyActionsConverter(
}
private fun List<TokenActionsState.ActionState>.filterIfSingleWithToken(): List<TokenActionsState.ActionState> {
return if (userWallet is UserWallet.Cold &&
userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()
) {
return if (userWallet.isSingleWalletWithToken()) {
filter { it !is TokenActionsState.ActionState.HideToken }
} else {
this
@ -45,7 +43,7 @@ internal class MultiWalletCurrencyActionsConverter(
private fun mapTokenActionState(
actionsState: TokenActionsState.ActionState,
cryptoCurrencyStatus: CryptoCurrencyStatus,
): TokenActionButtonConfig? {
): TokenActionButtonUM? {
if (actionsState is TokenActionsState.ActionState.Send && cryptoCurrencyStatus.value.amount.isNullOrZero()) {
return null
}
@ -112,12 +110,12 @@ internal class MultiWalletCurrencyActionsConverter(
}
}
return TokenActionButtonConfig(
return TokenActionButtonUM(
text = title,
iconResId = icon,
onClick = action,
isWarning = actionsState is TokenActionsState.ActionState.HideToken,
enabled = actionsState.unavailabilityReason == noneReason,
isEnabled = actionsState.unavailabilityReason == noneReason,
)
}
}

View file

@ -73,7 +73,6 @@ import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreview
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewDataLegacy.walletScreenState
import com.tangem.feature.wallet.presentation.wallet.state.model.*
import com.tangem.feature.wallet.presentation.wallet.state.model.holder.TxHistoryStateHolder
import com.tangem.feature.wallet.presentation.wallet.ui.components.TokenActionsBottomSheet
import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletsList
import com.tangem.feature.wallet.presentation.wallet.ui.components.common.*
import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.nftCollections
@ -716,7 +715,6 @@ internal fun LazyListScope.nftCollections(state: WalletState, itemModifier: Modi
private fun ShowBottomSheet(bottomSheetConfig: TangemBottomSheetConfig?) {
if (bottomSheetConfig != null) {
when (bottomSheetConfig.content) {
is ActionsBottomSheetConfig -> TokenActionsBottomSheet(config = bottomSheetConfig)
is ChooseAddressBottomSheetConfig -> ChooseAddressBottomSheet(config = bottomSheetConfig)
is ExpressStatusBottomSheetConfig -> ExpressStatusBottomSheet(config = bottomSheetConfig)
}

View file

@ -1,68 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import com.tangem.core.ui.components.SimpleSettingsRow
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
import com.tangem.core.ui.components.getDefaultRowColors
import com.tangem.core.ui.components.getWarningRowColors
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.feature.wallet.presentation.common.WalletPreviewDataLegacy
import com.tangem.feature.wallet.presentation.wallet.state.model.ActionsBottomSheetConfig
import com.tangem.feature.wallet.presentation.wallet.state.model.TokenActionButtonConfig
import kotlinx.collections.immutable.ImmutableList
@Composable
internal fun TokenActionsBottomSheet(config: TangemBottomSheetConfig) {
TangemBottomSheet<ActionsBottomSheetConfig>(config = config) {
ActionsBottomSheetContent(actions = it.actions)
}
}
@Composable
private fun ActionsBottomSheetContent(actions: ImmutableList<TokenActionButtonConfig>) {
Column(modifier = Modifier.background(TangemTheme.colors.background.primary)) {
actions.forEach { action ->
if (action.enabled) {
val rowColors = if (action.isWarning) {
getWarningRowColors()
} else {
getDefaultRowColors()
}
SimpleSettingsRow(
title = action.text.resolveReference(),
icon = action.iconResId,
enabled = action.enabled,
rowColors = rowColors,
onItemsClick = action.onClick,
)
}
}
}
}
@Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun ActionsBottomSheetContent_Light(
@PreviewParameter(ActionsBottomSheetContentConfigProvider::class)
config: ActionsBottomSheetConfig,
) {
TangemThemePreview {
// Use preview of content because ModalBottomSheet isn't supported in Preview mode
ActionsBottomSheetContent(actions = config.actions)
}
}
private class ActionsBottomSheetContentConfigProvider : CollectionPreviewParameterProvider<ActionsBottomSheetConfig>(
collection = listOf(WalletPreviewDataLegacy.actionsBottomSheet),
)