diff --git a/common/ui/src/main/java/com/tangem/common/ui/notifications/Notifications.kt b/common/ui/src/main/java/com/tangem/common/ui/notifications/Notifications.kt index 68a3f77292..7f8ec96750 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/notifications/Notifications.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/notifications/Notifications.kt @@ -4,8 +4,12 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import com.tangem.core.ui.components.notifications.Notification +import com.tangem.core.ui.components.notifications.NotificationConfig +import com.tangem.core.ui.ds.message.TangemMessage +import com.tangem.core.ui.ds.message.TangemMessageUM import com.tangem.core.ui.res.TangemTheme import kotlinx.collections.immutable.ImmutableList @@ -44,4 +48,66 @@ fun LazyListScope.notifications( ) }, ) +} + +/** + * Displays a list of notifications using TangemMessage composables. + * + * @param notifications List of NotificationConfig objects to be displayed. + * @param contentColor Color to be used for the content of the notifications. + * @param modifier Optional Modifier for the notifications. + * @param hasPaddingAbove Boolean indicating whether to add padding above the first notification. + */ +fun LazyListScope.notifications2( + notifications: ImmutableList, + contentColor: Color, + modifier: Modifier = Modifier, + hasPaddingAbove: Boolean = false, +) { + itemsIndexed( + items = notifications, + key = { index, item -> item.title?.hashCode()?.plus(index) ?: index }, + contentType = { _, item -> item::class.java }, + itemContent = { i, item -> + val topPadding = if (i == 0 && hasPaddingAbove) 0.dp else 12.dp + TangemMessage( + config = item, + contentColor = contentColor, + modifier = modifier + .padding(top = topPadding) + .animateItem(), + ) + }, + ) +} + +/** + * Displays a list of notifications using TangemMessage composables. + * + * @param notifications List of TangemMessageUM objects to be displayed. + * @param contentColor Color to be used for the content of the notifications. + * @param modifier Optional Modifier for the notifications. + * @param hasPaddingAbove Boolean indicating whether to add padding above the first notification. + */ +fun LazyListScope.notifications( + notifications: ImmutableList, + contentColor: Color, + modifier: Modifier = Modifier, + hasPaddingAbove: Boolean = false, +) { + itemsIndexed( + items = notifications, + key = { _, item -> item.id }, + contentType = { _, item -> item::class.java }, + itemContent = { i, item -> + val topPadding = if (i == 0 && hasPaddingAbove) TangemTheme.dimens2.x0 else TangemTheme.dimens2.x2 + TangemMessage( + messageUM = item, + contentColor = contentColor, + modifier = modifier + .padding(top = topPadding) + .animateItem(), + ) + }, + ) } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/badge/TangemBadge.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/badge/TangemBadge.kt index f41322e237..7f9f95b4d3 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ds/badge/TangemBadge.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/badge/TangemBadge.kt @@ -24,6 +24,7 @@ import androidx.compose.ui.unit.dp import com.tangem.core.ui.R import com.tangem.core.ui.ds.badge.TangemBadgeSize.* import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.clickableSingle import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.res.TangemTheme @@ -48,6 +49,7 @@ fun TangemBadge(badgeUM: TangemBadgeUM, modifier: Modifier = Modifier) { color = badgeUM.color, type = badgeUM.type, iconPosition = badgeUM.iconPosition, + onClick = badgeUM.onClick, modifier = modifier, ) } @@ -64,6 +66,7 @@ fun TangemBadge(badgeUM: TangemBadgeUM, modifier: Modifier = Modifier) { * @param color [TangemBadgeColor] defining the color scheme of the badge. * @param type [TangemBadgeType] defining the style of the badge. * @param iconPosition [TangemBadgeIconPosition] defining icon position of the badge. + * @param onClick Lambda to be invoked when the badge is clicked (optional). * [REDACTED_AUTHOR] */ @@ -77,6 +80,7 @@ fun TangemBadge( color: TangemBadgeColor = TangemBadgeColor.Gray, type: TangemBadgeType = TangemBadgeType.Solid, iconPosition: TangemBadgeIconPosition = TangemBadgeIconPosition.Start, + onClick: (() -> Unit)? = null, ) { val iconColor = getIconColor(type = type, color = color) Row( @@ -86,7 +90,8 @@ fun TangemBadge( .heightIn(min = size.toHeightDp()) .clip(shape.toShape(size)) .getBackgroundColor(type = type, color = color, shape = shape.toShape(size)) - .padding(size.toPaddingDp(position = iconPosition)), + .padding(size.toPaddingDp(position = iconPosition)) + .clickableSingle(enabled = onClick != null, onClick = { onClick?.invoke() }), ) { AnimatedVisibility( visible = iconRes != null && iconPosition == TangemBadgeIconPosition.Start, diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/badge/TangemBadgeUM.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/badge/TangemBadgeUM.kt index e28cbdd6ba..5f666898bb 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ds/badge/TangemBadgeUM.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/badge/TangemBadgeUM.kt @@ -8,13 +8,13 @@ import com.tangem.core.ui.extensions.TextReference * UI model for [TangemBadge] component * * @param text TextReference for the badge label. - * @param modifier Modifier to be applied to the badge. * @param iconRes Drawable resource ID for the icon to be displayed in the badge. * @param size [TangemBadgeSize] defining the size of the badge. * @param shape [TangemBadgeShape] defining the shape of the badge. * @param color [TangemBadgeColor] defining the color scheme of the badge. * @param type [TangemBadgeType] defining the style of the badge. * @param iconPosition [TangemBadgeIconPosition] defining icon position of the badge. + * @param onClick Lambda to be invoked when the badge is clicked (optional). */ class TangemBadgeUM( val text: TextReference, @@ -24,4 +24,5 @@ class TangemBadgeUM( val color: TangemBadgeColor = TangemBadgeColor.Gray, val type: TangemBadgeType = TangemBadgeType.Solid, val iconPosition: TangemBadgeIconPosition = TangemBadgeIconPosition.Start, + val onClick: (() -> Unit)? = null, ) \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/image/TangemIconUM.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/image/TangemIconUM.kt new file mode 100644 index 0000000000..ff7be95be2 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/image/TangemIconUM.kt @@ -0,0 +1,76 @@ +package com.tangem.core.ui.ds.image + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.Image +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource +import com.tangem.core.ui.components.currency.icon.CurrencyIcon +import com.tangem.core.ui.components.currency.icon.CurrencyIconState +import com.tangem.core.ui.components.icons.identicon.IdentIcon +import com.tangem.core.ui.extensions.ColorReference2 +import com.tangem.core.ui.res.TangemTheme + +/** + * Model representing different types of icons that can be displayed in the UI. + */ +@Immutable +sealed interface TangemIconUM { + + /** Icon representing a currency. */ + data class Currency( + val currencyIconState: CurrencyIconState, + ) : TangemIconUM + + /** Icon represented by a drawable resource. */ + data class Icon( + @DrawableRes val iconRes: Int, + val tintReference: ColorReference2 = ColorReference2 { TangemTheme.colors2.graphic.neutral.primary }, + ) : TangemIconUM + + /** Image represented by a drawable resource. */ + data class Image( + @DrawableRes val imageRes: Int, + ) : TangemIconUM + + /** Identicon represented by a text string (e.g., an address). */ + data class Ident( + val text: String, + ) : TangemIconUM +} + +/** + * Composable function to display an icon based on the provided [TangemIconUM] type. + * + * @param tangemIconUM The [TangemIconUM] instance representing the icon to be displayed. + * @param modifier The [Modifier] to be applied to the icon. + */ +@Composable +fun TangemIcon(tangemIconUM: TangemIconUM, modifier: Modifier = Modifier) { + when (tangemIconUM) { + is TangemIconUM.Currency -> { + CurrencyIcon( + state = tangemIconUM.currencyIconState, + modifier = modifier, + ) + } + is TangemIconUM.Icon -> Icon( + imageVector = ImageVector.vectorResource(tangemIconUM.iconRes), + contentDescription = null, + modifier = modifier, + tint = tangemIconUM.tintReference(), + ) + is TangemIconUM.Image -> Image( + imageVector = ImageVector.vectorResource(tangemIconUM.imageRes), + contentDescription = null, + modifier = modifier, + ) + is TangemIconUM.Ident -> IdentIcon( + address = tangemIconUM.text, + modifier = modifier, + ) + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/message/TangemMessage.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/message/TangemMessage.kt index ff63624792..3591920330 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ds/message/TangemMessage.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/message/TangemMessage.kt @@ -11,9 +11,11 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider @@ -24,6 +26,7 @@ import com.tangem.core.ui.components.flicker import com.tangem.core.ui.components.notifications.NotificationConfig import com.tangem.core.ui.components.notifications.NotificationConfig.ButtonsState import com.tangem.core.ui.ds.button.* +import com.tangem.core.ui.ds.image.TangemIcon import com.tangem.core.ui.extensions.* import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreviewRedesign @@ -35,21 +38,31 @@ import kotlinx.collections.immutable.persistentListOf * * @param messageUM Data model containing message properties. * @param modifier Modifier to be applied to the message component. - * @param content Optional composable content to be displayed alongside the title and subtitle. */ @Composable fun TangemMessage( messageUM: TangemMessageUM, modifier: Modifier = Modifier, - content: @Composable (RowScope.() -> Unit)? = null, + contentColor: Color = TangemTheme.colors2.surface.level3, ) { TangemMessage( - modifier = modifier, + modifier = modifier + .conditional(messageUM.onClick != null) { + clickableSingle(onClick = requireNotNull(messageUM.onClick)) + }, title = messageUM.title, subtitle = messageUM.subtitle, messageEffect = messageUM.messageEffect, isCentered = messageUM.isCentered, - content = content, + content = { + if (messageUM.iconUM != null) { + TangemIcon( + tangemIconUM = messageUM.iconUM, + modifier = Modifier.size(TangemTheme.dimens2.x8), + ) + } + }, + contentColor = contentColor, onCloseClick = messageUM.onCloseClick, buttons = { messageUM.buttonsUM.fastForEach { buttonUM -> @@ -73,7 +86,11 @@ fun TangemMessage( * @see com.tangem.core.ui.components.notifications.Notification for legacy component. */ @Composable -fun TangemMessage(config: NotificationConfig, modifier: Modifier = Modifier) { +fun TangemMessage( + config: NotificationConfig, + modifier: Modifier = Modifier, + contentColor: Color = TangemTheme.colors2.surface.level3, +) { val buttonState = config.buttonsState TangemMessage( title = config.title, @@ -101,6 +118,7 @@ fun TangemMessage(config: NotificationConfig, modifier: Modifier = Modifier) { ) } }, + contentColor = contentColor, buttons = if (buttonState != null) { { TangemMessageLegacyButtons(buttonState = buttonState) @@ -129,10 +147,11 @@ fun TangemMessage( title: TextReference? = null, subtitle: TextReference? = null, messageEffect: TangemMessageEffect = TangemMessageEffect.None, - content: (@Composable RowScope.() -> Unit)? = null, - buttons: (@Composable RowScope.() -> Unit)? = null, onCloseClick: (() -> Unit)? = null, isCentered: Boolean = false, + contentColor: Color = TangemTheme.colors2.surface.level3, + content: (@Composable RowScope.() -> Unit)? = null, + buttons: (@Composable RowScope.() -> Unit)? = null, ) { val alignment = if (isCentered) { Alignment.CenterHorizontally @@ -146,6 +165,7 @@ fun TangemMessage( .messageEffectBackground( messageEffect = messageEffect, radius = TangemTheme.dimens2.x6, + contentColor = contentColor, ), ) Column( @@ -160,8 +180,9 @@ fun TangemMessage( subtitle = subtitle, alignment = alignment, content = content, + isCentered = isCentered, ) - if (buttons != null && !isCentered) { + if (buttons != null) { Row( horizontalArrangement = Arrangement.spacedBy(6.dp), modifier = Modifier @@ -191,8 +212,14 @@ private fun TangemMessageContent( title: TextReference? = null, subtitle: TextReference? = null, alignment: Alignment.Horizontal = Alignment.Start, + isCentered: Boolean = false, content: (@Composable RowScope.() -> Unit)? = null, ) { + val textAlign = if (isCentered) { + TextAlign.Center + } else { + TextAlign.Start + } Row( horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2), modifier = Modifier.padding(TangemTheme.dimens2.x1), @@ -208,10 +235,12 @@ private fun TangemMessageContent( style = TangemTheme.typography2.bodySemibold16, color = TangemTheme.colors2.text.neutral.primary, maxLines = 1, + textAlign = textAlign, ) } if (subtitle != null) { Text( + textAlign = textAlign, text = subtitle.resolveAnnotatedReference(), style = TangemTheme.typography2.captionSemibold12, color = TangemTheme.colors2.text.neutral.secondary, @@ -310,12 +339,14 @@ private class TangemMessagePreviewProvider : PreviewParameterProvider get() = sequenceOf( TangemMessageUM( + id = "1", title = stringReference("Title text"), subtitle = stringReference("Subtext"), messageEffect = TangemMessageEffect.None, isCentered = true, ), TangemMessageUM( + id = "2", title = stringReference("Title text"), subtitle = stringReference("Subtext"), messageEffect = TangemMessageEffect.Magic, @@ -335,6 +366,7 @@ private class TangemMessagePreviewProvider : PreviewParameterProvider = persistentListOf(), + val onClick: (() -> Unit)? = null, val onCloseClick: (() -> Unit)? = null, ) diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/row/TangemRowUM.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/row/TangemRowUM.kt new file mode 100644 index 0000000000..d605343ac6 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/row/TangemRowUM.kt @@ -0,0 +1,12 @@ +package com.tangem.core.ui.ds.row + +import androidx.compose.runtime.Immutable + +/** + * Base interface for all row UI models in the Tangem application. Each row UI model must implement this interface + */ +@Immutable +interface TangemRowUM { + + val id: String +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/row/header/TangemHeaderRow.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/row/header/TangemHeaderRow.kt new file mode 100644 index 0000000000..f5af27f6c1 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/row/header/TangemHeaderRow.kt @@ -0,0 +1,230 @@ +package com.tangem.core.ui.ds.row.header + +import android.content.res.Configuration +import androidx.annotation.DrawableRes +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import com.tangem.core.ui.R +import com.tangem.core.ui.components.SpacerWMax +import com.tangem.core.ui.components.currency.icon.CurrencyIconState +import com.tangem.core.ui.ds.image.TangemIcon +import com.tangem.core.ui.ds.image.TangemIconUM +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.clickableSingle +import com.tangem.core.ui.extensions.resolveAnnotatedReference +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreviewRedesign +import com.tangem.core.ui.test.TokenElementsTestTags + +/** + * UI model for header row component + * + * @param headerRowUM UI model for the header row + * @param modifier Modifier for the composable + */ +@Composable +fun TangemHeaderRow(headerRowUM: TangemHeaderRowUM, modifier: Modifier = Modifier) { + TangemHeaderRow( + headTangemIconUM = headerRowUM.startIconUM, + footerTangemIconRes = headerRowUM.endIconRes, + title = headerRowUM.title, + subtitle = headerRowUM.subtitle, + modifier = modifier, + ) +} + +/** + * Composable function that represents a header row with customizable title and head content. + * + * @param modifier Modifier for the composable + * @param subtitle Optional subtitle as a TextReference + * @param onItemClick Optional click callback for the row + * @param footerTangemIconRes Optional drawable resource ID for the footer icon + * @param titleContent Composable lambda for the title content + * @param headContent Composable lambda for the head content + */ +@Composable +fun TangemHeaderRow( + modifier: Modifier = Modifier, + subtitle: TextReference? = null, + onItemClick: (() -> Unit)? = null, + @DrawableRes footerTangemIconRes: Int? = null, + titleContent: @Composable (Modifier) -> Unit, + headContent: @Composable (Modifier) -> Unit, +) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = modifier + .fillMaxWidth() + .clickableSingle(enabled = onItemClick != null, onClick = { onItemClick?.invoke() }) + .padding( + top = TangemTheme.dimens2.x4, + bottom = TangemTheme.dimens2.x3, + start = TangemTheme.dimens2.x4, + end = TangemTheme.dimens2.x4, + ), + ) { + headContent( + Modifier + .padding(end = TangemTheme.dimens2.x2) + .size(TangemTheme.dimens2.x4), + ) + titleContent(Modifier) + AnimatedVisibility( + visible = subtitle != null, + ) { + val wrappedSubtitle = remember(this) { requireNotNull(subtitle) } + Text( + text = wrappedSubtitle.resolveAnnotatedReference(), + style = TangemTheme.typography2.captionSemibold12, + color = TangemTheme.colors2.text.neutral.secondary, + maxLines = 1, + modifier = Modifier + .testTag(tag = TokenElementsTestTags.TOKEN_CRYPTO_AMOUNT) + .padding(start = TangemTheme.dimens2.x1), + ) + } + SpacerWMax() + AnimatedVisibility( + visible = footerTangemIconRes != null, + ) { + val wrappedIconUM = remember(this) { requireNotNull(footerTangemIconRes) } + Icon( + imageVector = ImageVector.vectorResource(id = wrappedIconUM), + contentDescription = null, + tint = TangemTheme.colors2.graphic.neutral.secondary, + modifier = Modifier.size(TangemTheme.dimens2.x4), + ) + } + } +} + +/** + * Composable function that represents a header row with title, optional subtitle, and optional icons. + * + * @param title Title as a TextReference + * @param modifier Modifier for the composable + * @param subtitle Optional subtitle as a TextReference + * @param headTangemIconUM Optional TangemIconUM for the head icon + * @param footerTangemIconRes Optional drawable resource ID for the footer icon + * @param isEnabled Boolean indicating if the row is clickable + * @param onItemClick Optional click callback for the row + */ +@Composable +fun TangemHeaderRow( + title: TextReference, + modifier: Modifier = Modifier, + subtitle: TextReference? = null, + headTangemIconUM: TangemIconUM? = null, + @DrawableRes footerTangemIconRes: Int? = null, + isEnabled: Boolean = false, + onItemClick: (() -> Unit)? = null, +) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = modifier + .fillMaxWidth() + .clickableSingle(enabled = isEnabled && onItemClick != null, onClick = { onItemClick?.invoke() }) + .padding( + top = TangemTheme.dimens2.x4, + bottom = TangemTheme.dimens2.x3, + start = TangemTheme.dimens2.x4, + end = TangemTheme.dimens2.x4, + ), + ) { + AnimatedVisibility( + visible = headTangemIconUM != null, + ) { + val wrappedIconUM = remember(this) { requireNotNull(headTangemIconUM) } + TangemIcon( + tangemIconUM = wrappedIconUM, + modifier = Modifier + .padding(end = TangemTheme.dimens2.x2) + .size(TangemTheme.dimens2.x4), + ) + } + Text( + text = title.resolveAnnotatedReference(), + style = TangemTheme.typography2.captionSemibold12, + color = TangemTheme.colors2.text.neutral.primary, + maxLines = 1, + modifier = Modifier.testTag(tag = TokenElementsTestTags.TOKEN_TITLE), + ) + AnimatedVisibility( + visible = subtitle != null, + ) { + val wrappedSubtitle = remember(this) { requireNotNull(subtitle) } + Text( + text = wrappedSubtitle.resolveAnnotatedReference(), + style = TangemTheme.typography2.captionSemibold12, + color = TangemTheme.colors2.text.neutral.secondary, + maxLines = 1, + modifier = Modifier + .testTag(tag = TokenElementsTestTags.TOKEN_CRYPTO_AMOUNT) + .padding(start = TangemTheme.dimens2.x1), + ) + } + SpacerWMax() + AnimatedVisibility( + visible = footerTangemIconRes != null, + ) { + val wrappedIconUM = remember(this) { requireNotNull(footerTangemIconRes) } + Icon( + imageVector = ImageVector.vectorResource(id = wrappedIconUM), + contentDescription = null, + tint = TangemTheme.colors2.graphic.neutral.secondary, + modifier = Modifier.size(TangemTheme.dimens2.x4), + ) + } + } +} + +// region Preview +@Composable +@Preview(showBackground = true, widthDp = 360) +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +private fun TangemHeaderRow_Preview(@PreviewParameter(PreviewProvider::class) params: TangemHeaderRowUM) { + TangemThemePreviewRedesign { + TangemHeaderRow( + headerRowUM = params, + modifier = Modifier.background(TangemTheme.colors2.surface.level3), + ) + } +} + +private class PreviewProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + TangemHeaderRowUM( + id = "1", + startIconUM = TangemIconUM.Currency( + currencyIconState = CurrencyIconState.Locked, + ), + endIconRes = R.drawable.ic_minimize_24, + title = stringReference("Account"), + subtitle = stringReference("\$ 42,900.17"), + ), + TangemHeaderRowUM( + id = "2", + title = stringReference("Account"), + ), + ) +} +// endregion \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/row/header/TangemHeaderRowUM.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/row/header/TangemHeaderRowUM.kt new file mode 100644 index 0000000000..fd69abb660 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/row/header/TangemHeaderRowUM.kt @@ -0,0 +1,29 @@ +package com.tangem.core.ui.ds.row.header + +import androidx.annotation.DrawableRes +import androidx.compose.runtime.Immutable +import com.tangem.core.ui.ds.image.TangemIconUM +import com.tangem.core.ui.ds.row.TangemRowUM +import com.tangem.core.ui.extensions.TextReference + +/** + * UI model for header row component + * + * @param id Unique id + * @param title Title text reference + * @param subtitle Subtitle text reference (optional) + * @param startIconUM Icon UI model (optional) + * @param endIconRes Icon UI model (optional) + * @param isEnabled Flag indicating if click is enabled + * @param onItemClick Callback for item click (optional) + */ +@Immutable +data class TangemHeaderRowUM( + override val id: String, + val title: TextReference, + val subtitle: TextReference? = null, + val startIconUM: TangemIconUM? = null, + @DrawableRes val endIconRes: Int? = null, + val isEnabled: Boolean = false, + val onItemClick: (() -> Unit)? = null, +) : TangemRowUM \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/TangemTokenRow.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/TangemTokenRow.kt index 8f9c251ea1..a0b50fe199 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/TangemTokenRow.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/TangemTokenRow.kt @@ -16,7 +16,7 @@ import androidx.compose.ui.platform.testTag 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.currency.icon.CurrencyIcon +import com.tangem.core.ui.ds.image.TangemIcon import com.tangem.core.ui.ds.row.TangemRowContainer import com.tangem.core.ui.ds.row.TangemRowLayoutId import com.tangem.core.ui.ds.row.token.internal.* @@ -44,19 +44,19 @@ fun TangemTokenRow( ) { TangemRowContainer( content = { - CurrencyIcon( - state = tokenRowUM.iconState, + TangemIcon( + tangemIconUM = tokenRowUM.headIconUM, modifier = Modifier .layoutId(layoutId = TangemRowLayoutId.HEAD) .padding(end = TangemTheme.dimens2.x2) - .testTag(TokenElementsTestTags.TOKEN_ICON), + .testTag(tag = TokenElementsTestTags.TOKEN_ICON), ) TokenRowPromoBanner( promoBannerUM = tokenRowUM.promoBannerUM, modifier = Modifier .layoutId(layoutId = TangemRowLayoutId.EXTRA_TOP) - .testTag(TokenElementsTestTags.TOKEN_YIELD_PROMO_BANNER) + .testTag(tag = TokenElementsTestTags.TOKEN_YIELD_PROMO_BANNER) .padding(horizontal = TangemTheme.dimens2.x3) .fillMaxWidth(), ) @@ -98,7 +98,89 @@ fun TangemTokenRow( reorderableTokenListState = reorderableTokenListState, modifier = Modifier .layoutId(layoutId = TangemRowLayoutId.TAIL) - .testTag(TokenElementsTestTags.TOKEN_NON_FIAT_BLOCK), + .testTag(tag = TokenElementsTestTags.TOKEN_NON_FIAT_BLOCK), + ) + }, + modifier = modifier.tokenClickable(tokenRowUM = tokenRowUM), + ) +} + +/** + * Composable function that represents a Tangem token row in a list. + * + * [Token Row](https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=8207-17583&t=k8dyaykorsNocGVq-4) + * + * @param tokenRowUM The user model containing the data for the token row. + * @param headComponent The composable function representing the head component. + * @param titleComponent The composable function representing the title component. + * @param isBalanceHidden A boolean indicating whether the balance should be hidden. + * @param reorderableTokenListState The state of the reorderable lazy list, if applicable. + * @param modifier The modifier to be applied to the row. + */ +@Composable +fun TangemTokenRow( + tokenRowUM: TangemTokenRowUM, + isBalanceHidden: Boolean, + reorderableTokenListState: ReorderableLazyListState?, + modifier: Modifier = Modifier, + headComponent: @Composable (Modifier) -> Unit, + titleComponent: @Composable (Modifier) -> Unit, +) { + TangemRowContainer( + content = { + headComponent( + Modifier + .layoutId(layoutId = TangemRowLayoutId.HEAD) + .padding(end = TangemTheme.dimens2.x2) + .testTag(tag = TokenElementsTestTags.TOKEN_ICON), + ) + + TokenRowPromoBanner( + promoBannerUM = tokenRowUM.promoBannerUM, + modifier = Modifier + .layoutId(layoutId = TangemRowLayoutId.EXTRA_TOP) + .testTag(tag = TokenElementsTestTags.TOKEN_YIELD_PROMO_BANNER) + .padding(horizontal = TangemTheme.dimens2.x3) + .fillMaxWidth(), + ) + + titleComponent( + Modifier + .layoutId(layoutId = TangemRowLayoutId.START_TOP) + .padding(end = TangemTheme.dimens2.x2) + .testTag(tag = TokenElementsTestTags.TOKEN_TITLE), + ) + + TokenRowSubtitle( + subtitleUM = tokenRowUM.subtitleUM, + modifier = Modifier + .layoutId(layoutId = TangemRowLayoutId.START_BOTTOM) + .padding(end = TangemTheme.dimens2.x2) + .testTag(tag = TokenElementsTestTags.TOKEN_PRICE), + ) + + TokenRowEndTopContent( + endContentUM = tokenRowUM.topEndContentUM, + isBalanceHidden = isBalanceHidden, + modifier = Modifier + .layoutId(layoutId = TangemRowLayoutId.END_TOP) + .testTag(tag = TokenElementsTestTags.TOKEN_FIAT_AMOUNT), + ) + + TokenRowEndBottomContent( + endContentUM = tokenRowUM.bottomEndContentUM, + isBalanceHidden = isBalanceHidden, + modifier = Modifier + .layoutId(layoutId = TangemRowLayoutId.END_BOTTOM) + .testTag(tag = TokenElementsTestTags.TOKEN_CRYPTO_AMOUNT), + ) + + TokenRowTail( + tailUM = tokenRowUM.tailUM, + reorderableTokenListState = reorderableTokenListState, + modifier = Modifier + .layoutId(layoutId = TangemRowLayoutId.TAIL) + .testTag(tag = TokenElementsTestTags.TOKEN_NON_FIAT_BLOCK), ) }, modifier = modifier.tokenClickable(tokenRowUM = tokenRowUM), @@ -114,7 +196,7 @@ private fun Modifier.tokenClickable(tokenRowUM: TangemTokenRowUM): Modifier = co val onHapticLongClick = if (onLongClick != null) { { hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) - onLongClick(tokenRowUM) + onLongClick() } } else { null @@ -123,9 +205,9 @@ private fun Modifier.tokenClickable(tokenRowUM: TangemTokenRowUM): Modifier = co when { onClick == null && onLongClick == null -> this onClick == null && onLongClick != null -> combinedClickable(onClick = {}, onLongClick = onHapticLongClick) - onClick != null && onLongClick == null -> combinedClickable(onClick = { onClick(tokenRowUM) }) + onClick != null && onLongClick == null -> combinedClickable(onClick = onClick) onClick != null && onLongClick != null -> { - combinedClickable(onClick = { onClick(tokenRowUM) }, onLongClick = onHapticLongClick) + combinedClickable(onClick = onClick, onLongClick = onHapticLongClick) } else -> this } diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/TangemTokenRowUM.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/TangemTokenRowUM.kt index 8ef54a567b..98027ff5dd 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/TangemTokenRowUM.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/TangemTokenRowUM.kt @@ -4,20 +4,20 @@ import androidx.compose.runtime.Immutable import com.tangem.core.ui.components.currency.icon.CurrencyIconState import com.tangem.core.ui.components.marketprice.PriceChangeState import com.tangem.core.ui.ds.badge.TangemBadgeUM -import com.tangem.core.ui.extensions.ColorReference2 +import com.tangem.core.ui.ds.image.TangemIconUM +import com.tangem.core.ui.ds.row.TangemRowUM import com.tangem.core.ui.extensions.TextReference -import com.tangem.core.ui.res.TangemTheme import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @Immutable -sealed class TangemTokenRowUM { +sealed class TangemTokenRowUM : TangemRowUM { /** Unique id */ - abstract val id: String + abstract override val id: String /** Token icon state */ - abstract val iconState: CurrencyIconState + abstract val headIconUM: TangemIconUM.Currency /** Token title UM (in one row with [topEndContentUM]) */ abstract val titleUM: TitleUM @@ -38,25 +38,25 @@ sealed class TangemTokenRowUM { abstract val promoBannerUM: PromoBannerUM /** Callback which will be called when an item is clicked */ - abstract val onItemClick: ((TangemTokenRowUM) -> Unit)? + abstract val onItemClick: (() -> Unit)? /** Callback which will be called when an item is long clicked */ - abstract val onItemLongClick: ((TangemTokenRowUM) -> Unit)? + abstract val onItemLongClick: (() -> Unit)? /** * Content state of [TangemTokenRowUM] */ data class Content( override val id: String, - override val iconState: CurrencyIconState, + override val headIconUM: TangemIconUM.Currency, override val titleUM: TitleUM, override val subtitleUM: SubtitleUM, override val topEndContentUM: EndContentUM, override val bottomEndContentUM: EndContentUM, override val promoBannerUM: PromoBannerUM = PromoBannerUM.Empty, override val tailUM: TailUM = TailUM.Empty, - override val onItemClick: ((TangemTokenRowUM) -> Unit)?, - override val onItemLongClick: ((TangemTokenRowUM) -> Unit)?, + override val onItemClick: (() -> Unit)?, + override val onItemLongClick: (() -> Unit)?, ) : TangemTokenRowUM() /** @@ -64,7 +64,7 @@ sealed class TangemTokenRowUM { */ data class Loading( override val id: String, - override val iconState: CurrencyIconState, + override val headIconUM: TangemIconUM.Currency = TangemIconUM.Currency(CurrencyIconState.Loading), override val titleUM: TitleUM = TitleUM.Loading, override val subtitleUM: SubtitleUM = SubtitleUM.Loading, ) : TangemTokenRowUM() { @@ -72,8 +72,8 @@ sealed class TangemTokenRowUM { override val bottomEndContentUM: EndContentUM = EndContentUM.Loading override val promoBannerUM: PromoBannerUM = PromoBannerUM.Empty override val tailUM: TailUM = TailUM.Empty - override val onItemClick: ((TangemTokenRowUM) -> Unit)? = null - override val onItemLongClick: ((TangemTokenRowUM) -> Unit)? = null + override val onItemClick: (() -> Unit)? = null + override val onItemLongClick: (() -> Unit)? = null } /** @@ -81,12 +81,12 @@ sealed class TangemTokenRowUM { */ data class Actionable( override val id: String, - override val iconState: CurrencyIconState, + override val headIconUM: TangemIconUM.Currency, override val titleUM: TitleUM, override val subtitleUM: SubtitleUM, override val tailUM: TailUM, - override val onItemClick: ((TangemTokenRowUM) -> Unit)?, - override val onItemLongClick: ((TangemTokenRowUM) -> Unit)?, + override val onItemClick: (() -> Unit)?, + override val onItemLongClick: (() -> Unit)?, override val topEndContentUM: EndContentUM = EndContentUM.Empty, override val bottomEndContentUM: EndContentUM = EndContentUM.Empty, ) : TangemTokenRowUM() { @@ -116,7 +116,7 @@ sealed class TangemTokenRowUM { val text: TextReference, val isAvailable: Boolean = true, val isFlickering: Boolean = false, - val icons: ImmutableList = persistentListOf(), + val icons: ImmutableList = persistentListOf(), val priceChangeUM: PriceChangeState = PriceChangeState.Unknown, val badge: TangemBadgeUM? = null, ) : SubtitleUM() @@ -133,7 +133,7 @@ sealed class TangemTokenRowUM { val text: TextReference, val isAvailable: Boolean = true, val isFlickering: Boolean = false, - val icons: ImmutableList = persistentListOf(), + val icons: ImmutableList = persistentListOf(), val priceChangeUM: PriceChangeState = PriceChangeState.Unknown, ) : EndContentUM() @@ -164,9 +164,4 @@ sealed class TangemTokenRowUM { data object Empty : TailUM() } - - data class IconUM( - val iconRes: Int, - val tintReference: ColorReference2 = ColorReference2 { TangemTheme.colors2.graphic.neutral.primary }, - ) } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/internal/TangemTokenRowPreviewData.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/internal/TangemTokenRowPreviewData.kt index aee2125611..ce68a8771d 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/internal/TangemTokenRowPreviewData.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/internal/TangemTokenRowPreviewData.kt @@ -6,6 +6,7 @@ import com.tangem.core.ui.R import com.tangem.core.ui.components.currency.icon.CurrencyIconState import com.tangem.core.ui.components.marketprice.PriceChangeState import com.tangem.core.ui.components.marketprice.PriceChangeType +import com.tangem.core.ui.ds.image.TangemIconUM import com.tangem.core.ui.ds.row.token.TangemTokenRowUM import com.tangem.core.ui.extensions.combinedReference import com.tangem.core.ui.extensions.stringReference @@ -63,49 +64,60 @@ internal object TangemTokenRowPreviewData { text = stringReference("Title"), ) - private val accountResIcon: CurrencyIconState.CryptoPortfolio.Icon - get() = CurrencyIconState.CryptoPortfolio.Icon( - resId = R.drawable.ic_rounded_star_24, - color = Color.Blue, - isGrayscale = false, - ) - private val accountLetterIcon: CurrencyIconState.CryptoPortfolio.Letter - get() = CurrencyIconState.CryptoPortfolio.Letter( - char = stringReference("A"), - color = Color.Blue, - isGrayscale = false, + private val accountResIcon: TangemIconUM.Currency + get() = TangemIconUM.Currency( + CurrencyIconState.CryptoPortfolio.Icon( + resId = R.drawable.ic_rounded_star_24, + color = Color.Blue, + isGrayscale = false, + ), ) - private val coinIconState - get() = CurrencyIconState.CoinIcon( - url = null, - fallbackResId = R.drawable.img_polygon_22, - isGrayscale = false, - shouldShowCustomBadge = false, + private val accountLetterIcon: TangemIconUM.Currency + get() = TangemIconUM.Currency( + CurrencyIconState.CryptoPortfolio.Letter( + char = stringReference("A"), + color = Color.Blue, + isGrayscale = false, + ), ) - private val tokenIconState - get() = CurrencyIconState.TokenIcon( - url = null, - topBadgeIconResId = R.drawable.img_polygon_22, - fallbackTint = TangemColorPalette.Black, - fallbackBackground = TangemColorPalette.Meadow, - isGrayscale = false, - shouldShowCustomBadge = false, + private val coinIconState: TangemIconUM.Currency + get() = TangemIconUM.Currency( + CurrencyIconState.CoinIcon( + url = null, + fallbackResId = R.drawable.img_polygon_22, + isGrayscale = false, + shouldShowCustomBadge = false, + ), ) - private val customTokenIconState - get() = CurrencyIconState.CustomTokenIcon( - tint = TangemColorPalette.Black, - background = TangemColorPalette.Meadow, - topBadgeIconResId = R.drawable.img_polygon_22, - isGrayscale = false, + private val tokenIconState: TangemIconUM.Currency + get() = TangemIconUM.Currency( + CurrencyIconState.TokenIcon( + url = null, + topBadgeIconResId = R.drawable.img_polygon_22, + fallbackTint = TangemColorPalette.Black, + fallbackBackground = TangemColorPalette.Meadow, + isGrayscale = false, + shouldShowCustomBadge = false, + ), + ) + + private val customTokenIconState: TangemIconUM.Currency + get() = TangemIconUM.Currency( + CurrencyIconState.CustomTokenIcon( + tint = TangemColorPalette.Black, + background = TangemColorPalette.Meadow, + topBadgeIconResId = R.drawable.img_polygon_22, + isGrayscale = false, + ), ) val defaultState: TangemTokenRowUM.Content get() = TangemTokenRowUM.Content( id = UUID.randomUUID().toString(), - iconState = coinIconState, + headIconUM = coinIconState, titleUM = titleUM, subtitleUM = subtitleUM, topEndContentUM = topEndContentUM, @@ -131,9 +143,9 @@ internal object TangemTokenRowPreviewData { }), ), icons = persistentListOf( - TangemTokenRowUM.IconUM(R.drawable.ic_staking_mini_10), - TangemTokenRowUM.IconUM(R.drawable.ic_attention_12), - TangemTokenRowUM.IconUM(R.drawable.ic_error_sync_24), + TangemIconUM.Icon(R.drawable.ic_staking_mini_10), + TangemIconUM.Icon(R.drawable.ic_attention_12), + TangemIconUM.Icon(R.drawable.ic_error_sync_24), ), ), bottomEndContentUM = bottomEndContentUM, @@ -146,7 +158,7 @@ internal object TangemTokenRowPreviewData { val tokenState: TangemTokenRowUM.Content get() = TangemTokenRowUM.Content( id = UUID.randomUUID().toString(), - iconState = tokenIconState, + headIconUM = tokenIconState, titleUM = titleUM, subtitleUM = subtitleUM, topEndContentUM = topEndContentUM, @@ -160,7 +172,7 @@ internal object TangemTokenRowPreviewData { val customTokenState: TangemTokenRowUM.Content get() = TangemTokenRowUM.Content( id = UUID.randomUUID().toString(), - iconState = customTokenIconState, + headIconUM = customTokenIconState, titleUM = titleUM, subtitleUM = subtitleUM, topEndContentUM = topEndContentUM, @@ -174,7 +186,7 @@ internal object TangemTokenRowPreviewData { val draggableState: TangemTokenRowUM.Actionable get() = TangemTokenRowUM.Actionable( id = UUID.randomUUID().toString(), - iconState = coinIconState, + headIconUM = coinIconState, titleUM = titleUM, subtitleUM = subtitleUM, tailUM = TangemTokenRowUM.TailUM.Draggable, @@ -185,7 +197,7 @@ internal object TangemTokenRowPreviewData { val draggableStateV2: TangemTokenRowUM.Actionable get() = TangemTokenRowUM.Actionable( id = UUID.randomUUID().toString(), - iconState = coinIconState, + headIconUM = coinIconState, titleUM = titleUM, subtitleUM = subtitleUM, topEndContentUM = topEndContentUM, @@ -198,12 +210,12 @@ internal object TangemTokenRowPreviewData { val loadingState: TangemTokenRowUM.Loading get() = TangemTokenRowUM.Loading( id = UUID.randomUUID().toString(), - iconState = coinIconState, + headIconUM = coinIconState, titleUM = TangemTokenRowUM.TitleUM.Loading, subtitleUM = TangemTokenRowUM.SubtitleUM.Loading, ) - val unreachableState: TangemTokenRowUM.Content + val unreachableState: TangemTokenRowUM get() = defaultState.copy( topEndContentUM = TangemTokenRowUM.EndContentUM.Content( text = stringReference(StringsSigns.DASH_SIGN), @@ -219,7 +231,7 @@ internal object TangemTokenRowPreviewData { val accountState: TangemTokenRowUM.Content get() = TangemTokenRowUM.Content( id = UUID.randomUUID().toString(), - iconState = accountResIcon, + headIconUM = accountResIcon, titleUM = TangemTokenRowUM.TitleUM.Content( text = stringReference(value = "Portfolio"), ), @@ -239,7 +251,7 @@ internal object TangemTokenRowPreviewData { val accountLetterState: TangemTokenRowUM.Content get() = accountState.copy( - iconState = accountLetterIcon, + headIconUM = accountLetterIcon, ) val accountEllipsisState: TangemTokenRowUM.Content diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/internal/TokenRowEndBottomContent.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/internal/TokenRowEndBottomContent.kt index a4bdecca54..376e670bda 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/internal/TokenRowEndBottomContent.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/internal/TokenRowEndBottomContent.kt @@ -65,9 +65,9 @@ private fun Content( ), ) - when (endContentUM.priceChangeUM) { + when (val priceChangeUM = endContentUM.priceChangeUM) { is PriceChangeState.Content -> TokenRowPriceChangeContent( - priceChangeState = endContentUM.priceChangeUM, + priceChangeState = priceChangeUM, isFlickering = endContentUM.isFlickering, isAvailable = endContentUM.isAvailable, ) diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/internal/TokenRowSubtitle.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/internal/TokenRowSubtitle.kt index c0d32baca0..4bcdac0586 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/internal/TokenRowSubtitle.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/internal/TokenRowSubtitle.kt @@ -65,9 +65,9 @@ private fun SubtitleContent(subtitleUM: TangemTokenRowUM.SubtitleUM.Content, mod ), ) - when (subtitleUM.priceChangeUM) { + when (val priceChangeUM = subtitleUM.priceChangeUM) { is PriceChangeState.Content -> TokenRowPriceChangeContent( - priceChangeState = subtitleUM.priceChangeUM, + priceChangeState = priceChangeUM, isFlickering = subtitleUM.isFlickering, isAvailable = subtitleUM.isAvailable, ) diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/internal/TokenRowTitle.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/internal/TokenRowTitle.kt index ad8368ab4d..65f1334bee 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/internal/TokenRowTitle.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/internal/TokenRowTitle.kt @@ -21,12 +21,12 @@ import com.tangem.core.ui.components.TextShimmer import com.tangem.core.ui.ds.badge.TangemBadge import com.tangem.core.ui.ds.row.token.TangemTokenRowUM import com.tangem.core.ui.extensions.conditional -import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.extensions.resolveAnnotatedReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreviewRedesign @Composable -internal fun TokenRowTitle(titleUM: TangemTokenRowUM.TitleUM, modifier: Modifier = Modifier) { +fun TokenRowTitle(titleUM: TangemTokenRowUM.TitleUM, modifier: Modifier = Modifier) { when (titleUM) { is TangemTokenRowUM.TitleUM.Content -> ContentTitle(titleUM = titleUM, modifier = modifier) TangemTokenRowUM.TitleUM.Loading -> TextShimmer( @@ -42,7 +42,7 @@ internal fun TokenRowTitle(titleUM: TangemTokenRowUM.TitleUM, modifier: Modifier private fun ContentTitle(titleUM: TangemTokenRowUM.TitleUM.Content, modifier: Modifier = Modifier) { Row( modifier = modifier, - horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens2.x4), + horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens2.x1), verticalAlignment = Alignment.CenterVertically, ) { /* @@ -50,7 +50,7 @@ private fun ContentTitle(titleUM: TangemTokenRowUM.TitleUM.Content, modifier: Mo * So we need to use [weight] to avoid displacement. */ Text( - text = titleUM.text.resolveReference(), + text = titleUM.text.resolveAnnotatedReference(), modifier = Modifier.weight(weight = 1f, fill = false), color = if (titleUM.isAvailable) { TangemTheme.colors2.text.neutral.primary diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/topbar/TangemTopBarInner.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/topbar/TangemTopBarInner.kt index 1014c640e4..e445ea4a30 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ds/topbar/TangemTopBarInner.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/topbar/TangemTopBarInner.kt @@ -8,6 +8,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalDensity import com.tangem.core.ui.extensions.clickableSingle import com.tangem.core.ui.extensions.conditional import com.tangem.core.ui.extensions.conditionalCompose @@ -37,16 +38,20 @@ internal fun TangemTopBarInner( onEndContentClick: (() -> Unit)? = null, isGhostButtons: Boolean = false, ) { + val statusBarHeight = with(LocalDensity.current) { WindowInsets.statusBars.getTop(density = this).toDp() } Box( modifier = modifier - .height(TangemTheme.dimens2.x16) + .height(TangemTheme.dimens2.x16 + statusBarHeight) .fillMaxWidth() + .padding(top = statusBarHeight) .padding(TangemTheme.dimens2.x4, TangemTheme.dimens2.x3), ) { val iconModifier = Modifier .size(TangemTheme.dimens2.x10) .clip(RoundedCornerShape(TangemTheme.dimens2.x25)) - .background(TangemTheme.colors2.button.backgroundSecondary) + .conditionalCompose(isGhostButtons) { + background(TangemTheme.colors2.button.backgroundSecondary) + } AnimatedVisibility( visible = startContent != null, diff --git a/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt b/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt index e771427974..1b734bd384 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt @@ -384,6 +384,13 @@ fun TextReference.orMaskWithStars(maskWithStars: Boolean): TextReference { return if (maskWithStars) stringReference(THREE_STARS) else this } +/** + * Returns the TextReference itself if it's not null, otherwise returns an empty TextReference. + */ +fun TextReference?.orEmpty(): TextReference { + return this ?: TextReference.EMPTY +} + @ReadOnlyComposable @Composable private fun createStyledText( diff --git a/core/ui/src/main/res/drawable/ic_attention_default_24.xml b/core/ui/src/main/res/drawable/ic_attention_default_24.xml new file mode 100644 index 0000000000..6442020ee2 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_attention_default_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/core/ui/src/main/res/drawable/ic_dollar_default_24.xml b/core/ui/src/main/res/drawable/ic_dollar_default_24.xml new file mode 100644 index 0000000000..d9cc704417 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_dollar_default_24.xml @@ -0,0 +1,12 @@ + + + diff --git a/core/ui/src/main/res/drawable/ic_error_sync_default_24.xml b/core/ui/src/main/res/drawable/ic_error_sync_default_24.xml new file mode 100644 index 0000000000..f86403750c --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_error_sync_default_24.xml @@ -0,0 +1,12 @@ + + + diff --git a/core/ui/src/main/res/drawable/ic_exchange_default_24.xml b/core/ui/src/main/res/drawable/ic_exchange_default_24.xml new file mode 100644 index 0000000000..86260f66f7 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_exchange_default_24.xml @@ -0,0 +1,12 @@ + + + diff --git a/core/ui/src/main/res/drawable/ic_filter_default_24.xml b/core/ui/src/main/res/drawable/ic_filter_default_24.xml new file mode 100644 index 0000000000..ec0a6ac12a --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_filter_default_24.xml @@ -0,0 +1,12 @@ + + + diff --git a/core/ui/src/main/res/drawable/ic_more_default_24.xml b/core/ui/src/main/res/drawable/ic_more_default_24.xml new file mode 100644 index 0000000000..afdee216b3 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_more_default_24.xml @@ -0,0 +1,33 @@ + + + + + + + + diff --git a/core/ui/src/main/res/drawable/ic_plus_default_24.xml b/core/ui/src/main/res/drawable/ic_plus_default_24.xml new file mode 100644 index 0000000000..cb630c28ca --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_plus_default_24.xml @@ -0,0 +1,13 @@ + + +