Updated on 2026-08-14

This commit is contained in:
Tangem 2026-06-19 16:07:08 +05:00
parent a0e5656330
commit d82dbdd1e6
12 changed files with 256 additions and 129 deletions

View file

@ -59,6 +59,8 @@ internal class TangemPayDetailsStateFactory(
fun getLoadedState(status: PaymentAccountStatusValue.Loaded): TangemPayDetailsUM {
val hasUnfrozenCard = status.cards.any { it.frozenState == TangemPayCardFrozenState.Unfrozen }
val hasIssuingCard = status.cards.any { it.state == TangemPayCardState.Issuing }
val isAddCardEnabled = status.error == null && !hasIssuingCard
return TangemPayDetailsUM(
topBarConfig = TangemPayDetailsTopBarConfig(
onBackClick = onBack,
@ -88,7 +90,7 @@ internal class TangemPayDetailsStateFactory(
}
.toImmutableList(),
onAddCardClick = intents::onAddCardClick,
isAddCardEnabled = status.error == null,
isAddCardEnabled = isAddCardEnabled,
),
),
isBalanceHidden = false,

View file

@ -141,6 +141,7 @@ internal fun TangemPayDetailsScreenV2(
expressTransactionsBottomSheetState?.content(null)
}
@Suppress("LongMethod")
private fun LazyListScope.payDetailsBody(state: TangemPayDetailsUM) {
item("balanceBlock") {
BalanceBlock(
@ -174,6 +175,15 @@ private fun LazyListScope.payDetailsBody(state: TangemPayDetailsUM) {
modifier = Modifier.padding(horizontal = TangemTheme.dimens2.x4),
title = resourceReference(R.string.tangempay_issuing_new_digital_card_title),
subtitle = resourceReference(R.string.tangempay_reissue_card_in_progress_description),
contentColor = TangemTheme.colors3.bg.opaque.secondary,
leadingContent = {
Icon(
modifier = Modifier.size(20.dp),
imageVector = ImageVector.vectorResource(R.drawable.ic_clock_24),
contentDescription = null,
tint = TangemTheme.colors3.icon.primary,
)
},
)
}
}

View file

@ -1,9 +1,11 @@
package com.tangem.features.tangempay.ui
import android.content.res.Configuration
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@ -11,7 +13,7 @@ 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.text.TextStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
@ -37,7 +39,6 @@ import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreviewRedesign
import com.tangem.core.ui.res.generated.icons.Icons
import com.tangem.core.ui.res.generated.icons.ic_card_plus_32
import com.tangem.core.ui.res.generated.icons.ic_error_28
import com.tangem.features.tangempay.details.impl.R
import com.tangem.features.tangempay.entity.TangemPayIssueAdditionalCardUM
@ -72,47 +73,55 @@ internal fun TangemPayIssueAdditionalCardContent(state: TangemPayIssueAdditional
@Composable
private fun Content(state: TangemPayIssueAdditionalCardUM) {
val appearance = state.contentAppearance()
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
SpacerH(16.dp)
StatusIcon(appearance = appearance)
SpacerH(32.dp)
CenteredMessageText(
textRes = appearance.titleRes,
style = TangemTheme.typography3.heading.small,
color = TangemTheme.colors3.text.primary,
)
CenteredMessageText(
textRes = appearance.subtitleRes,
style = TangemTheme.typography3.subheading.medium,
color = TangemTheme.colors3.text.secondary,
)
SpacerH(32.dp)
FeeBlock(modifier = Modifier.padding(top = 16.dp), state = state)
SpacerH(8.dp)
BottomButtonsBlock(state = state, appearance = appearance)
Column(modifier = Modifier.fillMaxWidth()) {
Header()
FeeBlock(state = state)
if (state.isBalanceInsufficient) {
InsufficientFundsNotification(state = state)
}
IssueButton(state = state)
}
}
@Composable
private fun StatusIcon(appearance: IssueAdditionalCardContentAppearance) {
private fun Header() {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
SpacerH(32.dp)
StatusIcon()
SpacerH(32.dp)
CenteredMessageText(
textRes = R.string.tangempay_issue_additional_card_title,
style = TangemTheme.typography3.heading.small,
color = TangemTheme.colors3.text.primary,
)
SpacerH(8.dp)
CenteredMessageText(
textRes = R.string.tangempay_issue_additional_card_description,
style = TangemTheme.typography3.subheading.medium,
color = TangemTheme.colors3.text.secondary,
)
SpacerH(32.dp)
}
}
@Composable
private fun StatusIcon() {
Box(
modifier = Modifier
.size(80.dp)
.clip(CircleShape)
.background(appearance.iconBackgroundColor),
.background(TangemTheme.colors3.bg.status.infoSubtle),
contentAlignment = Alignment.Center,
) {
Icon(
imageVector = appearance.icon,
imageVector = Icons.ic_card_plus_32,
contentDescription = null,
tint = appearance.iconColor,
tint = TangemTheme.colors3.icon.status.info,
modifier = Modifier.size(28.dp),
)
}
@ -152,68 +161,64 @@ private fun FeeBlock(state: TangemPayIssueAdditionalCardUM, modifier: Modifier =
}
@Composable
private fun BottomButtonsBlock(
state: TangemPayIssueAdditionalCardUM,
appearance: IssueAdditionalCardContentAppearance,
modifier: Modifier = Modifier,
) {
private fun InsufficientFundsNotification(state: TangemPayIssueAdditionalCardUM, modifier: Modifier = Modifier) {
Column(
modifier = modifier
.fillMaxWidth()
.padding(top = 16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
.padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 16.dp)
.clip(RoundedCornerShape(16.dp))
.background(TangemTheme.colors3.bg.status.warningSubtle)
.padding(horizontal = 14.dp, vertical = 16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.Top,
) {
Image(
modifier = Modifier.size(20.dp),
painter = painterResource(R.drawable.img_usdc_16),
contentDescription = null,
)
Column(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
Text(
text = stringResourceSafe(R.string.tangempay_reissue_card_insufficient_funds_title),
style = TangemTheme.typography3.subheading.medium,
color = TangemTheme.colors3.text.primary,
)
Text(
text = stringResourceSafe(R.string.tangempay_reissue_card_insufficient_funds_subtitle),
style = TangemTheme.typography3.caption.medium,
color = TangemTheme.colors3.text.secondary,
)
}
}
TangemButton(
modifier = Modifier.fillMaxWidth(),
variant = TangemButton.Variant.Secondary,
size = TangemButton.Size.X12,
onClick = state.onDismiss,
text = resourceReference(R.string.common_cancel),
)
TangemButton(
modifier = Modifier.fillMaxWidth(),
size = TangemButton.Size.X12,
onClick = appearance.primaryAction(state),
isEnabled = !state.isLoading,
isLoading = state.isLoading,
text = resourceReference(appearance.primaryButtonTextRes),
size = TangemButton.Size.X8,
onClick = state.onAddFundsClick,
text = resourceReference(R.string.tangempay_card_details_add_funds),
)
}
}
private data class IssueAdditionalCardContentAppearance(
val titleRes: Int,
val subtitleRes: Int,
val icon: ImageVector,
val iconColor: Color,
val iconBackgroundColor: Color,
val primaryButtonTextRes: Int,
val primaryAction: (TangemPayIssueAdditionalCardUM) -> () -> Unit,
)
@Composable
private fun TangemPayIssueAdditionalCardUM.contentAppearance(): IssueAdditionalCardContentAppearance {
return if (isBalanceInsufficient) {
IssueAdditionalCardContentAppearance(
titleRes = R.string.tangempay_reissue_card_insufficient_funds_title,
subtitleRes = R.string.tangempay_reissue_card_insufficient_funds_subtitle,
icon = Icons.ic_error_28,
iconColor = TangemTheme.colors3.icon.status.warning,
iconBackgroundColor = TangemTheme.colors3.bg.status.warningSubtle,
primaryButtonTextRes = R.string.tangempay_card_details_add_funds,
primaryAction = { it.onAddFundsClick },
)
} else {
IssueAdditionalCardContentAppearance(
titleRes = R.string.tangempay_issue_additional_card_title,
subtitleRes = R.string.tangempay_issue_additional_card_description,
icon = Icons.ic_card_plus_32,
iconColor = TangemTheme.colors3.icon.status.info,
iconBackgroundColor = TangemTheme.colors3.bg.status.infoSubtle,
primaryButtonTextRes = R.string.tangempay_issue_card,
primaryAction = { it.onIssueClick },
)
}
private fun IssueButton(state: TangemPayIssueAdditionalCardUM, modifier: Modifier = Modifier) {
TangemButton(
modifier = modifier
.fillMaxWidth()
.padding(16.dp),
size = TangemButton.Size.X12,
onClick = state.onIssueClick,
isEnabled = !state.isLoading && !state.isBalanceInsufficient,
isLoading = state.isLoading,
text = resourceReference(R.string.tangempay_issue_card),
)
}
@Preview(showBackground = true, widthDp = 360)
@ -223,29 +228,7 @@ private fun TangemPayIssueAdditionalCardContentPreview(
@PreviewParameter(IssueAdditionalCardPreviewProvider::class) state: TangemPayIssueAdditionalCardUM,
) {
TangemThemePreviewRedesign {
IssueAdditionalCardSheetPreview(state = state)
}
}
@Composable
private fun IssueAdditionalCardSheetPreview(state: TangemPayIssueAdditionalCardUM) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(TangemTheme.colors3.bg.secondary),
) {
TangemTopBar(
type = TangemTopBarType.BottomSheet,
endContent = {
TangemButton(
iconStart = TangemIconUM.Icon(iconRes = R.drawable.ic_close_24),
onClick = state.onDismiss,
size = TangemButton.Size.X11,
variant = TangemButton.Variant.Material,
)
},
)
Content(state)
TangemPayIssueAdditionalCardContent(state = state)
}
}