Updated on 2026-08-14
This commit is contained in:
parent
da6ee825ab
commit
2f5d6525e2
11 changed files with 362 additions and 15 deletions
|
|
@ -31,10 +31,7 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
|
|||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.containers.FooterContainer
|
||||
import com.tangem.core.ui.components.inputrow.InputRowDefault
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
@ -118,7 +115,7 @@ private fun GiveTxPermissionBottomSheetContent(content: GiveTxPermissionBottomSh
|
|||
@Composable
|
||||
private fun ApprovalBottomSheetInfo(data: GiveTxPermissionState.ReadyForRequest) {
|
||||
FooterContainer(
|
||||
footer = stringResource(id = R.string.give_permission_policy_type_footer),
|
||||
footer = resourceReference(R.string.give_permission_policy_type_footer),
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
) {
|
||||
AmountItem(
|
||||
|
|
@ -130,7 +127,7 @@ private fun ApprovalBottomSheetInfo(data: GiveTxPermissionState.ReadyForRequest)
|
|||
}
|
||||
SpacerH16()
|
||||
FooterContainer(
|
||||
footer = data.footerText.resolveReference(),
|
||||
footer = data.footerText,
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
) {
|
||||
FeeItem(fee = data.fee)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,228 @@
|
|||
package com.tangem.common.ui.expressStatus
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
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.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressLinkUM
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressStatusItemState
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressStatusItemUM
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressStatusUM
|
||||
import com.tangem.core.ui.components.SpacerWMax
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
/**
|
||||
* Block with express statuses
|
||||
*
|
||||
* @param state ui holder
|
||||
* @param modifier modifier
|
||||
* @see [Figma](https://www.figma.com/design/Vs6SkVsFnUPsSCNwlnVf5U/Android-%E2%80%93-UI?node-id=18459-26521&t=4jox7bfqUiXnm2h1-4)
|
||||
*/
|
||||
@Composable
|
||||
fun ExpressStatusBlock(state: ExpressStatusUM, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.padding(
|
||||
vertical = TangemTheme.dimens.spacing14,
|
||||
horizontal = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.padding(bottom = TangemTheme.dimens.spacing16),
|
||||
) {
|
||||
Text(
|
||||
text = state.title.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
SpacerWMax()
|
||||
AnimatedVisibility(visible = state.link is ExpressLinkUM.Content) {
|
||||
val link = remember(this) { state.link as ExpressLinkUM.Content }
|
||||
Row(
|
||||
modifier = Modifier.clickable { link.onClick() },
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = link.icon),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.spacing16)
|
||||
.padding(end = TangemTheme.dimens.spacing2),
|
||||
)
|
||||
Text(
|
||||
text = link.text.resolveReference(),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
state.statuses.forEachIndexed { index, item ->
|
||||
ExpressStatusStep(item, index == state.statuses.lastIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ExpressStatusStep(status: ExpressStatusItemUM, isLast: Boolean) {
|
||||
AnimatedContent(
|
||||
targetState = status,
|
||||
label = "Exchange Step Change Success",
|
||||
transitionSpec = {
|
||||
fadeIn(tween(durationMillis = 220)) togetherWith
|
||||
fadeOut(tween(durationMillis = 220))
|
||||
},
|
||||
) { content ->
|
||||
Row {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
when (content.state) {
|
||||
ExpressStatusItemState.Active -> StepInProgress()
|
||||
ExpressStatusItemState.Default -> StepDefault()
|
||||
ExpressStatusItemState.Done -> Step(
|
||||
iconRes = R.drawable.ic_check_24,
|
||||
iconColor = TangemTheme.colors.icon.primary1,
|
||||
borderColor = TangemTheme.colors.field.focused,
|
||||
)
|
||||
ExpressStatusItemState.Error -> Step(
|
||||
iconRes = R.drawable.ic_close_24,
|
||||
iconColor = TangemTheme.colors.icon.warning,
|
||||
)
|
||||
ExpressStatusItemState.Warning -> Step(
|
||||
iconRes = R.drawable.ic_close_24,
|
||||
iconColor = TangemTheme.colors.icon.attention,
|
||||
)
|
||||
}
|
||||
if (!isLast) {
|
||||
StepSeparator()
|
||||
}
|
||||
}
|
||||
val textColor = when (status.state) {
|
||||
ExpressStatusItemState.Active -> TangemTheme.colors.text.primary1
|
||||
ExpressStatusItemState.Default -> TangemTheme.colors.text.disabled
|
||||
ExpressStatusItemState.Done -> TangemTheme.colors.text.primary1
|
||||
ExpressStatusItemState.Error -> TangemTheme.colors.text.warning
|
||||
ExpressStatusItemState.Warning -> TangemTheme.colors.text.attention
|
||||
}
|
||||
Text(
|
||||
text = content.text.resolveReference(),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = textColor,
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens.spacing12),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun StepDefault() {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size20)
|
||||
.border(
|
||||
width = TangemTheme.dimens.size1_5,
|
||||
color = TangemTheme.colors.field.focused,
|
||||
shape = CircleShape,
|
||||
)
|
||||
.padding(TangemTheme.dimens.spacing2),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Step(iconColor: Color, @DrawableRes iconRes: Int, borderColor: Color = iconColor) {
|
||||
Icon(
|
||||
painter = painterResource(id = iconRes),
|
||||
contentDescription = null,
|
||||
tint = iconColor,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size20)
|
||||
.border(
|
||||
width = TangemTheme.dimens.size1_5,
|
||||
color = borderColor,
|
||||
shape = CircleShape,
|
||||
)
|
||||
.padding(TangemTheme.dimens.spacing2),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun StepInProgress() {
|
||||
CircularProgressIndicator(
|
||||
color = TangemTheme.colors.icon.primary1,
|
||||
strokeWidth = TangemTheme.dimens.size2,
|
||||
modifier = Modifier
|
||||
.padding(TangemTheme.dimens.spacing2)
|
||||
.size(TangemTheme.dimens.size14),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun StepSeparator() {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(vertical = TangemTheme.dimens.spacing2)
|
||||
.size(
|
||||
width = TangemTheme.dimens.size1_5,
|
||||
height = TangemTheme.dimens.size10,
|
||||
)
|
||||
.background(
|
||||
color = TangemTheme.colors.field.focused,
|
||||
shape = CircleShape,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun Preview_ExchangeStatusBlock() {
|
||||
val state = ExpressStatusUM(
|
||||
title = resourceReference(R.string.express_exchange_status_title),
|
||||
link = ExpressLinkUM.Content(
|
||||
icon = R.drawable.ic_alert_24,
|
||||
text = resourceReference(R.string.common_go_to_provider),
|
||||
onClick = {},
|
||||
),
|
||||
statuses = persistentListOf(
|
||||
ExpressStatusItemUM(text = stringReference("Done"), state = ExpressStatusItemState.Done),
|
||||
ExpressStatusItemUM(text = stringReference("Active"), state = ExpressStatusItemState.Active),
|
||||
ExpressStatusItemUM(text = stringReference("Warning"), state = ExpressStatusItemState.Warning),
|
||||
ExpressStatusItemUM(text = stringReference("Error"), state = ExpressStatusItemState.Error),
|
||||
ExpressStatusItemUM(text = stringReference("Default"), state = ExpressStatusItemState.Default),
|
||||
),
|
||||
)
|
||||
|
||||
TangemThemePreview {
|
||||
ExpressStatusBlock(state = state)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package com.tangem.common.ui.expressStatus
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.common.ui.notifications.ExpressNotificationsUM
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.core.ui.components.notifications.Notification
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
@Composable
|
||||
fun ExpressStatusNotificationBlock(state: NotificationUM?) {
|
||||
AnimatedContent(
|
||||
targetState = state,
|
||||
modifier = Modifier.padding(top = 12.dp),
|
||||
label = "Express Status Notification Change",
|
||||
) { notification ->
|
||||
if (notification?.config != null) {
|
||||
Notification(
|
||||
config = notification.config,
|
||||
iconTint = when (state) {
|
||||
is ExpressNotificationsUM.NeedVerification -> TangemTheme.colors.icon.attention
|
||||
is ExpressNotificationsUM.FailedByProvider -> TangemTheme.colors.icon.warning
|
||||
else -> null
|
||||
},
|
||||
containerColor = TangemTheme.colors.background.action,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package com.tangem.common.ui.expressStatus.state
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
/**
|
||||
* UI data holder for express status block
|
||||
*
|
||||
* @property title block title
|
||||
* @property link provider web link
|
||||
* @property statuses list of possible and active statuses
|
||||
*/
|
||||
data class ExpressStatusUM(
|
||||
val title: TextReference,
|
||||
val link: ExpressLinkUM,
|
||||
val statuses: ImmutableList<ExpressStatusItemUM>,
|
||||
)
|
||||
|
||||
/**
|
||||
* Provider web link for express status block.
|
||||
* [Empty] if no link needed
|
||||
* [Content] if link is provided and displayed
|
||||
*/
|
||||
@Stable
|
||||
sealed class ExpressLinkUM {
|
||||
data object Empty : ExpressLinkUM()
|
||||
data class Content(
|
||||
@DrawableRes val icon: Int,
|
||||
val text: TextReference,
|
||||
val onClick: () -> Unit,
|
||||
) : ExpressLinkUM()
|
||||
}
|
||||
|
||||
/**
|
||||
* Single status item in express status block
|
||||
*/
|
||||
data class ExpressStatusItemUM(
|
||||
val text: TextReference,
|
||||
val state: ExpressStatusItemState,
|
||||
)
|
||||
|
||||
/**
|
||||
* Available status states for express status block
|
||||
*/
|
||||
enum class ExpressStatusItemState {
|
||||
Active,
|
||||
Default,
|
||||
Done,
|
||||
Warning,
|
||||
Error,
|
||||
;
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package com.tangem.common.ui.notifications
|
||||
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
|
||||
object ExpressNotificationsUM {
|
||||
|
||||
data class NeedVerification(val onGoToProviderClick: () -> Unit) : NotificationUM.Warning(
|
||||
title = resourceReference(R.string.express_exchange_notification_verification_title),
|
||||
subtitle = resourceReference(R.string.express_exchange_notification_verification_text),
|
||||
iconResId = R.drawable.ic_alert_triangle_20,
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.common_go_to_provider),
|
||||
onClick = onGoToProviderClick,
|
||||
),
|
||||
)
|
||||
|
||||
data class FailedByProvider(val onGoToProviderClick: () -> Unit) : NotificationUM.Error(
|
||||
title = resourceReference(R.string.express_exchange_notification_failed_title),
|
||||
subtitle = resourceReference(R.string.express_exchange_notification_failed_text),
|
||||
iconResId = R.drawable.ic_alert_circle_24,
|
||||
buttonState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.common_go_to_provider),
|
||||
onClick = onGoToProviderClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -5,8 +5,11 @@ import androidx.compose.foundation.layout.Column
|
|||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
/**
|
||||
|
|
@ -20,15 +23,16 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
@Composable
|
||||
fun FooterContainer(
|
||||
modifier: Modifier = Modifier,
|
||||
footer: String? = null,
|
||||
footer: TextReference? = null,
|
||||
footerTopPadding: Dp = TangemTheme.dimens.spacing8,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
content()
|
||||
AnimatedVisibility(visible = footer != null) {
|
||||
val footerWrapped = remember(this) { requireNotNull(footer) }
|
||||
Text(
|
||||
text = footer.orEmpty(),
|
||||
text = footerWrapped.resolveReference(),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.core.ui.components.inputrow.InputRowEnterAmount
|
||||
import com.tangem.core.ui.components.inputrow.InputRowEnterInfoAmount
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.send.impl.presentation.state.fee.FeeType
|
||||
import com.tangem.features.send.impl.presentation.state.fields.SendTextField
|
||||
|
|
@ -41,7 +40,7 @@ internal fun SendCustomFee(
|
|||
repeat(customValues.size) { index ->
|
||||
val value = customValues[index]
|
||||
FooterContainer(
|
||||
footer = value.footer.resolveReference(),
|
||||
footer = value.footer,
|
||||
) {
|
||||
if (value.label != null) {
|
||||
InputRowEnterInfoAmount(
|
||||
|
|
|
|||
|
|
@ -21,8 +21,12 @@ import androidx.compose.ui.res.stringResource
|
|||
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.components.containers.FooterContainer
|
||||
import com.tangem.core.ui.components.inputrow.InputRowRecipient
|
||||
import com.tangem.core.ui.extensions.orMaskWithStars
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.send.impl.R
|
||||
|
|
@ -32,8 +36,6 @@ import com.tangem.features.send.impl.presentation.state.SendStates
|
|||
import com.tangem.features.send.impl.presentation.state.fields.SendTextField
|
||||
import com.tangem.features.send.impl.presentation.state.previewdata.RecipientStatePreviewData
|
||||
import com.tangem.features.send.impl.presentation.state.previewdata.SendClickIntentsStub
|
||||
import com.tangem.core.ui.components.containers.FooterContainer
|
||||
import com.tangem.core.ui.extensions.orMaskWithStars
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
|
|
@ -107,7 +109,7 @@ private fun LazyListScope.addressItem(
|
|||
) {
|
||||
item(key = ADDRESS_FIELD_KEY) {
|
||||
FooterContainer(
|
||||
footer = stringResource(R.string.send_recipient_address_footer, network),
|
||||
footer = resourceReference(R.string.send_recipient_address_footer, wrappedList(network)),
|
||||
) {
|
||||
InputRowRecipient(
|
||||
value = address.value,
|
||||
|
|
@ -137,7 +139,7 @@ private fun LazyListScope.memoField(memoField: SendTextField.RecipientMemo?, onM
|
|||
value = memoField.value,
|
||||
label = memoField.label,
|
||||
placeholder = placeholder,
|
||||
footer = stringResource(R.string.send_recipient_memo_footer),
|
||||
footer = resourceReference(R.string.send_recipient_memo_footer),
|
||||
onValueChange = memoField.onValueChange,
|
||||
onPasteClick = onMemoChange,
|
||||
modifier = Modifier.padding(top = TangemTheme.dimens.spacing20),
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ internal fun TextFieldWithPaste(
|
|||
onValueChange: (String) -> Unit,
|
||||
onPasteClick: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
footer: String? = null,
|
||||
footer: TextReference? = null,
|
||||
labelStyle: TextStyle = TangemTheme.typography.body2,
|
||||
error: TextReference? = null,
|
||||
isError: Boolean = false,
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ internal sealed interface ExchangeStatusNotifications {
|
|||
|
||||
sealed class CommonNotification(val config: NotificationConfig) : ExchangeStatusNotifications
|
||||
|
||||
@Deprecated("Use one in ExpressNotificationsUM")
|
||||
data class NeedVerification(val onGoToProviderClick: () -> Unit) : CommonNotification(
|
||||
config = NotificationConfig(
|
||||
title = resourceReference(R.string.express_exchange_notification_verification_title),
|
||||
|
|
@ -31,6 +32,7 @@ internal sealed interface ExchangeStatusNotifications {
|
|||
),
|
||||
)
|
||||
|
||||
@Deprecated("Use one in ExpressNotificationsUM")
|
||||
data class Failed(val onGoToProviderClick: () -> Unit) : CommonNotification(
|
||||
config = NotificationConfig(
|
||||
title = resourceReference(R.string.express_exchange_notification_failed_title),
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import com.tangem.features.tokendetails.impl.R
|
|||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
@Deprecated("Use ExpressStatusBlock from common")
|
||||
@Composable
|
||||
internal fun ExchangeStatusBlock(
|
||||
statuses: ImmutableList<ExchangeStatusState>,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue