Updated on 2026-08-14
This commit is contained in:
parent
34edacb74c
commit
4d75db3cdd
2 changed files with 105 additions and 29 deletions
|
|
@ -26,6 +26,8 @@ import androidx.compose.ui.graphics.Color
|
|||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.layout.SubcomposeLayout
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
|
|
@ -37,12 +39,14 @@ import androidx.compose.ui.text.rememberTextMeasurer
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import androidx.compose.ui.unit.Constraints
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import androidx.constraintlayout.compose.ConstrainedLayoutReference
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import androidx.constraintlayout.compose.ConstraintLayoutScope
|
||||
import androidx.constraintlayout.compose.Dimension
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.components.SpacerWMax
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButton
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
|
||||
|
|
@ -60,9 +64,12 @@ import com.tangem.features.tangempay.details.impl.R
|
|||
import com.tangem.features.tangempay.entity.CardDataType
|
||||
import com.tangem.features.tangempay.entity.DisplayNameState
|
||||
import com.tangem.features.tangempay.entity.TangemPayCardDetailsUM
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
private const val TEXT_WIDTH_PADDING = 2
|
||||
private const val FREEZE_ANIMATION_DURATION_MS = 600
|
||||
private const val CARD_WIDTH_RATIO = 328f
|
||||
private const val CARD_HEIGHT_RATIO = 212f
|
||||
private val CustomCardBlockColor = Color(0x1F828282)
|
||||
private val CardBackgroundColor = Color(0xFF171A27)
|
||||
|
||||
|
|
@ -86,9 +93,10 @@ internal fun TangemPayCard(state: TangemPayCardDetailsUM, modifier: Modifier = M
|
|||
CardBgWrapper(
|
||||
rotateCardY = rotateCardY,
|
||||
zAxisDistance = zAxisDistance,
|
||||
shouldShowDetails = shouldShowDetails,
|
||||
modifier = modifier,
|
||||
) {
|
||||
if (shouldShowDetails) {
|
||||
front = { TangemPayCardDetailsHiddenBlock(state = state) },
|
||||
back = {
|
||||
TangemPayCardDetailsShownBlock(
|
||||
cardNumber = state.number,
|
||||
expiry = state.expiry,
|
||||
|
|
@ -99,10 +107,8 @@ internal fun TangemPayCard(state: TangemPayCardDetailsUM, modifier: Modifier = M
|
|||
onHideDetails = state.onClick,
|
||||
modifier = Modifier.graphicsLayer { rotationY = 180f },
|
||||
)
|
||||
} else {
|
||||
TangemPayCardDetailsHiddenBlock(state = state)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod", "DestructuringDeclarationWithTooManyEntries")
|
||||
|
|
@ -111,7 +117,7 @@ private fun TangemPayCardDetailsHiddenBlock(state: TangemPayCardDetailsUM, modif
|
|||
Box(modifier = modifier.fillMaxSize()) {
|
||||
TangemPayCardBackground(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.matchParentSize()
|
||||
.zIndex(0f),
|
||||
cardFrozenState = state.cardFrozenState,
|
||||
)
|
||||
|
|
@ -219,6 +225,7 @@ private fun TangemPayCardBackground(cardFrozenState: TangemPayCardFrozenState, m
|
|||
modifier = Modifier.fillMaxSize(),
|
||||
painter = painterResource(R.drawable.img_tangem_pay_visa),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.FillBounds,
|
||||
)
|
||||
|
||||
if (isFrozen || freezeProgress > 0f) {
|
||||
|
|
@ -228,25 +235,27 @@ private fun TangemPayCardBackground(cardFrozenState: TangemPayCardFrozenState, m
|
|||
.graphicsLayer { alpha = freezeProgress },
|
||||
painter = painterResource(R.drawable.img_tangem_pay_visa_frozen),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Crop,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Suppress("MagicNumber", "LongMethod")
|
||||
@Composable
|
||||
private fun CardBgWrapper(
|
||||
rotateCardY: Float,
|
||||
zAxisDistance: Float,
|
||||
shouldShowDetails: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable BoxScope.() -> Unit,
|
||||
back: @Composable () -> Unit,
|
||||
front: @Composable () -> Unit,
|
||||
) {
|
||||
val isRedesignEnabled = LocalVisaRedesignEnabled.current
|
||||
val shouldShowDetailsBg = rotateCardY > 90f && isRedesignEnabled
|
||||
val shouldShowDetailsBg = shouldShowDetails && isRedesignEnabled
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(328f / 212f) // size of img_tangem_pay_visa
|
||||
.graphicsLayer {
|
||||
rotationY = rotateCardY
|
||||
cameraDistance = zAxisDistance
|
||||
|
|
@ -278,24 +287,77 @@ private fun CardBgWrapper(
|
|||
},
|
||||
),
|
||||
) {
|
||||
Box(modifier = Modifier.fillMaxWidth()) {
|
||||
if (shouldShowDetailsBg) {
|
||||
Image(
|
||||
EqualHeightCardSides(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
placeBackOnTop = shouldShowDetails,
|
||||
front = {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.graphicsLayer {
|
||||
rotationY = rotateCardY
|
||||
cameraDistance = zAxisDistance
|
||||
},
|
||||
painter = painterResource(R.drawable.img_bg_card_details),
|
||||
contentDescription = null,
|
||||
)
|
||||
.graphicsLayer { alpha = if (shouldShowDetails) 0f else 1f },
|
||||
) { front() }
|
||||
},
|
||||
back = {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.graphicsLayer { alpha = if (shouldShowDetails) 1f else 0f },
|
||||
) {
|
||||
if (shouldShowDetailsBg) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.matchParentSize()
|
||||
.graphicsLayer {
|
||||
rotationY = rotateCardY
|
||||
cameraDistance = zAxisDistance
|
||||
},
|
||||
painter = painterResource(R.drawable.img_bg_card_details),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Crop,
|
||||
)
|
||||
}
|
||||
back()
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EqualHeightCardSides(
|
||||
placeBackOnTop: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
back: @Composable () -> Unit,
|
||||
front: @Composable () -> Unit,
|
||||
) {
|
||||
SubcomposeLayout(modifier) { constraints ->
|
||||
val width = constraints.maxWidth
|
||||
val minHeight = if (width == Constraints.Infinity) {
|
||||
0
|
||||
} else {
|
||||
(width * CARD_HEIGHT_RATIO / CARD_WIDTH_RATIO).roundToInt()
|
||||
}
|
||||
|
||||
val naturalConstraints = constraints.copy(minHeight = 0, maxHeight = Constraints.Infinity)
|
||||
val backNaturalHeight = subcompose(CardSide.BackMeasure, back)
|
||||
.maxOfOrNull { it.measure(naturalConstraints).height } ?: 0
|
||||
val finalHeight = maxOf(minHeight, backNaturalHeight)
|
||||
val sizeConstraints = constraints.copy(minHeight = finalHeight, maxHeight = finalHeight)
|
||||
val frontPlaceables = subcompose(CardSide.Front, front).map { it.measure(sizeConstraints) }
|
||||
val backPlaceables = subcompose(CardSide.Back, back).map { it.measure(sizeConstraints) }
|
||||
layout(width, finalHeight) {
|
||||
val ordered = if (placeBackOnTop) {
|
||||
frontPlaceables + backPlaceables
|
||||
} else {
|
||||
backPlaceables + frontPlaceables
|
||||
}
|
||||
content()
|
||||
ordered.forEach { it.place(0, 0) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum class CardSide { BackMeasure, Front, Back }
|
||||
|
||||
@Composable
|
||||
private fun CardTopBlock(modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
|
|
@ -488,7 +550,7 @@ private fun EditingCardDisplayName(state: DisplayNameState.Editing, modifier: Mo
|
|||
LaunchedEffect(Unit) { focusRequester.requestFocus() }
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber", "LongParameterList")
|
||||
@Suppress("MagicNumber", "LongParameterList", "LongMethod")
|
||||
@Composable
|
||||
private fun TangemPayCardDetailsShownBlock(
|
||||
cardNumber: String,
|
||||
|
|
@ -538,6 +600,7 @@ private fun TangemPayCardDetailsShownBlock(
|
|||
copyTestTag = TangemPayTestTags.CARD_DETAILS_COPY_CVC,
|
||||
)
|
||||
}
|
||||
SpacerH(8.dp)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Row {
|
||||
SpacerWMax()
|
||||
|
|
|
|||
|
|
@ -97,8 +97,6 @@ private fun TangemPayCardPageScreen(
|
|||
.fillMaxSize()
|
||||
.padding(scaffoldPaddings),
|
||||
contentPadding = PaddingValues(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
bottom = TangemTheme.dimens.spacing16 + bottomBarHeight,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(if (isRedesignEnabled) 0.dp else TangemTheme.dimens.spacing16),
|
||||
|
|
@ -136,7 +134,10 @@ private fun TangemPayCardSwipePager(
|
|||
) {
|
||||
when {
|
||||
controllers.isEmpty() -> Unit
|
||||
controllers.size == 1 -> CardDetailsPage(controller = controllers.first(), modifier = modifier)
|
||||
controllers.size == 1 -> CardDetailsPage(
|
||||
controller = controllers.first(),
|
||||
modifier = modifier.padding(horizontal = 16.dp),
|
||||
)
|
||||
else -> {
|
||||
val initialPage = controllers.indexOfFirst { it.cardId == selectedCardId }.coerceAtLeast(0)
|
||||
val pagerState = rememberPagerState(initialPage = initialPage) { controllers.size }
|
||||
|
|
@ -328,7 +329,9 @@ private fun LazyListScope.cardPageItem(
|
|||
enter = fadeIn(animationSpec = tween(CONTENT_FADE_DURATION_MS)),
|
||||
exit = fadeOut(animationSpec = tween(CONTENT_FADE_DURATION_MS)),
|
||||
) {
|
||||
content()
|
||||
Box(modifier = Modifier.padding(horizontal = 16.dp)) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -340,7 +343,12 @@ private fun TangemPayCardPageScreenPreviewV1() {
|
|||
TangemThemePreview {
|
||||
TangemPayCardPageScreen(
|
||||
state = TangemPayCardPageUM.stub(),
|
||||
cardSection = { TangemPayCard(state = previewCardDetailsState()) },
|
||||
cardSection = {
|
||||
TangemPayCard(
|
||||
state = previewCardDetailsState(),
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -371,7 +379,12 @@ private fun TangemPayCardPageScreenPreviewV2() {
|
|||
) {
|
||||
TangemPayCardPageScreen(
|
||||
state = TangemPayCardPageUM.stub(),
|
||||
cardSection = { TangemPayCard(state = previewCardDetailsState()) },
|
||||
cardSection = {
|
||||
TangemPayCard(
|
||||
state = previewCardDetailsState(),
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue