Updated on 2026-08-14
This commit is contained in:
parent
ec9bd02e35
commit
6eee08b9eb
6 changed files with 24 additions and 763 deletions
|
|
@ -1,121 +1,20 @@
|
|||
package com.tangem.core.ui.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Divider
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.states.Item
|
||||
import com.tangem.core.ui.components.states.SelectableItemsState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.valentinilk.shimmer.shimmer
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
/**
|
||||
* Small card with text information attached to the edges
|
||||
*
|
||||
* @param startText text information attached to the left edge
|
||||
* @param endText text information attached to the right edge
|
||||
* @param isLoading if true, shimmer is shown instead of [startText] and [endText]
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=290%3A339&t=WdN5XpixzZLlQAZO-4"
|
||||
* >Figma component</a>
|
||||
*/
|
||||
@Composable
|
||||
fun SmallInfoCard(startText: String, endText: String, isLoading: Boolean = false) {
|
||||
Surface(
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius12),
|
||||
color = TangemTheme.colors.background.primary,
|
||||
elevation = TangemTheme.dimens.elevation2,
|
||||
) {
|
||||
CardInfoBox(
|
||||
startText = startText,
|
||||
endText = endText,
|
||||
isLoading = isLoading,
|
||||
disclaimer = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Small card with text information attached to the edges
|
||||
*
|
||||
* @param startText text information attached to the left edge
|
||||
* @param endText text information attached to the right edge
|
||||
* @param disclaimer closable description text
|
||||
* @param isLoading if true, shimmer is shown instead of [startText] and [endText]
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=1007%3A709&t=X19ZWlYP0rnmdcDO-4"
|
||||
* >Figma component</a>
|
||||
*/
|
||||
@Composable
|
||||
fun SmallInfoCardWithDisclaimer(startText: String, endText: String, disclaimer: String, isLoading: Boolean = false) {
|
||||
Surface(
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius12),
|
||||
color = TangemTheme.colors.background.primary,
|
||||
elevation = TangemTheme.dimens.elevation2,
|
||||
) {
|
||||
CardInfoBox(
|
||||
startText = startText,
|
||||
endText = endText,
|
||||
isLoading = isLoading,
|
||||
disclaimer = disclaimer,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [SmallInfoCard] with warning information shown underneath it.
|
||||
*
|
||||
* @param startText text information attached to the left edge
|
||||
* @param endText text information attached to the right edge
|
||||
* @param warningText text of the warning
|
||||
* @param disclaimer closable description text
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=515%3A1541&t=qYGQJdtf1eeUPdkR-1"
|
||||
* >Figma component</a>
|
||||
*/
|
||||
@Composable
|
||||
fun SmallInfoCardWithWarning(startText: String, endText: String, warningText: String, disclaimer: String? = null) {
|
||||
Surface(
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.size12),
|
||||
color = TangemTheme.colors.background.primary,
|
||||
elevation = TangemTheme.dimens.elevation2,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
CardInfoBox(startText = startText, endText = endText, disclaimer = disclaimer)
|
||||
|
||||
Divider(
|
||||
color = TangemTheme.colors.stroke.primary,
|
||||
thickness = TangemTheme.dimens.size0_5,
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing12),
|
||||
)
|
||||
|
||||
WarningItem(warningText = warningText)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Small card with an icon to the left and title with description shown to the right of it.
|
||||
|
|
@ -140,7 +39,8 @@ fun CardWithIcon(
|
|||
Surface(
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius12),
|
||||
color = TangemTheme.colors.background.primary,
|
||||
elevation = TangemTheme.dimens.elevation2,
|
||||
tonalElevation = TangemTheme.dimens.elevation2,
|
||||
shadowElevation = TangemTheme.dimens.elevation2,
|
||||
) {
|
||||
IconWithTitleAndDescription(
|
||||
title = title,
|
||||
|
|
@ -223,393 +123,12 @@ internal fun IconWithTitleAndDescription(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Card with text information attached to the edges and selectable items
|
||||
*
|
||||
* @param state state of block with items
|
||||
* @param isLoading if true, shimmer is shown instead of data
|
||||
* @param disclaimer description text
|
||||
* @param onSelect listener on item selection
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=290-217&t=FMnRqkWPAgdZSdhv-0"
|
||||
* >Figma component</a>
|
||||
*/
|
||||
@Composable
|
||||
fun <T> SelectableInfoCard(
|
||||
state: SelectableItemsState<T>,
|
||||
onSelect: (Item<T>) -> Unit,
|
||||
isLoading: Boolean = false,
|
||||
disclaimer: String? = null,
|
||||
) {
|
||||
Surface(
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius12),
|
||||
color = TangemTheme.colors.background.primary,
|
||||
elevation = TangemTheme.dimens.elevation2,
|
||||
) {
|
||||
SelectableInfoBlock(
|
||||
state = state,
|
||||
isLoading = isLoading,
|
||||
disclaimer = disclaimer,
|
||||
onSelect = onSelect,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Card with text information attached to the edges, selectable items and warning
|
||||
*
|
||||
* @param state state of block with items
|
||||
* @param isLoading if true, shimmer is shown instead of data
|
||||
* @param disclaimer description text
|
||||
* @param onSelect listener on item selection
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=290-217&t=FMnRqkWPAgdZSdhv-0"
|
||||
* >Figma component</a>
|
||||
*/
|
||||
@Composable
|
||||
fun <T> SelectableInfoCardWithWarning(
|
||||
state: SelectableItemsState<T>,
|
||||
warningText: String,
|
||||
onSelect: (Item<T>) -> Unit,
|
||||
isLoading: Boolean = false,
|
||||
disclaimer: String? = null,
|
||||
) {
|
||||
Surface(
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius12),
|
||||
color = TangemTheme.colors.background.primary,
|
||||
elevation = TangemTheme.dimens.elevation2,
|
||||
) {
|
||||
SelectableInfoBlock(
|
||||
state = state,
|
||||
isLoading = isLoading,
|
||||
disclaimer = disclaimer,
|
||||
warningText = warningText,
|
||||
onSelect = onSelect,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// region elements
|
||||
|
||||
@Composable
|
||||
private fun CardInfoBox(startText: String, endText: String, disclaimer: String?, isLoading: Boolean = false) {
|
||||
val isOpened = remember { mutableStateOf(false) }
|
||||
Column {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(TangemTheme.dimens.size48)
|
||||
.clickable { isOpened.value = !isOpened.value }
|
||||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing16,
|
||||
vertical = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = startText,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
)
|
||||
|
||||
if (isLoading) {
|
||||
ShimmerItem(width = TangemTheme.dimens.size40, height = TangemTheme.dimens.size12)
|
||||
} else {
|
||||
Row {
|
||||
Text(
|
||||
text = endText,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
if (disclaimer != null) {
|
||||
val chevronIcon = if (isOpened.value) {
|
||||
painterResource(id = R.drawable.ic_chevron_up_24)
|
||||
} else {
|
||||
painterResource(id = R.drawable.ic_chevron_24)
|
||||
}
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size20),
|
||||
painter = chevronIcon,
|
||||
tint = TangemTheme.colors.icon.primary1,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isOpened.value && disclaimer != null) {
|
||||
DisclaimerItem(disclaimer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun <T> SelectableInfoBlock(
|
||||
state: SelectableItemsState<T>,
|
||||
onSelect: (Item<T>) -> Unit,
|
||||
isLoading: Boolean = false,
|
||||
disclaimer: String? = null,
|
||||
warningText: String? = null,
|
||||
) {
|
||||
var isOpened by remember { mutableStateOf(false) }
|
||||
Column {
|
||||
if (isLoading) {
|
||||
ShimmerItem(width = TangemTheme.dimens.size40, height = TangemTheme.dimens.size12)
|
||||
} else {
|
||||
val selectedItem = state.selectedItem
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(TangemTheme.dimens.size48)
|
||||
.clickable { isOpened = !isOpened }
|
||||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing16,
|
||||
vertical = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = selectedItem.startText.resolveReference(),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
)
|
||||
|
||||
Row {
|
||||
Text(
|
||||
text = selectedItem.endText.resolveReference(),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
val chevronIcon = if (isOpened) {
|
||||
painterResource(id = R.drawable.ic_chevron_up_24)
|
||||
} else {
|
||||
painterResource(id = R.drawable.ic_chevron_24)
|
||||
}
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size20),
|
||||
painter = chevronIcon,
|
||||
tint = TangemTheme.colors.icon.primary1,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Divider(
|
||||
color = TangemTheme.colors.stroke.primary,
|
||||
thickness = TangemTheme.dimens.size0_5,
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing12),
|
||||
)
|
||||
|
||||
if (isOpened) {
|
||||
state.items.forEach { item ->
|
||||
SelectableItem(item) {
|
||||
onSelect.invoke(item)
|
||||
}
|
||||
Divider(
|
||||
color = TangemTheme.colors.stroke.primary,
|
||||
thickness = TangemTheme.dimens.size0_5,
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing12),
|
||||
)
|
||||
}
|
||||
if (disclaimer != null) {
|
||||
DisclaimerItem(disclaimer)
|
||||
Divider(
|
||||
color = TangemTheme.colors.stroke.primary,
|
||||
thickness = TangemTheme.dimens.size0_5,
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing12),
|
||||
)
|
||||
}
|
||||
}
|
||||
if (warningText?.isNotEmpty() == true) {
|
||||
WarningItem(warningText = warningText)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun <T> SelectableItem(item: Item<T>, onSelect: () -> Unit) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(TangemTheme.dimens.size48)
|
||||
.clickable { onSelect() }
|
||||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing16,
|
||||
vertical = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = item.startText.resolveReference(),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
)
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = item.endText.resolveReference(),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
val size = TangemTheme.dimens.size24
|
||||
if (item.isSelected) {
|
||||
Icon(
|
||||
modifier = Modifier.size(size),
|
||||
painter = painterResource(id = R.drawable.ic_check_24),
|
||||
tint = TangemTheme.colors.icon.accent,
|
||||
contentDescription = null,
|
||||
)
|
||||
} else {
|
||||
SpacerW(width = size)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ShimmerItem(width: Dp, height: Dp) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(width = width, height = height)
|
||||
.shimmer()
|
||||
.background(
|
||||
color = TangemTheme.colors.button.secondary,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius3),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DisclaimerItem(disclaimer: String) {
|
||||
Divider(
|
||||
color = TangemTheme.colors.stroke.primary,
|
||||
thickness = TangemTheme.dimens.size0_5,
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing12),
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
top = TangemTheme.dimens.spacing14,
|
||||
bottom = TangemTheme.dimens.spacing16,
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors.background.secondary,
|
||||
shape = CircleShape,
|
||||
)
|
||||
.height(TangemTheme.dimens.size40)
|
||||
.width(TangemTheme.dimens.size40),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_heart_24),
|
||||
tint = TangemTheme.colors.icon.secondary,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
SpacerW12()
|
||||
Text(
|
||||
text = disclaimer,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WarningItem(warningText: String) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
top = TangemTheme.dimens.spacing14,
|
||||
bottom = TangemTheme.dimens.spacing16,
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors.background.secondary,
|
||||
shape = CircleShape,
|
||||
)
|
||||
.height(TangemTheme.dimens.size40)
|
||||
.width(TangemTheme.dimens.size40),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.img_attention_20),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
SpacerW12()
|
||||
Text(
|
||||
text = warningText,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion elements
|
||||
|
||||
// region Preview
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Preview(widthDp = 360, showBackground = true)
|
||||
@Preview(widthDp = 360, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun CardsPreview() {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
SmallInfoCard(
|
||||
startText = "Balance",
|
||||
endText = "0.4405434 BTC",
|
||||
)
|
||||
|
||||
SpacerH32()
|
||||
|
||||
SmallInfoCardWithDisclaimer(
|
||||
startText = "Balance",
|
||||
endText = "0.4405434 BTC",
|
||||
disclaimer = "Not enough funds for fee on your Polygon wallet to create a transaction." +
|
||||
"Top up your Polygon wallet first.",
|
||||
)
|
||||
|
||||
SpacerH32()
|
||||
|
||||
SmallInfoCardWithWarning(
|
||||
startText = "Balance",
|
||||
endText = "0.4405434 BTC",
|
||||
warningText = "Not enough funds for fee on your Polygon wallet to create a transaction. " +
|
||||
"Top up your Polygon wallet first.",
|
||||
disclaimer = "Not enough funds for fee on your Polygon wallet to create a transaction." +
|
||||
"Top up your Polygon wallet first.",
|
||||
)
|
||||
|
||||
SpacerH32()
|
||||
|
||||
private fun Preview_CardWithIcon() {
|
||||
TangemThemePreview {
|
||||
CardWithIcon(
|
||||
title = "Permit is valid until",
|
||||
description = "26:30",
|
||||
|
|
@ -621,52 +140,6 @@ private fun CardsPreview() {
|
|||
)
|
||||
},
|
||||
)
|
||||
|
||||
SpacerH32()
|
||||
|
||||
val state = SelectableItemsState(
|
||||
selectedItem = Item(0, TextReference.Str("Balance"), TextReference.Str("0.4405434 BTC"), true, ""),
|
||||
items = listOf(
|
||||
Item(0, TextReference.Str("Normal"), TextReference.Str("0.4405434 BTC"), true, ""),
|
||||
Item(1, TextReference.Str("Priority"), TextReference.Str("0.46 BTC"), false, ""),
|
||||
).toImmutableList(),
|
||||
)
|
||||
SelectableInfoCard(
|
||||
state = state,
|
||||
isLoading = false,
|
||||
disclaimer = "Not enough funds for fee on your Polygon wallet to create a transaction",
|
||||
onSelect = {},
|
||||
)
|
||||
|
||||
SpacerH32()
|
||||
|
||||
SelectableInfoCardWithWarning(
|
||||
state = state,
|
||||
isLoading = false,
|
||||
warningText = "Not enough funds for fee on your Polygon wallet to create a transaction. " +
|
||||
"Top up your Polygon wallet first.",
|
||||
disclaimer = "Not enough funds for fee on your Polygon wallet to create a transaction",
|
||||
onSelect = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview_Cards() {
|
||||
TangemThemePreview {
|
||||
CardsPreview()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 48, showBackground = true)
|
||||
@Preview(widthDp = 328, heightDp = 48, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview_SimpleInfoCard() {
|
||||
TangemThemePreview {
|
||||
SmallInfoCard(startText = "Balance", endText = "0.4405434 BTC")
|
||||
SmallInfoCardWithDisclaimer(startText = "Balance", endText = "0.4405434 BTC", disclaimer = "test")
|
||||
}
|
||||
}
|
||||
// endregion Preview
|
||||
|
|
@ -1,209 +0,0 @@
|
|||
package com.tangem.core.ui.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
/**
|
||||
* A card with a warning icon to the left and title with description shown to the right of it.
|
||||
*
|
||||
* @param title title of the warning in bold
|
||||
* @param description description of the warning
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=290%3A217&t=yMepJZTRh5bLkOoJ-1"
|
||||
* >Figma component</a>
|
||||
*/
|
||||
@Composable
|
||||
fun WarningCard(title: String, description: String, icon: @Composable (() -> Unit)? = null) {
|
||||
WarningCardSurface(
|
||||
content = {
|
||||
WarningBody(
|
||||
title = title,
|
||||
description = description,
|
||||
icon = icon,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A card with a warning icon to the left and title without description shown to the right of it.
|
||||
*
|
||||
* @param title title of the warning in bold
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=290%3A217&t=yMepJZTRh5bLkOoJ-1"
|
||||
* >Figma component</a>
|
||||
*/
|
||||
@Composable
|
||||
fun WarningCardTitleOnly(title: String, icon: @Composable (() -> Unit)? = null) {
|
||||
WarningCardMaterial3Style(
|
||||
content = {
|
||||
WarningBody(
|
||||
title = title,
|
||||
description = null,
|
||||
icon = icon,
|
||||
iconBackground = TangemTheme.colors.button.disabled,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* [WarningCard], but clickable (with an 'greater then' icon to the left)
|
||||
*
|
||||
* @param title title of the warning in bold
|
||||
* @param description description of the warning
|
||||
* @param onClick action to be performed when warning is clicked
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=402%3A528&t=yMepJZTRh5bLkOoJ-1"
|
||||
* >Figma component</a>
|
||||
*/
|
||||
@Composable
|
||||
fun ClickableWarningCard(
|
||||
title: String,
|
||||
description: String,
|
||||
icon: @Composable (() -> Unit)? = null,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
WarningCardSurface(
|
||||
content = {
|
||||
WarningBody(title = title, description = description, icon = icon) {
|
||||
SpacerW12()
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_chevron_right_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* [WarningCard], but clickable and with a 'refresh' icon at the right edge
|
||||
*
|
||||
* @param title title of the warning in bold
|
||||
* @param description description of the warning
|
||||
* @param onClick action to be performed when warning is clicked
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=290%3A217&t=yMepJZTRh5bLkOoJ-1"
|
||||
* >Figma component</a>
|
||||
*/
|
||||
@Composable
|
||||
fun RefreshableWarningCard(
|
||||
title: String,
|
||||
description: String,
|
||||
icon: @Composable (() -> Unit)? = null,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
WarningCardSurface(
|
||||
content = {
|
||||
WarningBody(title = title, description = description, icon = icon) {
|
||||
SpacerW12()
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_refresh_24),
|
||||
tint = TangemTheme.colors.icon.primary1,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
|
||||
// region elements
|
||||
|
||||
@Composable
|
||||
private fun WarningBody(
|
||||
title: String,
|
||||
description: String?,
|
||||
iconBackground: Color = TangemTheme.colors.background.secondary,
|
||||
icon: @Composable (() -> Unit)? = null,
|
||||
additionalContent: @Composable () -> Unit = {},
|
||||
) {
|
||||
IconWithTitleAndDescription(
|
||||
title = title,
|
||||
description = description,
|
||||
additionalContent = additionalContent,
|
||||
icon = icon ?: {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.img_attention_20),
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
iconBackground = iconBackground,
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
private fun WarningCardSurface(onClick: (() -> Unit)? = null, content: @Composable () -> Unit) {
|
||||
Card(
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.size12),
|
||||
backgroundColor = TangemTheme.colors.background.primary,
|
||||
elevation = TangemTheme.dimens.elevation2,
|
||||
onClick = onClick ?: {},
|
||||
enabled = onClick != null,
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
private fun WarningCardMaterial3Style(onClick: (() -> Unit)? = null, content: @Composable () -> Unit) {
|
||||
Card(
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius16),
|
||||
backgroundColor = TangemTheme.colors.button.disabled,
|
||||
elevation = TangemTheme.dimens.elevation0,
|
||||
onClick = onClick ?: {},
|
||||
enabled = onClick != null,
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
// endregion elements
|
||||
|
||||
// region Preview
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun WarningsPreview() {
|
||||
TangemThemePreview {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
WarningCard(
|
||||
title = "Exchange rate has expired",
|
||||
description = "To access all the networks, you need to scan the card.",
|
||||
)
|
||||
SpacerH32()
|
||||
ClickableWarningCard(
|
||||
title = "Exchange rate has expired",
|
||||
description = "To access all the networks, you need to scan the card.",
|
||||
onClick = {},
|
||||
)
|
||||
SpacerH32()
|
||||
RefreshableWarningCard(
|
||||
title = "Exchange rate has expired",
|
||||
description = "To access all the networks, you need to scan the card.",
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// endregion Preview
|
||||
|
|
@ -35,7 +35,7 @@ internal object SwapNotificationUM {
|
|||
title = title,
|
||||
subtitle = subtitle ?: resourceReference(id = R.string.common_unknown_error),
|
||||
buttonState = NotificationConfig.ButtonsState.PrimaryButtonConfig(
|
||||
text = resourceReference(R.string.common_ok),
|
||||
text = resourceReference(R.string.common_retry),
|
||||
onClick = onConfirmClick,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -43,19 +43,15 @@ internal class SwapNotificationsFactory(
|
|||
}
|
||||
|
||||
fun getGeneralErrorStateNotifications(
|
||||
notifications: ImmutableList<NotificationUM>,
|
||||
message: TextReference?,
|
||||
onClick: () -> Unit,
|
||||
): ImmutableList<NotificationUM> {
|
||||
val updatedNotifications = notifications.toMutableList()
|
||||
updatedNotifications.add(
|
||||
return persistentListOf(
|
||||
SwapNotificationUM.Error.GenericError(
|
||||
subtitle = message,
|
||||
onConfirmClick = onClick,
|
||||
),
|
||||
)
|
||||
|
||||
return updatedNotifications.toPersistentList()
|
||||
}
|
||||
|
||||
fun getNotAvailableStateNotifications(fromCurrencyName: String): ImmutableList<NotificationUM> {
|
||||
|
|
|
|||
|
|
@ -911,7 +911,6 @@ internal class StateBuilder(
|
|||
fun addNotification(uiState: SwapStateHolder, message: TextReference?, onClick: () -> Unit): SwapStateHolder {
|
||||
return uiState.copy(
|
||||
notifications = notificationsFactory.getGeneralErrorStateNotifications(
|
||||
notifications = uiState.notifications,
|
||||
message = message,
|
||||
onClick = onClick,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import androidx.compose.foundation.rememberScrollState
|
|||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.text.ClickableText
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
|
|
@ -21,6 +21,7 @@ import androidx.compose.ui.text.input.TextFieldValue
|
|||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import com.tangem.common.ui.bottomsheet.permission.state.GiveTxPermissionState
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
|
|
@ -256,14 +257,20 @@ private fun getAnnotatedStringForLegalsWithClick(
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
private fun SwapButton(state: SwapStateHolder, modifier: Modifier = Modifier) {
|
||||
Card(
|
||||
elevation = TangemTheme.dimens.elevation3,
|
||||
elevation = CardDefaults.cardElevation(
|
||||
defaultElevation = 2.dp,
|
||||
pressedElevation = 2.dp,
|
||||
),
|
||||
shape = CircleShape,
|
||||
backgroundColor = TangemTheme.colors.background.action,
|
||||
contentColor = TangemTheme.colors.text.primary1,
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = TangemTheme.colors.background.action,
|
||||
disabledContainerColor = TangemTheme.colors.background.action,
|
||||
contentColor = TangemTheme.colors.text.primary1,
|
||||
disabledContentColor = TangemTheme.colors.text.primary1,
|
||||
),
|
||||
modifier = modifier.size(TangemTheme.dimens.size48),
|
||||
onClick = state.onChangeCardsClicked,
|
||||
enabled = state.changeCardsButtonState == ChangeCardsButtonState.ENABLED,
|
||||
|
|
@ -308,13 +315,6 @@ private fun SwapNotifications(notifications: List<NotificationUM>) {
|
|||
) {
|
||||
notifications.forEach { notification ->
|
||||
when (notification) {
|
||||
is SwapNotificationUM.Error.GenericError -> {
|
||||
RefreshableWarningCard(
|
||||
title = notification.title.resolveReference(),
|
||||
description = notification.config.subtitle.resolveReference(),
|
||||
onClick = notification.onConfirmClick,
|
||||
)
|
||||
}
|
||||
is SwapNotificationUM.Error.ApprovalInProgressWarning,
|
||||
is SwapNotificationUM.Error.TransactionInProgressWarning,
|
||||
-> {
|
||||
|
|
@ -341,7 +341,9 @@ private fun SwapNotifications(notifications: List<NotificationUM>) {
|
|||
is NotificationUM.Info,
|
||||
is NotificationUM.Warning,
|
||||
-> null
|
||||
is NotificationUM.Error -> TangemTheme.colors.icon.warning
|
||||
is SwapNotificationUM.Error.GenericError,
|
||||
is NotificationUM.Error,
|
||||
-> TangemTheme.colors.icon.warning
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue