Updated on 2026-08-14

This commit is contained in:
Tangem 2025-02-25 16:48:39 +05:00
commit 777ebd58cb
6 changed files with 105 additions and 74 deletions

View file

@ -18,9 +18,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextOverflow
@ -30,9 +28,9 @@ import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameter
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.R
import com.tangem.core.ui.components.icons.badge.drawBadge
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemColorPalette
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
@ -225,20 +223,6 @@ fun getTextColor(config: ActionButtonConfig): Color {
}
}
private fun DrawScope.drawBadge(containerColor: Color) {
val width = size.width
drawCircle(
color = containerColor,
center = Offset(x = width - 2.dp.toPx(), y = 2.dp.toPx()),
radius = 5.dp.toPx(),
)
drawCircle(
color = TangemColorPalette.Azure,
center = Offset(x = width - 2.dp.toPx(), y = 2.dp.toPx()),
radius = 3.dp.toPx(),
)
}
@Preview(group = "RoundedActionButton", showBackground = true)
@Preview(group = "RoundedActionButton", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable

View file

@ -0,0 +1,22 @@
package com.tangem.core.ui.components.icons.badge
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.res.TangemColorPalette
fun DrawScope.drawBadge(containerColor: Color, offset: Dp = 2.dp) {
val width = size.width
drawCircle(
color = containerColor,
center = Offset(x = width - offset.toPx(), y = offset.toPx()),
radius = 5.dp.toPx(),
)
drawCircle(
color = TangemColorPalette.Azure,
center = Offset(x = width - offset.toPx(), y = offset.toPx()),
radius = 3.dp.toPx(),
)
}

View file

@ -48,33 +48,14 @@ internal class PortfolioTokenUMConverter(
private fun quickActions(cryptoData: PortfolioData.CryptoCurrencyData): PortfolioTokenUM.QuickActions {
return PortfolioTokenUM.QuickActions(
actions = listOfNotNull(
QuickActionUM.Buy.takeIf {
cryptoData.actions.any {
it is TokenActionsState.ActionState.Buy &&
it.unavailabilityReason == ScenarioUnavailabilityReason.None
}
},
QuickActionUM.Exchange.takeIf {
cryptoData.actions.any {
it is TokenActionsState.ActionState.Swap &&
it.unavailabilityReason == ScenarioUnavailabilityReason.None
}
},
QuickActionUM.Receive.takeIf {
cryptoData.actions.any {
it is TokenActionsState.ActionState.Receive &&
it.unavailabilityReason == ScenarioUnavailabilityReason.None
}
},
).toImmutableList(),
actions = toQuickActions(cryptoData.actions),
onQuickActionClick = {
when (it) {
QuickActionUM.Buy -> tokenActionsHandler.handle(
action = TokenActionsBSContentUM.Action.Buy,
cryptoCurrencyData = cryptoData,
)
QuickActionUM.Exchange -> tokenActionsHandler.handle(
is QuickActionUM.Exchange -> tokenActionsHandler.handle(
action = TokenActionsBSContentUM.Action.Exchange,
cryptoCurrencyData = cryptoData,
)
@ -94,4 +75,17 @@ internal class PortfolioTokenUMConverter(
},
)
}
private fun toQuickActions(actions: List<TokenActionsState.ActionState>) = buildList {
actions.forEach { action ->
if (action.unavailabilityReason == ScenarioUnavailabilityReason.None) {
when (action) {
is TokenActionsState.ActionState.Buy -> QuickActionUM.Buy
is TokenActionsState.ActionState.Swap -> QuickActionUM.Exchange(showBadge = action.showBadge)
is TokenActionsState.ActionState.Receive -> QuickActionUM.Receive
else -> null
}?.let(::add)
}
}
}.toImmutableList()
}

View file

@ -4,7 +4,10 @@ import android.content.res.Configuration
import androidx.compose.animation.*
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.foundation.*
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Button
@ -13,6 +16,7 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalLayoutDirection
@ -22,6 +26,7 @@ import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import com.tangem.core.ui.components.SpacerH4
import com.tangem.core.ui.components.icons.badge.drawBadge
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.haptic.TangemHapticEffect
import com.tangem.core.ui.res.LocalHapticManager
@ -29,7 +34,7 @@ import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.markets.portfolio.impl.ui.state.QuickActionUM
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.persistentListOf
@Composable
internal fun PortfolioQuickActions(
@ -59,7 +64,6 @@ internal fun PortfolioQuickActions(
}
}
@OptIn(ExperimentalAnimationApi::class)
@Composable
private fun AnimatedVisibilityScope.LineSeparator(modifier: Modifier = Modifier) {
val lineColor = TangemTheme.colors.stroke.primary
@ -99,7 +103,7 @@ private fun AnimatedVisibilityScope.LineSeparator(modifier: Modifier = Modifier)
}
}
@OptIn(ExperimentalAnimationApi::class, ExperimentalFoundationApi::class)
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun AnimatedVisibilityScope.QuickActionItem(
state: QuickActionUM,
@ -131,27 +135,7 @@ private fun AnimatedVisibilityScope.QuickActionItem(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing18),
) {
Box(
Modifier
.animateEnterExit(
enter = scaleIn(),
exit = scaleOut(),
)
.background(
color = TangemTheme.colors.button.secondary,
shape = CircleShape,
)
.size(TangemTheme.dimens.size32),
contentAlignment = Alignment.Center,
) {
Icon(
modifier = Modifier
.requiredSize(TangemTheme.dimens.size16),
imageVector = ImageVector.vectorResource(id = state.icon),
contentDescription = null,
tint = TangemTheme.colors.button.primary,
)
}
QuickActionIcon(state)
Column(
modifier = Modifier
.animateEnterExit(
@ -174,6 +158,38 @@ private fun AnimatedVisibilityScope.QuickActionItem(
}
}
@Composable
private fun AnimatedVisibilityScope.QuickActionIcon(state: QuickActionUM) {
val containerColor = TangemTheme.colors.background.action
Box(
Modifier
.animateEnterExit(
enter = scaleIn(),
exit = scaleOut(),
)
.background(
color = TangemTheme.colors.button.secondary,
shape = CircleShape,
)
.size(TangemTheme.dimens.size32)
.drawWithContent {
drawContent()
if (state is QuickActionUM.Exchange && state.showBadge) {
drawBadge(containerColor = containerColor, offset = 4.dp)
}
},
contentAlignment = Alignment.Center,
) {
Icon(
modifier = Modifier
.requiredSize(TangemTheme.dimens.size16),
imageVector = ImageVector.vectorResource(id = state.icon),
contentDescription = null,
tint = TangemTheme.colors.button.primary,
)
}
}
@Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
@ -197,7 +213,11 @@ private fun Preview() {
modifier = Modifier.background(color = TangemTheme.colors.background.action),
) {
PortfolioQuickActions(
actions = QuickActionUM.entries.toImmutableList(),
actions = persistentListOf(
QuickActionUM.Buy,
QuickActionUM.Exchange(showBadge = true),
QuickActionUM.Receive,
),
isVisible = isVisible,
onActionClick = {},
onActionLongClick = {},
@ -214,7 +234,11 @@ private fun PreviewRtl() {
TangemThemePreview(rtl = true) {
Box(modifier = Modifier.background(color = TangemTheme.colors.background.action)) {
PortfolioQuickActions(
actions = QuickActionUM.entries.toImmutableList(),
actions = persistentListOf(
QuickActionUM.Buy,
QuickActionUM.Exchange(showBadge = true),
QuickActionUM.Receive,
),
isVisible = true,
onActionClick = {},
onActionLongClick = {},

View file

@ -10,7 +10,6 @@ import com.tangem.features.markets.portfolio.impl.ui.state.MyPortfolioUM
import com.tangem.features.markets.portfolio.impl.ui.state.PortfolioTokenUM
import com.tangem.features.markets.portfolio.impl.ui.state.QuickActionUM
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
internal class PreviewMyPortfolioUMProvider : PreviewParameterProvider<MyPortfolioUM> {
@ -61,7 +60,11 @@ internal class PreviewMyPortfolioUMProvider : PreviewParameterProvider<MyPortfol
),
isQuickActionsShown = false,
quickActions = PortfolioTokenUM.QuickActions(
actions = QuickActionUM.entries.toImmutableList(),
actions = persistentListOf(
QuickActionUM.Buy,
QuickActionUM.Exchange(showBadge = true),
QuickActionUM.Receive,
),
onQuickActionClick = {},
onQuickActionLongClick = {},
),

View file

@ -7,26 +7,30 @@ import com.tangem.core.ui.extensions.resourceReference
import com.tangem.features.markets.impl.R
@Immutable
internal enum class QuickActionUM(
internal sealed class QuickActionUM(
val title: TextReference,
val description: TextReference,
@DrawableRes val icon: Int,
val longClickAvailable: Boolean = false,
) {
Buy(
data object Buy : QuickActionUM(
title = resourceReference(R.string.common_buy),
description = resourceReference(R.string.buy_token_description),
icon = R.drawable.ic_plus_24,
),
Exchange(
)
data class Exchange(
val showBadge: Boolean,
) : QuickActionUM(
title = resourceReference(R.string.common_exchange),
description = resourceReference(R.string.exсhange_token_description),
icon = R.drawable.ic_exchange_vertical_24,
),
Receive(
)
data object Receive : QuickActionUM(
title = resourceReference(R.string.common_receive),
description = resourceReference(R.string.receive_token_description),
icon = R.drawable.ic_arrow_down_24,
longClickAvailable = true,
),
)
}