Updated on 2026-08-14
This commit is contained in:
parent
0b86264bbf
commit
6a082c1d58
19 changed files with 603 additions and 118 deletions
|
|
@ -0,0 +1,110 @@
|
|||
package com.tangem.feature.wallet.child.tokenActions
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
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.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.feature.wallet.child.tokenActions.TokenActionsComponent.Params
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.fastForEach
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
internal class DefaultTokenActionsComponent @AssistedInject constructor(
|
||||
@Assisted appComponentContext: AppComponentContext,
|
||||
@Assisted private val params: Params,
|
||||
val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||
) : TokenActionsComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
val isBalanceHiddenFlow: StateFlow<Boolean>
|
||||
field = MutableStateFlow(false)
|
||||
|
||||
init {
|
||||
getBalanceHidingSettingsUseCase()
|
||||
.conflate()
|
||||
.distinctUntilChanged()
|
||||
.onEach {
|
||||
isBalanceHiddenFlow.value = it.isBalanceHidden
|
||||
}
|
||||
.launchIn(componentScope)
|
||||
}
|
||||
|
||||
override fun dismiss() {
|
||||
params.onDismiss()
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun BottomSheet() {
|
||||
if (!LocalRedesignEnabled.current) {
|
||||
TangemBottomSheet<TangemBottomSheetConfigContent.Empty>(
|
||||
containerColor = TangemTheme.colors.background.primary,
|
||||
config = TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
onDismissRequest = ::dismiss,
|
||||
content = TangemBottomSheetConfigContent.Empty,
|
||||
),
|
||||
) {
|
||||
Column {
|
||||
params.actions.fastForEach { 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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
if (params.tokenRowUM == null) {
|
||||
dismiss()
|
||||
} else {
|
||||
val isBalanceHidden by isBalanceHiddenFlow.collectAsStateWithLifecycle()
|
||||
if (LocalRedesignEnabled.current) {
|
||||
val offset = with(LocalDensity.current) {
|
||||
DpOffset(params.offsetX.toDp(), params.offsetY.toDp())
|
||||
}
|
||||
TokenActionContent(
|
||||
tokenRowUM = params.tokenRowUM,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
offset = offset,
|
||||
actions = params.actions,
|
||||
onDismiss = params.onDismiss,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : TokenActionsComponent.Factory {
|
||||
override fun create(context: AppComponentContext, params: Params): DefaultTokenActionsComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
package com.tangem.feature.wallet.child.tokenActions
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import com.tangem.core.ui.ds.contextmenu.CenteredContextMenuPositionProvider
|
||||
import com.tangem.core.ui.ds.contextmenu.TangemContextMenu
|
||||
import com.tangem.core.ui.ds.row.token.TangemTokenRow
|
||||
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
|
||||
import com.tangem.core.ui.ds.row.token.internal.TangemTokenRowPreviewData
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TokenActionButtonUM
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
@Composable
|
||||
internal fun TokenActionContent(
|
||||
tokenRowUM: TangemTokenRowUM,
|
||||
isBalanceHidden: Boolean,
|
||||
offset: DpOffset,
|
||||
actions: ImmutableList<TokenActionButtonUM>,
|
||||
modifier: Modifier = Modifier,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
val density = LocalDensity.current
|
||||
var anchorShiftPx by remember { mutableIntStateOf(0) }
|
||||
val anchorShiftDp = with(density) { anchorShiftPx.toDp() }
|
||||
val animatedShift by animateDpAsState(
|
||||
targetValue = anchorShiftDp,
|
||||
animationSpec = tween(),
|
||||
label = "AnchorShift",
|
||||
)
|
||||
|
||||
Box(modifier.fillMaxSize()) {
|
||||
Box(modifier = Modifier.offset(y = offset.y - animatedShift)) {
|
||||
TangemTokenRow(
|
||||
tokenRowUM = tokenRowUM,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
reorderableState = null,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens2.x3)
|
||||
.clip(RoundedCornerShape(18.dp))
|
||||
.background(TangemTheme.colors2.surface.level3),
|
||||
)
|
||||
TangemContextMenu(
|
||||
expanded = true,
|
||||
onDismissRequest = onDismiss,
|
||||
positionProvider = remember(density) {
|
||||
CenteredContextMenuPositionProvider(
|
||||
contentOffset = DpOffset(x = 0.dp, y = 12.dp),
|
||||
density = density,
|
||||
onAnchorShiftRequired = { shift ->
|
||||
if (anchorShiftPx == 0 && shift > 0) {
|
||||
anchorShiftPx = shift
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
) {
|
||||
TokenActionContextMenuContent(
|
||||
actions = actions,
|
||||
onDismiss = onDismiss,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TokenActionContextMenuContent(actions: ImmutableList<TokenActionButtonUM>, onDismiss: () -> Unit) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.widthIn(min = 206.dp)
|
||||
.padding(
|
||||
vertical = TangemTheme.dimens2.x2_5,
|
||||
horizontal = TangemTheme.dimens2.x4,
|
||||
),
|
||||
) {
|
||||
actions.fastForEach { item ->
|
||||
Column {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
|
||||
modifier = Modifier
|
||||
.clickable(
|
||||
enabled = item.isEnabled,
|
||||
onClick = {
|
||||
item.onClick()
|
||||
onDismiss()
|
||||
},
|
||||
)
|
||||
.padding(
|
||||
start = TangemTheme.dimens2.x1_5,
|
||||
end = TangemTheme.dimens2.x2,
|
||||
top = TangemTheme.dimens2.x2_5,
|
||||
bottom = TangemTheme.dimens2.x2_5,
|
||||
),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(item.iconResId),
|
||||
contentDescription = null,
|
||||
tint = if (item.isWarning) {
|
||||
TangemTheme.colors2.graphic.status.warning
|
||||
} else {
|
||||
TangemTheme.colors2.graphic.neutral.primary
|
||||
},
|
||||
modifier = Modifier.size(TangemTheme.dimens2.x5),
|
||||
)
|
||||
Text(
|
||||
text = item.text.resolveReference(),
|
||||
style = TangemTheme.typography2.headingRegular17,
|
||||
color = if (item.isWarning) {
|
||||
TangemTheme.colors2.text.status.warning
|
||||
} else {
|
||||
TangemTheme.colors2.text.neutral.primary
|
||||
},
|
||||
)
|
||||
}
|
||||
if (item.hasDivider) {
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
vertical = TangemTheme.dimens2.x2_5,
|
||||
horizontal = TangemTheme.dimens2.x2,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.height(1.dp)
|
||||
.background(TangemTheme.colors2.border.neutral.primary),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
private fun TokenActionContent_Preview() {
|
||||
TangemThemePreviewRedesign {
|
||||
TokenActionContent(
|
||||
tokenRowUM = TangemTokenRowPreviewData.tokenState,
|
||||
offset = DpOffset(
|
||||
x = 100.dp,
|
||||
y = 100.dp,
|
||||
),
|
||||
actions = persistentListOf(
|
||||
TokenActionButtonUM(
|
||||
id = "Send",
|
||||
text = stringReference("Send"),
|
||||
iconResId = R.drawable.ic_arrow_up_24,
|
||||
isEnabled = true,
|
||||
isWarning = false,
|
||||
hasDivider = false,
|
||||
onClick = {},
|
||||
),
|
||||
TokenActionButtonUM(
|
||||
id = "Receive",
|
||||
text = stringReference("Receive"),
|
||||
iconResId = R.drawable.ic_arrow_down_24,
|
||||
isEnabled = true,
|
||||
isWarning = false,
|
||||
hasDivider = false,
|
||||
onClick = {},
|
||||
),
|
||||
TokenActionButtonUM(
|
||||
id = "Swap",
|
||||
text = stringReference("Swap"),
|
||||
iconResId = R.drawable.ic_exchange_vertical_24,
|
||||
isEnabled = true,
|
||||
isWarning = false,
|
||||
hasDivider = true,
|
||||
onClick = {},
|
||||
),
|
||||
TokenActionButtonUM(
|
||||
id = "Remove",
|
||||
text = stringReference("Remove"),
|
||||
iconResId = R.drawable.ic_trash_24,
|
||||
isEnabled = true,
|
||||
isWarning = true,
|
||||
hasDivider = false,
|
||||
onClick = {},
|
||||
),
|
||||
),
|
||||
isBalanceHidden = false,
|
||||
onDismiss = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
|
@ -1,64 +1,20 @@
|
|||
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.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TokenActionButtonUM
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.fastForEach
|
||||
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.fastForEach { 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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
internal interface TokenActionsComponent : ComposableBottomSheetComponent, ComposableContentComponent {
|
||||
data class Params(
|
||||
val actions: List<TokenActionButtonUM>,
|
||||
val actions: ImmutableList<TokenActionButtonUM>,
|
||||
val tokenRowUM: TangemTokenRowUM?,
|
||||
val offsetX: Float,
|
||||
val offsetY: Float,
|
||||
val onDismiss: () -> Unit,
|
||||
)
|
||||
|
||||
interface Factory : ComponentFactory<Params, TokenActionsComponent>
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.feature.wallet.child.tokenActions.di
|
||||
|
||||
import com.tangem.feature.wallet.child.tokenActions.DefaultTokenActionsComponent
|
||||
import com.tangem.feature.wallet.child.tokenActions.TokenActionsComponent
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface TokenActionsModule {
|
||||
|
||||
@Binds
|
||||
fun bindTokenActionsComponentFactory(impl: DefaultTokenActionsComponent.Factory): TokenActionsComponent.Factory
|
||||
}
|
||||
|
|
@ -17,12 +17,14 @@ import com.tangem.core.decompose.context.childByContext
|
|||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.DesignFeatureToggles
|
||||
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
|
||||
import com.tangem.core.ui.components.haze.hazeEffectTangem
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.core.ui.decompose.ComposableDialogComponent
|
||||
import com.tangem.core.ui.utils.parseBigDecimal
|
||||
import com.tangem.domain.tokens.model.details.TokenAction
|
||||
import com.tangem.feature.wallet.child.organizetokens.OrganizeTokensComponent
|
||||
import com.tangem.feature.wallet.child.tokenActions.DefaultTokenActionsComponent
|
||||
import com.tangem.feature.wallet.child.tokenActions.TokenActionsComponent
|
||||
import com.tangem.feature.wallet.child.wallet.model.WalletModel
|
||||
import com.tangem.feature.wallet.navigation.WalletRoute
|
||||
|
|
@ -61,6 +63,7 @@ internal class WalletComponent @AssistedInject constructor(
|
|||
private val promoBannersBlockComponentFactory: PromoBannersBlockComponent.Factory,
|
||||
private val newPromoBannersFeatureToggles: NewPromoBannersFeatureToggles,
|
||||
private val networkSelectionComponentFactory: NetworkSelectionComponent.Factory,
|
||||
private val tokenActionsComponentFactory: TokenActionsComponent.Factory,
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
) : ComposableContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
|
|
@ -164,11 +167,14 @@ internal class WalletComponent @AssistedInject constructor(
|
|||
)
|
||||
}
|
||||
is WalletDialogConfig.TokenActionList -> {
|
||||
TokenActionsComponent(
|
||||
appComponentContext = childByContext(componentContext),
|
||||
tokenActionsComponentFactory.create(
|
||||
context = childByContext(componentContext),
|
||||
params = TokenActionsComponent.Params(
|
||||
actions = dialogConfig.actionList,
|
||||
onDismiss = model.innerWalletRouter.dialogNavigation::dismiss,
|
||||
tokenRowUM = dialogConfig.tokenRowUM,
|
||||
offsetX = dialogConfig.offsetX,
|
||||
offsetY = dialogConfig.offsetY,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -261,6 +267,13 @@ internal class WalletComponent @AssistedInject constructor(
|
|||
|
||||
when (val dialog = dialog.child?.instance) {
|
||||
is ComposableDialogComponent -> dialog.Dialog()
|
||||
is DefaultTokenActionsComponent -> {
|
||||
if (designFeatureToggles.isRedesignEnabled) {
|
||||
dialog.Content(Modifier.hazeEffectTangem())
|
||||
} else {
|
||||
dialog.BottomSheet()
|
||||
}
|
||||
}
|
||||
is ComposableBottomSheetComponent -> dialog.BottomSheet()
|
||||
else -> {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.feature.wallet.child.wallet.model.intents
|
||||
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
|
||||
|
|
@ -9,6 +10,7 @@ import com.tangem.core.analytics.models.AnalyticsParam
|
|||
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
|
|
@ -38,6 +40,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.transformers.OpenBott
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.MultiWalletCurrencyActionsConverter
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.take
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -56,6 +59,13 @@ internal interface WalletContentClickIntents {
|
|||
|
||||
fun onTokenItemLongClick(accountId: AccountId, cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||
|
||||
fun onTokenItemLongClickV2(
|
||||
accountId: AccountId,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
offset: Offset,
|
||||
tokenRowUM: TangemTokenRowUM,
|
||||
)
|
||||
|
||||
fun onApyLabelClick(accountId: AccountId, currencyStatus: CryptoCurrencyStatus, apySource: ApySource, apy: String)
|
||||
|
||||
fun onYieldPromoCloseClick()
|
||||
|
|
@ -153,6 +163,47 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
accountId = accountId,
|
||||
clickIntents = currencyActionsClickIntents,
|
||||
).convert(actionsState),
|
||||
offset = Offset.Zero,
|
||||
tokenRowUM = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTokenItemLongClickV2(
|
||||
accountId: AccountId,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
offset: Offset,
|
||||
tokenRowUM: TangemTokenRowUM,
|
||||
) {
|
||||
modelScope.launch {
|
||||
val userWalletId = accountId.userWalletId
|
||||
val userWallet = getUserWalletUseCase(userWalletId).getOrElse { exception ->
|
||||
TangemLogger.e(
|
||||
"""
|
||||
Unable to get user wallet
|
||||
|- ID: $userWalletId
|
||||
|- Exception: $exception
|
||||
""".trimIndent(),
|
||||
)
|
||||
|
||||
return@launch
|
||||
}
|
||||
|
||||
getCryptoCurrencyActionsUseCase(userWallet = userWallet, cryptoCurrencyStatus = cryptoCurrencyStatus)
|
||||
.take(count = 1)
|
||||
.collectLatest { actionsState ->
|
||||
router.openTokenActionSheet(
|
||||
userWallet = userWallet,
|
||||
tokenActionList = MultiWalletCurrencyActionsConverter(
|
||||
userWallet = userWallet,
|
||||
accountId = accountId,
|
||||
clickIntents = currencyActionsClickIntents,
|
||||
).convert(actionsState)
|
||||
.filter { it.isEnabled }
|
||||
.toPersistentList(),
|
||||
offset = offset,
|
||||
tokenRowUM = tokenRowUM,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ internal object WalletScreenPreviewData {
|
|||
promoBannerUM = TangemTokenRowUM.PromoBannerUM.Empty,
|
||||
tailUM = TangemRowTailUM.Empty,
|
||||
onItemClick = {},
|
||||
onItemLongClick = {},
|
||||
onItemLongClick = { _, _ -> },
|
||||
)
|
||||
|
||||
private val accountRowDefault = TangemTokenRowUM.Content(
|
||||
|
|
@ -61,7 +61,7 @@ internal object WalletScreenPreviewData {
|
|||
promoBannerUM = TangemTokenRowUM.PromoBannerUM.Empty,
|
||||
tailUM = TangemRowTailUM.Empty,
|
||||
onItemClick = {},
|
||||
onItemLongClick = {},
|
||||
onItemLongClick = { _, _ -> },
|
||||
)
|
||||
|
||||
private val tokenListDefault = WalletTokensListUM.Content(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.feature.wallet.presentation.router
|
||||
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import com.arkivanov.decompose.router.slot.SlotNavigation
|
||||
import com.arkivanov.decompose.router.slot.activate
|
||||
import com.arkivanov.decompose.router.slot.dismiss
|
||||
|
|
@ -8,6 +9,7 @@ import com.tangem.common.routing.AppRouter
|
|||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.DesignFeatureToggles
|
||||
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
|
||||
import com.tangem.domain.models.TokenReceiveConfig
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
|
|
@ -161,10 +163,18 @@ internal class DefaultWalletRouter @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
override fun openTokenActionSheet(userWallet: UserWallet, tokenActionList: ImmutableList<TokenActionButtonUM>) {
|
||||
override fun openTokenActionSheet(
|
||||
userWallet: UserWallet,
|
||||
tokenActionList: ImmutableList<TokenActionButtonUM>,
|
||||
offset: Offset,
|
||||
tokenRowUM: TangemTokenRowUM?,
|
||||
) {
|
||||
dialogNavigation.activate(
|
||||
configuration = WalletDialogConfig.TokenActionList(
|
||||
actionList = tokenActionList,
|
||||
offsetX = offset.x,
|
||||
offsetY = offset.y,
|
||||
tokenRowUM = tokenRowUM,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package com.tangem.feature.wallet.presentation.router
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import com.arkivanov.decompose.router.slot.SlotNavigation
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
|
||||
import com.tangem.domain.models.TokenReceiveConfig
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
|
|
@ -86,7 +88,12 @@ internal interface InnerWalletRouter {
|
|||
fun openYieldSupplyEntryScreen(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, apy: String)
|
||||
|
||||
/** Open token action sheet */
|
||||
fun openTokenActionSheet(userWallet: UserWallet, tokenActionList: ImmutableList<TokenActionButtonUM>)
|
||||
fun openTokenActionSheet(
|
||||
userWallet: UserWallet,
|
||||
tokenActionList: ImmutableList<TokenActionButtonUM>,
|
||||
offset: Offset,
|
||||
tokenRowUM: TangemTokenRowUM?,
|
||||
)
|
||||
|
||||
/** Open QR scanner screen */
|
||||
fun openQrScanner()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.model
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
|
|
@ -13,11 +14,14 @@ import kotlinx.serialization.Serializable
|
|||
* @property isWarning if warning row
|
||||
* @property isEnabled enabled
|
||||
*/
|
||||
@Stable
|
||||
@Serializable
|
||||
data class TokenActionButtonUM(
|
||||
val id: String,
|
||||
val text: TextReference,
|
||||
@DrawableRes val iconResId: Int,
|
||||
val onClick: () -> Unit,
|
||||
val isWarning: Boolean,
|
||||
val isEnabled: Boolean = true,
|
||||
val hasDivider: Boolean = false,
|
||||
)
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.model
|
||||
|
||||
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
|
||||
import com.tangem.domain.models.TokenReceiveConfig
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.account.AccountName
|
||||
|
|
@ -34,6 +35,9 @@ internal sealed interface WalletDialogConfig {
|
|||
@Serializable
|
||||
data class TokenActionList(
|
||||
val actionList: ImmutableList<TokenActionButtonUM>,
|
||||
val tokenRowUM: TangemTokenRowUM?,
|
||||
val offsetY: Float,
|
||||
val offsetX: Float,
|
||||
) : WalletDialogConfig
|
||||
|
||||
@Serializable
|
||||
|
|
|
|||
|
|
@ -23,11 +23,25 @@ internal class MultiWalletCurrencyActionsConverter(
|
|||
) : Converter<TokenActionsState, ImmutableList<TokenActionButtonUM>> {
|
||||
|
||||
override fun convert(value: TokenActionsState): ImmutableList<TokenActionButtonUM> {
|
||||
return value.states
|
||||
.filterIfSingleWithToken()
|
||||
val actionList = value.states.filterIfSingleWithToken()
|
||||
.mapNotNull {
|
||||
mapTokenActionState(actionsState = it, cryptoCurrencyStatus = value.cryptoCurrencyStatus)
|
||||
}
|
||||
|
||||
return actionList
|
||||
.mapIndexed { index, action ->
|
||||
val analyticsAction = TokenActionsState.ActionState.Analytics::class.java.simpleName
|
||||
val hideTokenAction = TokenActionsState.ActionState.HideToken::class.java.simpleName
|
||||
|
||||
if (
|
||||
action.id == analyticsAction ||
|
||||
index != actionList.lastIndex && actionList[index + 1].id == hideTokenAction
|
||||
) {
|
||||
action.copy(hasDivider = true)
|
||||
} else {
|
||||
action
|
||||
}
|
||||
}
|
||||
.toImmutableList()
|
||||
}
|
||||
|
||||
|
|
@ -111,6 +125,7 @@ internal class MultiWalletCurrencyActionsConverter(
|
|||
}
|
||||
|
||||
return TokenActionButtonUM(
|
||||
id = actionsState::class.java.simpleName,
|
||||
text = title,
|
||||
iconResId = icon,
|
||||
onClick = action,
|
||||
|
|
|
|||
|
|
@ -76,8 +76,13 @@ internal class WalletTokenCurrencyItemConverter(
|
|||
onItemLongClick = when (value.value) {
|
||||
CryptoCurrencyStatus.Loading -> null
|
||||
else -> {
|
||||
{
|
||||
clickIntents.onTokenItemLongClick(accountId, value)
|
||||
{ offset, tokenRowUM ->
|
||||
clickIntents.onTokenItemLongClickV2(
|
||||
accountId = accountId,
|
||||
cryptoCurrencyStatus = value,
|
||||
offset = offset,
|
||||
tokenRowUM = tokenRowUM,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ import com.tangem.core.ui.components.background.northernlights.NorthernLightsBac
|
|||
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheetDraggableHeader
|
||||
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
|
||||
import com.tangem.core.ui.components.containers.pullToRefresh.TangemPullToRefreshSlidingContainer
|
||||
import com.tangem.core.ui.components.haze.hazeEffectTangem
|
||||
import com.tangem.core.ui.components.haze.hazeSourceTangem
|
||||
import com.tangem.core.ui.components.rememberIsKeyboardVisible
|
||||
import com.tangem.core.ui.components.sheetscaffold.*
|
||||
|
|
@ -72,6 +73,8 @@ import com.tangem.feature.wallet.presentation.wallet.ui.components.common.Wallet
|
|||
import com.tangem.feature.wallet.presentation.wallet.ui.utils.lazyListStateMapSaver
|
||||
import com.tangem.features.tangempay.component.TangemPayMainBlockComponent
|
||||
import com.tangem.features.tangempay.entity.TangemPayMainUM
|
||||
import dev.chrisbanes.haze.HazeProgressive
|
||||
import dev.chrisbanes.haze.HazeTint
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.math.abs
|
||||
|
||||
|
|
@ -82,6 +85,7 @@ private const val MARKET_HINT_THRESHOLD = 0.5f
|
|||
internal fun WalletScreen2(
|
||||
state: WalletScreenState,
|
||||
tangemPayComponent: TangemPayMainBlockComponent,
|
||||
modifier: Modifier = Modifier,
|
||||
bottomSheetContent: @Composable (() -> Unit),
|
||||
bottomSheetHeaderHeightProvider: () -> Dp,
|
||||
onBottomSheetStateChange: (BottomSheetState) -> Unit,
|
||||
|
|
@ -130,6 +134,7 @@ internal fun WalletScreen2(
|
|||
bottomSheetContent = bottomSheetContent,
|
||||
bottomSheetHeaderHeightProvider = bottomSheetHeaderHeightProvider,
|
||||
onBottomSheetStateChange = onBottomSheetStateChange,
|
||||
modifier = modifier,
|
||||
listStates = listStates,
|
||||
)
|
||||
|
||||
|
|
@ -154,6 +159,7 @@ private fun WalletContent2(
|
|||
tangemPayComponent: TangemPayMainBlockComponent,
|
||||
behavior: TangemCollapsingAppBarBehavior,
|
||||
listStates: Map<Int, LazyListState>,
|
||||
modifier: Modifier = Modifier,
|
||||
bottomSheetHeaderHeightProvider: () -> Dp,
|
||||
onBottomSheetStateChange: (BottomSheetState) -> Unit,
|
||||
bottomSheetContent: @Composable (() -> Unit),
|
||||
|
|
@ -169,6 +175,7 @@ private fun WalletContent2(
|
|||
}
|
||||
|
||||
BaseScaffoldWithMarkets(
|
||||
modifier = modifier,
|
||||
state = state,
|
||||
bottomSheetHeaderHeightProvider = bottomSheetHeaderHeightProvider,
|
||||
onBottomSheetStateChange = onBottomSheetStateChange,
|
||||
|
|
@ -200,7 +207,7 @@ private fun WalletContent2(
|
|||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.hazeSourceTangem(zIndex = -1f),
|
||||
.hazeSourceTangem(zIndex = -2f),
|
||||
) {
|
||||
NorthernLightsBackground(
|
||||
containerColor = if (LocalIsInDarkTheme.current) {
|
||||
|
|
@ -220,10 +227,20 @@ private fun WalletContent2(
|
|||
behavior = behavior,
|
||||
)
|
||||
|
||||
val overlay = TangemTheme.colors2.overlay.overlayPrimary
|
||||
|
||||
HorizontalPager(
|
||||
state = walletsPagerState,
|
||||
userScrollEnabled = canPagerScroll,
|
||||
beyondViewportPageCount = 1,
|
||||
modifier = Modifier.hazeEffectTangem {
|
||||
fallbackTint = HazeTint(color = overlay)
|
||||
progressive = HazeProgressive.verticalGradient(
|
||||
startIntensity = 1f,
|
||||
endIntensity = 1f,
|
||||
preferPerformance = true,
|
||||
)
|
||||
},
|
||||
) { currentWalletIndex ->
|
||||
val listState = listStates[currentWalletIndex] ?: rememberLazyListState()
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import androidx.compose.animation.core.animateFloatAsState
|
|||
import androidx.compose.animation.core.animateIntAsState
|
||||
import androidx.compose.animation.core.snap
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
|
|
@ -17,8 +18,12 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.layout.positionInWindow
|
||||
import androidx.compose.ui.layout.positionOnScreen
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
|
|
@ -155,12 +160,19 @@ private fun LazyListScope.tokenItem(
|
|||
backgroundColor = TangemTheme.colors2.surface.level3,
|
||||
)
|
||||
|
||||
var position by remember { mutableStateOf(Offset.Zero) }
|
||||
when (val tokenRowUM = listItem.tokenRowUM) {
|
||||
is TangemTokenRowUM -> TangemTokenRow(
|
||||
tokenRowUM = tokenRowUM,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
reorderableState = null,
|
||||
modifier = itemModifier,
|
||||
modifier = itemModifier
|
||||
.onGloballyPositioned { position = it.positionOnScreen() }
|
||||
.combinedClickable(
|
||||
enabled = tokenRowUM.onItemClick != null || tokenRowUM.onItemLongClick != null,
|
||||
onClick = tokenRowUM.onItemClick ?: {},
|
||||
onLongClick = { tokenRowUM.onItemLongClick?.invoke(position, tokenRowUM) },
|
||||
),
|
||||
)
|
||||
is TangemHeaderRowUM -> TangemHeaderRow(
|
||||
headerRowUM = tokenRowUM,
|
||||
|
|
@ -170,6 +182,7 @@ private fun LazyListScope.tokenItem(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
private fun LazyListScope.portfolioItem(
|
||||
listItem: TokensListItemUM2.Portfolio,
|
||||
index: Int,
|
||||
|
|
@ -220,12 +233,23 @@ private fun LazyListScope.portfolioItem(
|
|||
.testTag(MainScreenTestTags.TOKEN_LIST_ITEM)
|
||||
.semantics { lazyListItemPosition = tokenIndex + 1 }
|
||||
|
||||
var position by remember { mutableStateOf(Offset.Zero) }
|
||||
when (val tokenRowUM = item.tokenRowUM) {
|
||||
is TangemTokenRowUM -> TangemTokenRow(
|
||||
tokenRowUM = tokenRowUM,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
reorderableState = null,
|
||||
modifier = itemModifier,
|
||||
modifier = itemModifier
|
||||
.onGloballyPositioned {
|
||||
position = it.positionInWindow()
|
||||
}
|
||||
.combinedClickable(
|
||||
enabled = tokenRowUM.onItemClick != null || tokenRowUM.onItemLongClick != null,
|
||||
onClick = tokenRowUM.onItemClick ?: {},
|
||||
onLongClick = {
|
||||
tokenRowUM.onItemLongClick?.invoke(position, tokenRowUM)
|
||||
},
|
||||
),
|
||||
)
|
||||
is TangemHeaderRowUM -> TangemHeaderRow(
|
||||
headerRowUM = tokenRowUM,
|
||||
|
|
@ -343,7 +367,8 @@ internal fun PortfolioRowItem(
|
|||
val composables = remember {
|
||||
SharedTokenRowComposables(
|
||||
icon = { modifier ->
|
||||
val size = if (isExpandedWrapped) AccountIconSize.ExtraSmall else AccountIconSize.Default
|
||||
val size =
|
||||
if (isExpandedWrapped) AccountIconSize.ExtraSmall else AccountIconSize.Default
|
||||
val headIcon = item.tokenRowUM.headIconUM
|
||||
val sizedHeadIcon = if (headIcon is TangemIconUM.Currency) {
|
||||
headIcon.copy(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue