diff --git a/core/ui/src/main/res/drawable/ic_replace_20.xml b/core/ui/src/main/res/drawable/ic_replace_20.xml new file mode 100644 index 0000000000..3fdf7b9e3d --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_replace_20.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/core/ui/src/main/res/drawable/ic_visa_logo.xml b/core/ui/src/main/res/drawable/ic_visa_logo.xml new file mode 100644 index 0000000000..a03fb04772 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_visa_logo.xml @@ -0,0 +1,29 @@ + + + + + + + diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayDetailsComponent.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayDetailsComponent.kt index a669eab012..abff694265 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayDetailsComponent.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayDetailsComponent.kt @@ -16,11 +16,13 @@ import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.components.NavigationBar3ButtonsScrim import com.tangem.core.ui.decompose.ComposableBottomSheetComponent import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.core.ui.res.LocalRedesignEnabled import com.tangem.features.tangempay.components.txHistory.DefaultTangemPayTxHistoryComponent import com.tangem.features.tangempay.components.txHistory.TangemPayTxHistoryDetailsComponent import com.tangem.features.tangempay.entity.TangemPayDetailsNavigation import com.tangem.features.tangempay.model.TangemPayDetailsModel import com.tangem.features.tangempay.ui.TangemPayDetailsScreen +import com.tangem.features.tangempay.ui.TangemPayDetailsScreenV2 import com.tangem.features.tangempay.utils.requireLoaded import com.tangem.features.tangempay.utils.userWalletId import com.tangem.features.tokendetails.ExpressTransactionsComponent @@ -72,12 +74,21 @@ internal class TangemPayDetailsComponent( val bottomSheet by bottomSheetSlot.subscribeAsState() NavigationBar3ButtonsScrim() - TangemPayDetailsScreen( - state = state, - txHistoryComponent = txHistoryComponent, - expressTransactionsComponent = expressTransactionsComponent, - modifier = modifier, - ) + if (LocalRedesignEnabled.current) { + TangemPayDetailsScreenV2( + state = state, + txHistoryComponent = txHistoryComponent, + expressTransactionsComponent = expressTransactionsComponent, + modifier = modifier, + ) + } else { + TangemPayDetailsScreen( + state = state, + txHistoryComponent = txHistoryComponent, + expressTransactionsComponent = expressTransactionsComponent, + modifier = modifier, + ) + } bottomSheet.child?.instance?.BottomSheet() } diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayCardPageUM.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayCardPageUM.kt index b4d70fd998..cb24f4b42d 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayCardPageUM.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayCardPageUM.kt @@ -17,6 +17,7 @@ internal data class TangemPayCardPageUM( val dailyLimitState: TangemPayDailyLimitBlockState, val addToWalletBlockState: AddToWalletBlockState? = null, val isReissueInProgress: Boolean = false, + val menuItems: ImmutableList, ) { companion object { fun stub( @@ -40,6 +41,7 @@ internal data class TangemPayCardPageUM( onBackClick = {}, isReissueInProgress = isReissueInProgress, dailyLimitState = dailyLimitState, + menuItems = persistentListOf(), ) } } diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsStateFactory.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsStateFactory.kt index efabb31b08..8de4b0c86d 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsStateFactory.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsStateFactory.kt @@ -4,9 +4,12 @@ import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem import com.tangem.core.ui.components.notifications.NotificationConfig +import com.tangem.core.ui.ds.image.TangemIconUM import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.themedColor import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.generated.icons.Icons +import com.tangem.core.ui.res.generated.icons.ic_document_20 import com.tangem.domain.models.pay.TangemPayCardFrozenState import com.tangem.features.tangempay.details.impl.R import com.tangem.features.tangempay.utils.TangemPayDetailIntents @@ -31,6 +34,7 @@ internal class TangemPayDetailsStateFactory( onBackClick = onBack, onOpenMenu = onOpenMenu, items = getTopBarMenuItems(isTangemPayDeactivated), + itemsV2 = getTopBarMenuItemsV2(isTangemPayDeactivated), ), pullToRefreshConfig = PullToRefreshConfig( isRefreshing = false, @@ -89,4 +93,31 @@ internal class TangemPayDetailsStateFactory( ), ) } + + private fun getTopBarMenuItemsV2(isTangemPayDeactivated: Boolean): ImmutableList { + if (isTangemPayDeactivated) return persistentListOf() + + return persistentListOf( + TangemPayDropDownItemUM( + title = resourceReference(R.string.tangem_pay_terms_limits), + onClick = intents::onClickTermsAndLimits, + icon = TangemIconUM.Icon( + imageVector = Icons.ic_document_20, + tintReference = { + TangemTheme.colors3.icon.primary + }, + ), + ), + TangemPayDropDownItemUM( + title = resourceReference(R.string.tangempay_pay_support), + onClick = intents::onContactSupportClicked, + icon = TangemIconUM.Icon( + imageVector = Icons.ic_document_20, + tintReference = { + TangemTheme.colors3.icon.primary + }, + ), // TODO change when the icon will be ready in design + ), + ) + } } \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsTopBarConfig.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsTopBarConfig.kt index 9675f1b31d..f25f792ca5 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsTopBarConfig.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsTopBarConfig.kt @@ -7,4 +7,5 @@ internal data class TangemPayDetailsTopBarConfig( val onBackClick: () -> Unit, val onOpenMenu: () -> Unit, val items: ImmutableList, + val itemsV2: ImmutableList, ) \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsUM.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsUM.kt index e51533434b..706d875d26 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsUM.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsUM.kt @@ -77,7 +77,7 @@ internal sealed class TangemPayDetailsBalanceBlockState { data class Content( override val actionButtons: ImmutableList, override val cardsBlockState: CardsBlockState?, - val fiatBalance: String, + val fiatBalance: TextReference, val isBalanceFlickering: Boolean, ) : TangemPayDetailsBalanceBlockState() diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDropDownItemUM.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDropDownItemUM.kt new file mode 100644 index 0000000000..71d30e3f2e --- /dev/null +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDropDownItemUM.kt @@ -0,0 +1,10 @@ +package com.tangem.features.tangempay.entity + +import com.tangem.core.ui.ds.image.TangemIconUM +import com.tangem.core.ui.extensions.TextReference + +internal data class TangemPayDropDownItemUM( + val onClick: () -> Unit, + val title: TextReference, + val icon: TangemIconUM, +) \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayCardPageModel.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayCardPageModel.kt index 0694b3694b..9db19e4817 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayCardPageModel.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayCardPageModel.kt @@ -13,6 +13,7 @@ import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.navigation.Router import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.ui.DesignFeatureToggles +import com.tangem.core.ui.ds.image.TangemIconUM import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.format.bigdecimal.fiat @@ -20,6 +21,7 @@ import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.format.bigdecimal.getJavaCurrencyByCode import com.tangem.core.ui.format.bigdecimal.optionalDecimals import com.tangem.core.ui.message.SnackbarMessage +import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.test.TangemPayTestTags import com.tangem.domain.models.StatusSource import com.tangem.domain.models.TokenReceiveConfig @@ -53,7 +55,7 @@ import kotlinx.coroutines.launch import javax.inject.Inject import com.tangem.core.ui.R as CoreUiR -@Suppress("LongParameterList") +@Suppress("LongParameterList", "LargeClass") @Stable @ModelScoped internal class TangemPayCardPageModel @Inject constructor( @@ -89,6 +91,7 @@ internal class TangemPayCardPageModel @Inject constructor( dailyLimitState = TangemPayDailyLimitBlockState.Loading, settings = persistentListOf(), settingsV2 = persistentListOf(), + menuItems = buildMenuItems(), ), ) @@ -210,6 +213,21 @@ internal class TangemPayCardPageModel @Inject constructor( ) } + private fun buildMenuItems(): ImmutableList { + return persistentListOf( + TangemPayDropDownItemUM( + title = TextReference.Res(R.string.tangempay_card_details_reissue_card), + onClick = ::onClickReissueCard, + icon = TangemIconUM.Icon( + iconRes = CoreUiR.drawable.ic_replace_20, + tintReference = { + TangemTheme.colors3.icon.primary + }, + ), + ), + ) + } + private fun onClickViewDetails() { modelScope.launch(dispatchers.default) { cardDetailsEventListener.send(CardDetailsEvent.Show) diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsBalanceTransformer.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsBalanceTransformer.kt index d2a9482577..eca90fdbb4 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsBalanceTransformer.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsBalanceTransformer.kt @@ -2,8 +2,10 @@ package com.tangem.features.tangempay.model.transformers import arrow.core.Either import com.tangem.core.error.UniversalError +import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.format.bigdecimal.fiat -import com.tangem.core.ui.format.bigdecimal.format +import com.tangem.core.ui.format.bigdecimal.formatStyled +import com.tangem.core.ui.res.TangemTheme import com.tangem.domain.pay.model.TangemPayCardBalance import com.tangem.features.tangempay.entity.TangemPayDetailsBalanceBlockState import com.tangem.features.tangempay.entity.TangemPayDetailsUM @@ -35,10 +37,14 @@ internal class DetailsBalanceTransformer( return prevState.copy(balanceBlockState = balance) } - private fun getFiatBalanceText(balance: TangemPayCardBalance): String { + private fun getFiatBalanceText(balance: TangemPayCardBalance): TextReference { val currency = Currency.getInstance(balance.currencyCode) - return balance.fiatBalance.format { - fiat(fiatCurrencyCode = currency.currencyCode, fiatCurrencySymbol = currency.symbol) + return balance.fiatBalance.formatStyled { + fiat( + fiatCurrencyCode = currency.currencyCode, + fiatCurrencySymbol = currency.symbol, + spanStyleReference = { TangemTheme.typography3.heading.medium.toSpanStyle() }, + ) } } } \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayCardPageScreen.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayCardPageScreen.kt index 6b2374624b..6793c51dd5 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayCardPageScreen.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayCardPageScreen.kt @@ -15,9 +15,8 @@ import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.material3.Scaffold import androidx.compose.material3.ScaffoldDefaults import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.remember +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalDensity @@ -25,6 +24,9 @@ import androidx.compose.ui.platform.testTag import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.util.fastForEach import com.tangem.core.ui.components.appbar.AppBarWithBackButton +import com.tangem.core.ui.ds.image.TangemIconUM +import com.tangem.core.ui.ds.topbar.TangemTopBar +import com.tangem.core.ui.ds2.button.TangemButton import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.LocalRedesignEnabled @@ -36,7 +38,9 @@ import com.tangem.features.tangempay.components.cardDetails.PreviewTangemPayCard import com.tangem.features.tangempay.components.cardDetails.TangemPayCardDetailsBlockComponent import com.tangem.features.tangempay.details.impl.R import com.tangem.features.tangempay.entity.* +import com.tangem.features.tangempay.ui.components.PayContextMenuBlock import kotlinx.collections.immutable.ImmutableList +import com.tangem.core.ui.R as CoreUiR private const val CONTENT_FADE_DURATION_MS = 300 @@ -51,8 +55,8 @@ internal fun TangemPayCardPageScreen( Scaffold( modifier = modifier, topBar = { - AppBarWithBackButton( - modifier = Modifier.statusBarsPadding(), + CardPageTopBar( + items = state.menuItems, onBackClick = state.onBackClick, ) }, @@ -170,6 +174,48 @@ private fun TangemPayCardPageSettingRow( } } +@Composable +private fun CardPageTopBar( + onBackClick: () -> Unit, + items: ImmutableList, + modifier: Modifier = Modifier, +) { + if (LocalRedesignEnabled.current) { + var isDropdownMenuShown by rememberSaveable { mutableStateOf(false) } + TangemTopBar( + modifier = modifier.statusBarsPadding(), + startContent = { + TangemButton( + iconStart = TangemIconUM.Icon(iconRes = R.drawable.ic_arrow_back_28), + onClick = onBackClick, + size = TangemButton.Size.X11, + variant = TangemButton.Variant.Material, + ) + }, + endContent = { + Box { + TangemButton( + iconStart = TangemIconUM.Icon(iconRes = CoreUiR.drawable.ic_more_default_24), + onClick = { isDropdownMenuShown = true }, + size = TangemButton.Size.X11, + variant = TangemButton.Variant.Material, + ) + PayContextMenuBlock( + items = items, + onMenuDismiss = { isDropdownMenuShown = false }, + isDropdownMenuShown = isDropdownMenuShown, + ) + } + }, + ) + } else { + AppBarWithBackButton( + modifier = modifier, + onBackClick = onBackClick, + ) + } +} + private fun LazyListScope.cardPageItem( key: Any? = null, contentType: Any? = null, diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayCardPageSettingsButtonsBlock.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayCardPageSettingsButtonsBlock.kt index ce36ac6ad5..ca0e75ed12 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayCardPageSettingsButtonsBlock.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayCardPageSettingsButtonsBlock.kt @@ -2,20 +2,20 @@ package com.tangem.features.tangempay.ui import android.content.res.Configuration import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.material3.Text +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.util.fastForEach -import com.tangem.core.ui.ds.image.TangemIconUM -import com.tangem.core.ui.ds2.button.TangemButton -import com.tangem.core.ui.extensions.resolveAnnotatedReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreviewRedesign import com.tangem.features.tangempay.entity.TangemPayCardPageSettingV2 +import com.tangem.features.tangempay.ui.components.TangemPayActionButton import kotlinx.collections.immutable.ImmutableList @Composable @@ -30,41 +30,18 @@ internal fun TangemPayCardPageSettingsButtonsBlock( horizontalArrangement = Arrangement.Center, ) { settings.fastForEach { setting -> - TangemPaySettingButton( - setting = setting, + TangemPayActionButton( modifier = Modifier.then(if (setting.testTag != null) Modifier.testTag(setting.testTag) else Modifier), + title = setting.title, + iconRes = setting.iconRes, + onClick = setting.onClick, + isEnabled = setting.isEnabled, + isLoading = setting.isLoading, ) } } } -@Composable -private fun TangemPaySettingButton(setting: TangemPayCardPageSettingV2, modifier: Modifier = Modifier) { - Column( - modifier = modifier.padding(horizontal = TangemTheme.dimens2.x6), - verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - TangemButton( - variant = TangemButton.Variant.Material, - size = TangemButton.Size.X14, - onClick = setting.onClick, - iconStart = TangemIconUM.Icon( - iconRes = setting.iconRes, - tintReference = { TangemTheme.colors3.icon.primary }, - ), - isLoading = setting.isLoading, - isEnabled = setting.isEnabled, - ) - - Text( - text = setting.title.resolveAnnotatedReference(), - style = TangemTheme.typography3.subheading.medium, - color = TangemTheme.colors3.text.primary, - ) - } -} - @Preview(showBackground = true) @Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreen.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreen.kt index 55d6159978..3371d0dcdb 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreen.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreen.kt @@ -25,6 +25,7 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.tooling.preview.Devices import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter @@ -45,9 +46,7 @@ import com.tangem.core.ui.components.dropdownmenu.TangemDropdownItem import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenu import com.tangem.core.ui.components.notifications.Notification import com.tangem.core.ui.components.text.applyBladeBrush -import com.tangem.core.ui.extensions.orMaskWithStars -import com.tangem.core.ui.extensions.resourceReference -import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.core.ui.extensions.* import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.test.TangemPayTestTags @@ -343,7 +342,7 @@ private fun FiatBalance( ) is TangemPayDetailsBalanceBlockState.Content -> Text( modifier = modifier.testTag(TangemPayTestTags.PAYMENT_ACCOUNT_BALANCE), - text = state.fiatBalance.orMaskWithStars(isBalanceHidden), + text = state.fiatBalance.orMaskWithStars(isBalanceHidden).resolveReference(), style = TangemTheme.typography.h2.applyBladeBrush( isEnabled = state.isBalanceFlickering, textColor = TangemTheme.colors.text.primary1, @@ -432,10 +431,15 @@ private fun TangemPayDetailsScreenPreview( } } -private class TangemPayDetailsUMProvider : CollectionPreviewParameterProvider( +internal class TangemPayDetailsUMProvider : CollectionPreviewParameterProvider( collection = listOf( TangemPayDetailsUM( - topBarConfig = TangemPayDetailsTopBarConfig(onBackClick = {}, onOpenMenu = {}, items = persistentListOf()), + topBarConfig = TangemPayDetailsTopBarConfig( + onBackClick = {}, + onOpenMenu = {}, + items = persistentListOf(), + itemsV2 = persistentListOf(), + ), pullToRefreshConfig = PullToRefreshConfig(isRefreshing = false, onRefresh = {}), balanceBlockState = TangemPayDetailsBalanceBlockState.Content( actionButtons = persistentListOf( @@ -445,7 +449,14 @@ private class TangemPayDetailsUMProvider : CollectionPreviewParameterProvider( +internal class TangemPayDetailsTxHistoryProvider : CollectionPreviewParameterProvider( collection = listOf( PreviewTangemPayTxHistoryComponent.loadingUM, PreviewTangemPayTxHistoryComponent.contentUM, diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreenV2.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreenV2.kt new file mode 100644 index 0000000000..8be30b01d3 --- /dev/null +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreenV2.kt @@ -0,0 +1,404 @@ +package com.tangem.features.tangempay.ui + +import android.content.res.Configuration +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.togetherWith +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.* +import androidx.compose.foundation.text.TextAutoSize +import androidx.compose.material3.Text +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Devices +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.compose.ui.util.fastAny +import androidx.compose.ui.util.fastForEach +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.tangem.core.ui.components.SpacerW +import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig +import com.tangem.core.ui.components.containers.pullToRefresh.TangemPullToRefreshSlidingContainer +import com.tangem.core.ui.components.text.applyBladeBrush +import com.tangem.core.ui.components.topFade +import com.tangem.core.ui.ds.image.TangemIconUM +import com.tangem.core.ui.ds.message.TangemMessage +import com.tangem.core.ui.ds.message.TangemMessageEffect +import com.tangem.core.ui.ds.topbar.TangemTopBar +import com.tangem.core.ui.ds2.button.TangemButton +import com.tangem.core.ui.ds2.shimmers.TextShimmer +import com.tangem.core.ui.ds2.shimmers.TextShimmerStyle +import com.tangem.core.ui.extensions.clickableSingle +import com.tangem.core.ui.extensions.orMaskWithStars +import com.tangem.core.ui.extensions.resolveAnnotatedReference +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreviewRedesign +import com.tangem.core.ui.test.TangemPayTestTags +import com.tangem.features.tangempay.components.express.PreviewEmptyExpressTransactionsComponent +import com.tangem.features.tangempay.components.txHistory.PreviewTangemPayTxHistoryComponent +import com.tangem.features.tangempay.components.txHistory.TangemPayTxHistoryComponent +import com.tangem.features.tangempay.details.impl.R +import com.tangem.features.tangempay.entity.TangemPayDetailsBalanceBlockState +import com.tangem.features.tangempay.entity.TangemPayDetailsTopBarConfig +import com.tangem.features.tangempay.entity.TangemPayDetailsUM +import com.tangem.features.tangempay.entity.TangemPayTxHistoryUM +import com.tangem.features.tangempay.ui.components.PayContextMenuBlock +import com.tangem.features.tangempay.ui.components.TangemPayActionButton +import com.tangem.features.tangempay.ui.components.TangemPayAddCardView +import com.tangem.features.tangempay.ui.components.TangemPayCardView +import com.tangem.features.tokendetails.ExpressTransactionsComponent +import com.tangem.utils.StringsSigns.DASH_SIGN +import kotlinx.collections.immutable.ImmutableList +import com.tangem.core.ui.R as CoreUiR + +private val InitialTopBarHeight: Dp = 64.dp +private val BgImageFadeDistance: Dp = 96.dp +private const val TOP_FADE_MID_STOP = 0.8f +private const val TOP_FADE_MID_ALPHA = 0.8f + +@Suppress("LongMethod") +@Composable +internal fun TangemPayDetailsScreenV2( + state: TangemPayDetailsUM, + txHistoryComponent: TangemPayTxHistoryComponent, + expressTransactionsComponent: ExpressTransactionsComponent, + modifier: Modifier = Modifier, +) { + val listState = rememberLazyListState() + val density = LocalDensity.current + val statusBarHeight = with(density) { WindowInsets.systemBars.getTop(this).toDp() } + val bottomBarHeight = with(density) { WindowInsets.systemBars.getBottom(this).toDp() } + var topBarTotalHeight by remember { mutableStateOf(InitialTopBarHeight + statusBarHeight) } + val rootBackground = TangemTheme.colors3.bg.primary + val fadeDistancePx = with(LocalDensity.current) { BgImageFadeDistance.toPx() } + val bgImageAlpha by remember(fadeDistancePx) { + derivedStateOf { + val scrollOffsetPx = when (listState.firstVisibleItemIndex) { + 0 -> listState.firstVisibleItemScrollOffset.toFloat() + else -> fadeDistancePx + }.coerceAtLeast(0f) + (1f - scrollOffsetPx / fadeDistancePx).coerceIn(0f, 1f) + } + } + + val txHistoryState by txHistoryComponent.state.collectAsStateWithLifecycle() + val expressState by expressTransactionsComponent.state.collectAsStateWithLifecycle() + val expressTransactionsBottomSheetState = expressState.bottomSheetSlot + + Box( + modifier = modifier + .fillMaxSize() + .background(rootBackground), + ) { + Image( + modifier = Modifier + .fillMaxWidth(), + painter = painterResource(R.drawable.img_bg_pay_details), + contentDescription = null, + contentScale = ContentScale.FillWidth, + alpha = bgImageAlpha, + ) + + TangemPullToRefreshSlidingContainer( + config = state.pullToRefreshConfig, + indicatorOffset = topBarTotalHeight, + ) { + LazyColumn( + modifier = Modifier + .fillMaxSize() + .topFade( + height = topBarTotalHeight, + 0f to rootBackground.copy(alpha = 1f - bgImageAlpha), + TOP_FADE_MID_STOP to rootBackground.copy(alpha = TOP_FADE_MID_ALPHA * (1f - bgImageAlpha)), + 1f to Color.Transparent, + ), + horizontalAlignment = Alignment.CenterHorizontally, + state = listState, + contentPadding = PaddingValues( + top = topBarTotalHeight, + bottom = TangemTheme.dimens2.x4 + bottomBarHeight, + ), + ) { + payDetailsBody(state) + with(expressTransactionsComponent) { + expressTransactionsContent( + state = expressState.transactionsToDisplay, + modifier = Modifier + .padding(horizontal = 16.dp) + .padding(top = 12.dp) + .fillMaxWidth(), + ) + } + with(txHistoryComponent) { txHistoryContent(listState = listState, state = txHistoryState) } + } + } + + PayDetailsTopBar( + config = state.topBarConfig, + onHeightChange = { measuredHeight -> + if (topBarTotalHeight != measuredHeight) topBarTotalHeight = measuredHeight + }, + ) + } + expressTransactionsBottomSheetState?.content(null) +} + +private fun LazyListScope.payDetailsBody(state: TangemPayDetailsUM) { + item("balanceBlock") { + BalanceBlock( + state = state.balanceBlockState, + isBalanceHidden = state.isBalanceHidden, + ) + } + state.balanceBlockState.cardsBlockState?.let { cardsState -> + item("cardsBlock") { + CardsBlock(cardsBlockState = cardsState) + } + } + if (state.balanceBlockState.actionButtons.isNotEmpty()) { + item("actionButtonsBlock") { + ActionBlock(actionButtons = state.balanceBlockState.actionButtons) + } + } + when { + state.balanceBlockState.cardsBlockState?.cards?.fastAny { it.isReissuing } == true -> { + item("reissuingBannerBlock") { + TangemMessage( + modifier = Modifier.padding(horizontal = TangemTheme.dimens2.x4), + title = resourceReference(R.string.tangempay_reissue_card_in_progress), + subtitle = resourceReference(R.string.tangempay_reissue_card_in_progress_description), + ) + } + } + else -> { + if (state.addToWalletBlockState != null) { + item("addToWalletBannerBlock") { + TangemPayAddToWalletBlock( + state = state.addToWalletBlockState, + modifier = Modifier.padding(horizontal = TangemTheme.dimens2.x4), + ) + } + } + if (state.accountDeactivatedNotificationConfig != null) { + item("deactivationBannerBlock") { + TangemMessage( + modifier = Modifier + .padding(horizontal = TangemTheme.dimens2.x4) + .clickableSingle( + onClick = { state.accountDeactivatedNotificationConfig.onClick?.invoke() }, + ), + title = state.accountDeactivatedNotificationConfig.title, + subtitle = state.accountDeactivatedNotificationConfig.subtitle, + messageEffect = TangemMessageEffect.Warning, + ) + } + } + } + } +} + +@Composable +private fun PayDetailsTopBar( + config: TangemPayDetailsTopBarConfig, + onHeightChange: (Dp) -> Unit, + modifier: Modifier = Modifier, +) { + val density = LocalDensity.current + TangemTopBar( + modifier = modifier + .onSizeChanged { size -> + onHeightChange(with(density) { size.height.toDp() }) + } + .statusBarsPadding(), + title = resourceReference(R.string.tangempay_payment_account), + subtitle = resourceReference(R.string.tangempay_usdc_on_polygon_network), + startContent = { + TangemButton( + iconStart = TangemIconUM.Icon(iconRes = CoreUiR.drawable.ic_arrow_back_28), + onClick = config.onBackClick, + size = TangemButton.Size.X11, + variant = TangemButton.Variant.Material, + ) + }, + endContent = if (config.itemsV2.isNotEmpty()) { + { + var isDropdownMenuShown by rememberSaveable { mutableStateOf(false) } + Box { + TangemButton( + iconStart = TangemIconUM.Icon(iconRes = CoreUiR.drawable.ic_more_default_24), + onClick = { + config.onOpenMenu() + isDropdownMenuShown = true + }, + size = TangemButton.Size.X11, + variant = TangemButton.Variant.Material, + ) + PayContextMenuBlock( + items = config.itemsV2, + onMenuDismiss = { isDropdownMenuShown = false }, + isDropdownMenuShown = isDropdownMenuShown, + ) + } + } + } else { + null + }, + ) +} + +@Composable +private fun BalanceBlock( + state: TangemPayDetailsBalanceBlockState, + isBalanceHidden: Boolean, + modifier: Modifier = Modifier, +) { + Column( + modifier + .fillMaxWidth() + .padding(horizontal = TangemTheme.dimens2.x4) + .padding(top = TangemTheme.dimens2.x12), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + AnimatedContent( + targetState = state, + label = "Updating the balance", + transitionSpec = { + fadeIn(animationSpec = tween(durationMillis = 220, delayMillis = 90)) togetherWith + fadeOut(animationSpec = tween(durationMillis = 90)) + }, + ) { animatedState -> + when (animatedState) { + is TangemPayDetailsBalanceBlockState.Loading -> TextShimmer( + modifier = Modifier.size(width = 160.dp, height = 56.dp), + text = "1234.00", + style = TextShimmerStyle.HEADING_MEDIUM, + radius = TangemTheme.dimens2.x25, + ) + is TangemPayDetailsBalanceBlockState.Content -> Text( + modifier = Modifier.testTag(TangemPayTestTags.PAYMENT_ACCOUNT_BALANCE), + text = animatedState.fiatBalance.orMaskWithStars(isBalanceHidden).resolveAnnotatedReference(), + style = TangemTheme.typography3.display.medium.applyBladeBrush( + isEnabled = animatedState.isBalanceFlickering, + textColor = TangemTheme.colors3.text.primary, + ), + color = TangemTheme.colors3.text.primary, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + autoSize = TextAutoSize.StepBased( + minFontSize = TangemTheme.typography3.heading.medium.fontSize, + maxFontSize = TangemTheme.typography3.display.medium.fontSize, + ), + ) + is TangemPayDetailsBalanceBlockState.Error -> Text( + modifier = Modifier.testTag(TangemPayTestTags.PAYMENT_ACCOUNT_BALANCE), + text = DASH_SIGN.orMaskWithStars(isBalanceHidden), + style = TangemTheme.typography3.display.medium, + color = TangemTheme.colors3.text.primary, + ) + } + } + } +} + +@Composable +private fun CardsBlock( + cardsBlockState: TangemPayDetailsBalanceBlockState.CardsBlockState, + modifier: Modifier = Modifier, +) { + LazyRow( + modifier = modifier.fillMaxWidth(), + state = rememberLazyListState(), + contentPadding = PaddingValues( + horizontal = TangemTheme.dimens2.x4, + vertical = TangemTheme.dimens2.x6, + ), + horizontalArrangement = Arrangement.Center, + ) { + items(items = cardsBlockState.cards) { item -> + TangemPayCardView( + isReissuing = item.isReissuing, + lastDigits = item.lastDigits, + onClick = item.onClick, + ) + SpacerW(TangemTheme.dimens2.x2) + } + item { + TangemPayAddCardView(onClick = cardsBlockState.onAddCardClick) + } + } +} + +@Composable +private fun LazyItemScope.ActionBlock( + actionButtons: ImmutableList, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier + .fillMaxWidth() + .padding(vertical = TangemTheme.dimens2.x6), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + ) { + actionButtons.fastForEach { actionConfig -> + TangemPayActionButton( + iconRes = actionConfig.iconResId, + onClick = actionConfig.onClick, + isEnabled = actionConfig.isEnabled, + isLoading = actionConfig.isInProgress, + title = actionConfig.text, + ) + } + } +} + +// region preview + +@Preview(device = Devices.PIXEL_7_PRO) +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, device = Devices.PIXEL_7_PRO) +@Composable +private fun TangemPayDetailsScreenPreview( + @PreviewParameter(TangemPayDetailsUMProvider::class) state: TangemPayDetailsUM, +) { + TangemThemePreviewRedesign { + TangemPayDetailsScreenV2( + state = state, + txHistoryComponent = PreviewTangemPayTxHistoryComponent( + txHistoryUM = PreviewTangemPayTxHistoryComponent.contentUM, + ), + expressTransactionsComponent = PreviewEmptyExpressTransactionsComponent(), + ) + } +} + +@Preview(device = Devices.PIXEL_7_PRO) +@Composable +private fun TangemPayDetailsTxHistoryScreenPreview( + @PreviewParameter(TangemPayDetailsTxHistoryProvider::class) state: TangemPayTxHistoryUM, +) { + TangemThemePreviewRedesign { + TangemPayDetailsScreenV2( + state = TangemPayDetailsUMProvider().values.first(), + txHistoryComponent = PreviewTangemPayTxHistoryComponent(txHistoryUM = state), + expressTransactionsComponent = PreviewEmptyExpressTransactionsComponent(), + ) + } +} + +// end region preview \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/components/PayContextMenuBlock.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/components/PayContextMenuBlock.kt new file mode 100644 index 0000000000..88dd7ca342 --- /dev/null +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/components/PayContextMenuBlock.kt @@ -0,0 +1,57 @@ +package com.tangem.features.tangempay.ui.components + +import androidx.compose.foundation.layout.* +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.DpOffset +import androidx.compose.ui.util.fastForEach +import com.tangem.core.ui.ds.contextmenu.TangemContextMenu +import com.tangem.core.ui.ds.image.TangemIcon +import com.tangem.core.ui.extensions.clickableSingle +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.features.tangempay.entity.TangemPayDropDownItemUM +import kotlinx.collections.immutable.ImmutableList + +@Composable +internal fun PayContextMenuBlock( + items: ImmutableList, + isDropdownMenuShown: Boolean, + onMenuDismiss: () -> Unit, + modifier: Modifier = Modifier, +) { + TangemContextMenu( + expanded = isDropdownMenuShown, + onDismissRequest = onMenuDismiss, + offset = DpOffset.Zero, + modifier = modifier, + ) { + items.fastForEach { item -> + Column { + Row( + horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2), + modifier = Modifier + .clickableSingle( + onClick = { + item.onClick() + onMenuDismiss() + }, + ) + .padding(vertical = TangemTheme.dimens2.x3, horizontal = TangemTheme.dimens2.x4), + ) { + TangemIcon( + modifier = Modifier.size(TangemTheme.dimens2.x5), + tangemIconUM = item.icon, + ) + Text( + text = item.title.resolveReference(), + style = TangemTheme.typography3.body.medium, + color = TangemTheme.colors3.text.primary, + maxLines = 1, + ) + } + } + } + } +} \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/components/TangemPayActionButton.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/components/TangemPayActionButton.kt new file mode 100644 index 0000000000..924d154d90 --- /dev/null +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/components/TangemPayActionButton.kt @@ -0,0 +1,66 @@ +package com.tangem.features.tangempay.ui.components + +import android.content.res.Configuration +import androidx.annotation.DrawableRes +import androidx.compose.foundation.layout.Arrangement +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.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.tangem.core.ui.R +import com.tangem.core.ui.ds.image.TangemIconUM +import com.tangem.core.ui.ds2.button.TangemButton +import com.tangem.core.ui.extensions.TextReference +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 + +@Composable +internal fun TangemPayActionButton( + title: TextReference, + @DrawableRes iconRes: Int, + onClick: () -> Unit, + modifier: Modifier = Modifier, + isEnabled: Boolean = true, + isLoading: Boolean = false, +) { + Column( + modifier = modifier.padding(horizontal = TangemTheme.dimens2.x4), + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + TangemButton( + variant = TangemButton.Variant.Material, + size = TangemButton.Size.X14, + onClick = onClick, + iconStart = TangemIconUM.Icon( + iconRes = iconRes, + tintReference = { TangemTheme.colors3.icon.primary }, + ), + isLoading = isLoading, + isEnabled = isEnabled, + ) + + Text( + text = title.resolveAnnotatedReference(), + style = TangemTheme.typography3.subheading.medium, + color = TangemTheme.colors3.text.primary, + ) + } +} + +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun TangemPayActionButtonPreview() { + TangemThemePreviewRedesign { + TangemPayActionButton( + title = stringReference("Action"), + iconRes = R.drawable.ic_arrow_down_24, + onClick = {}, + ) + } +} \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/components/TangemPayCardView.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/components/TangemPayCardView.kt new file mode 100644 index 0000000000..0663d4bd6d --- /dev/null +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/components/TangemPayCardView.kt @@ -0,0 +1,215 @@ +package com.tangem.features.tangempay.ui.components + +import android.content.res.Configuration +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +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.draw.drawBehind +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.TileMode +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.Devices +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.tangem.core.ui.R +import com.tangem.core.ui.components.SpacerH +import com.tangem.core.ui.extensions.clickableSingle +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_clock_12 +import com.tangem.core.ui.res.generated.icons.ic_cloud_12 +import com.tangem.core.ui.test.TangemPayTestTags + +private const val DEFAULT_CARD_BG = 0xFF1C1F29 +private const val REISSUING_CARD_BG = 0xFF1E1E1E + +@Composable +internal fun TangemPayCardView( + isReissuing: Boolean, + lastDigits: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + CardBackground( + modifier = modifier + .size( + height = TangemTheme.dimens2.x10, + width = TangemTheme.dimens2.x14, + ) + .testTag(TangemPayTestTags.PAYMENT_ACCOUNT_CARD_BUTTON), + isReissuing = isReissuing, + onClick = onClick, + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = TangemTheme.dimens2.x1) + .padding(top = TangemTheme.dimens2.x1), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Icon( + modifier = Modifier.size(TangemTheme.dimens2.x3), + imageVector = if (isReissuing) { + Icons.ic_clock_12 + } else { + Icons.ic_cloud_12 + }, + tint = TangemTheme.colors3.icon.staticDark, + contentDescription = null, + ) + + Icon( + modifier = Modifier.size(height = TangemTheme.dimens2.x2, width = 22.dp), + imageVector = ImageVector.vectorResource(R.drawable.ic_visa_logo), + tint = TangemTheme.colors3.icon.staticDark, + contentDescription = null, + ) + } + + Text( + modifier = Modifier + .align(Alignment.BottomEnd) + .padding(end = TangemTheme.dimens2.x1, bottom = TangemTheme.dimens2.x1), + text = lastDigits, + style = TangemTheme.typography3.caption.medium.copy(fontSize = 10.sp), + color = TangemTheme.colors3.text.staticDark.primary, + ) + } +} + +@Composable +internal fun TangemPayAddCardView(onClick: () -> Unit, modifier: Modifier = Modifier) { + Box( + modifier = modifier + .size( + height = TangemTheme.dimens2.x10, + width = TangemTheme.dimens2.x14, + ) + .clip(RoundedCornerShape(TangemTheme.dimens3.borderRadius.b075)) + .background(TangemTheme.colors3.bg.opaque.primary) + .clickableSingle(onClick = onClick), + contentAlignment = Alignment.Center, + ) { + Icon( + modifier = Modifier.size(20.dp), + imageVector = ImageVector.vectorResource(R.drawable.ic_plus_default_24), + contentDescription = null, + tint = TangemTheme.colors3.icon.secondary, + ) + } +} + +@Suppress("MagicNumber") +@Composable +private fun CardBackground( + isReissuing: Boolean, + onClick: () -> Unit, + modifier: Modifier = Modifier, + content: @Composable BoxScope.() -> Unit, +) { + val bgColor = remember(isReissuing) { + if (isReissuing) { + Color(REISSUING_CARD_BG) + } else { + Color(DEFAULT_CARD_BG) + } + } + + Box( + modifier = modifier + .clip(RoundedCornerShape(TangemTheme.dimens3.borderRadius.b075)) + .drawBehind { + drawRect(bgColor) + + val w = size.width + val h = size.height + val radiusScaleRightCorner = h / 2f + val radiusScaleLeftCorner = h / 1.27f + + drawRect( + brush = Brush.radialGradient( + colors = listOf( + Color( + if (isReissuing) 0xFFB0B4BC else 0xFF38587F, + ).copy( + if (isReissuing) .1f else .41f, + ), + Color.Transparent, + ), + center = Offset(w - 20f, h * .05f), + radius = radiusScaleRightCorner, + tileMode = TileMode.Clamp, + ), + ) + + if (!isReissuing) { + drawRect( + brush = Brush.radialGradient( + colors = listOf( + Color(0xFF2881FF).copy(.25f), + Color.Transparent, + ), + center = Offset(0f, h + h * .1f), + radius = radiusScaleLeftCorner, + ), + ) + } + } + .border( + width = 1.dp, + color = TangemTheme.colors3.border.primary, + shape = RoundedCornerShape(TangemTheme.dimens3.borderRadius.b075), + ) + .clickableSingle(onClick = onClick), + content = content, + ) +} + +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, device = Devices.PIXEL_7_PRO) +@Composable +private fun CardBackgroundPreview() { + TangemThemePreviewRedesign { + Column(modifier = Modifier.fillMaxWidth(), verticalArrangement = Arrangement.Center) { + CardBackground( + modifier = Modifier.size( + height = TangemTheme.dimens2.x10, + width = TangemTheme.dimens2.x14, + ), + isReissuing = false, + onClick = {}, + content = {}, + ) + SpacerH(TangemTheme.dimens2.x4) + CardBackground( + modifier = Modifier.size( + height = TangemTheme.dimens2.x10, + width = TangemTheme.dimens2.x14, + ), + isReissuing = true, + onClick = {}, + content = {}, + ) + SpacerH(TangemTheme.dimens2.x4) + TangemPayCardView(isReissuing = false, onClick = {}, lastDigits = "1234") + SpacerH(TangemTheme.dimens2.x4) + TangemPayCardView(isReissuing = true, onClick = {}, lastDigits = "") + SpacerH(TangemTheme.dimens2.x4) + TangemPayAddCardView(onClick = {}) + } + } +} \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/res/drawable-night-hdpi/img_bg_pay_details.webp b/features/tangempay/details/impl/src/main/res/drawable-night-hdpi/img_bg_pay_details.webp new file mode 100644 index 0000000000..190f51ba02 Binary files /dev/null and b/features/tangempay/details/impl/src/main/res/drawable-night-hdpi/img_bg_pay_details.webp differ diff --git a/features/tangempay/details/impl/src/main/res/drawable-night-xhdpi/img_bg_pay_details.webp b/features/tangempay/details/impl/src/main/res/drawable-night-xhdpi/img_bg_pay_details.webp new file mode 100644 index 0000000000..c9b4084734 Binary files /dev/null and b/features/tangempay/details/impl/src/main/res/drawable-night-xhdpi/img_bg_pay_details.webp differ diff --git a/features/tangempay/details/impl/src/main/res/drawable-night-xxhdpi/img_bg_pay_details.webp b/features/tangempay/details/impl/src/main/res/drawable-night-xxhdpi/img_bg_pay_details.webp new file mode 100644 index 0000000000..d8aedcf855 Binary files /dev/null and b/features/tangempay/details/impl/src/main/res/drawable-night-xxhdpi/img_bg_pay_details.webp differ diff --git a/features/tangempay/details/impl/src/main/res/drawable-night-xxxhdpi/img_bg_pay_details.webp b/features/tangempay/details/impl/src/main/res/drawable-night-xxxhdpi/img_bg_pay_details.webp new file mode 100644 index 0000000000..7671f73787 Binary files /dev/null and b/features/tangempay/details/impl/src/main/res/drawable-night-xxxhdpi/img_bg_pay_details.webp differ diff --git a/features/tangempay/details/impl/src/main/res/drawable-notnight-hdpi/img_bg_pay_details.webp b/features/tangempay/details/impl/src/main/res/drawable-notnight-hdpi/img_bg_pay_details.webp new file mode 100644 index 0000000000..dcc819b567 Binary files /dev/null and b/features/tangempay/details/impl/src/main/res/drawable-notnight-hdpi/img_bg_pay_details.webp differ diff --git a/features/tangempay/details/impl/src/main/res/drawable-notnight-xhdpi/img_bg_pay_details.webp b/features/tangempay/details/impl/src/main/res/drawable-notnight-xhdpi/img_bg_pay_details.webp new file mode 100644 index 0000000000..8f47020f26 Binary files /dev/null and b/features/tangempay/details/impl/src/main/res/drawable-notnight-xhdpi/img_bg_pay_details.webp differ diff --git a/features/tangempay/details/impl/src/main/res/drawable-notnight-xxhdpi/img_bg_pay_details.webp b/features/tangempay/details/impl/src/main/res/drawable-notnight-xxhdpi/img_bg_pay_details.webp new file mode 100644 index 0000000000..1a91ca2907 Binary files /dev/null and b/features/tangempay/details/impl/src/main/res/drawable-notnight-xxhdpi/img_bg_pay_details.webp differ diff --git a/features/tangempay/details/impl/src/main/res/drawable-notnight-xxxhdpi/img_bg_pay_details.webp b/features/tangempay/details/impl/src/main/res/drawable-notnight-xxxhdpi/img_bg_pay_details.webp new file mode 100644 index 0000000000..0122654158 Binary files /dev/null and b/features/tangempay/details/impl/src/main/res/drawable-notnight-xxxhdpi/img_bg_pay_details.webp differ