From 72c9e20396c81f2438c6c81275b66035251f764f Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 29 May 2026 10:11:15 +0200 Subject: [PATCH] Updated on 2026-08-14 --- core/res/src/main/res/values/strings.xml | 12 +- .../src/main/res/drawable/ic_card_pin_24.xml | 33 +++ core/ui/src/main/res/drawable/ic_close_20.xml | 24 ++ .../src/main/res/drawable/ic_edit_card_20.xml | 27 ++ .../ui/src/main/res/drawable/ic_freeze_24.xml | 24 ++ .../src/main/res/drawable/ic_limit_new_20.xml | 27 ++ .../res/drawable/ic_visa_card_details_24.xml | 27 ++ .../src/main/res/drawable/ic_warning_20.xml | 30 +++ .../tangempay/entity/TangemPayCardPageUM.kt | 60 ++++- .../tangempay/entity/TangemPayDetailsUM.kt | 4 + .../model/TangemPayCardDetailsBlockModel.kt | 20 +- .../tangempay/model/TangemPayCardPageModel.kt | 70 +++++ .../DetailsAddToWalletBannerTransformer.kt | 6 +- .../tangempay/ui/TangemPayAddToWalletBlock.kt | 35 ++- .../tangempay/ui/TangemPayCardDetailsBlock.kt | 254 ++++++++++++------ .../tangempay/ui/TangemPayCardPageScreen.kt | 108 ++++++-- .../TangemPayCardPageSettingsButtonsBlock.kt | 94 +++++++ .../tangempay/ui/TangemPayDailyLimitBlock.kt | 200 +++++++++++++- .../tangempay/ui/TangemPayDetailsScreen.kt | 6 +- 19 files changed, 934 insertions(+), 127 deletions(-) create mode 100644 core/ui/src/main/res/drawable/ic_card_pin_24.xml create mode 100644 core/ui/src/main/res/drawable/ic_close_20.xml create mode 100644 core/ui/src/main/res/drawable/ic_edit_card_20.xml create mode 100644 core/ui/src/main/res/drawable/ic_freeze_24.xml create mode 100644 core/ui/src/main/res/drawable/ic_limit_new_20.xml create mode 100644 core/ui/src/main/res/drawable/ic_visa_card_details_24.xml create mode 100644 core/ui/src/main/res/drawable/ic_warning_20.xml create mode 100644 features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayCardPageSettingsButtonsBlock.kt diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index af23d2743e..da439ddcc8 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -91,6 +91,7 @@ Add custom token Manage tokens Credit card or bank account + Fund token Share your address or QR-code Between your portfolios You receive @@ -1213,9 +1214,7 @@ Organize tokens Ungroup %s support - Grant permission - Push Notifications are enabled but won\'t work until you allow notifications in your device settings - Push Notifications are enabled but won\'t work until you grant permission + Push Notifications are enabled but won\'t work until you allow them Allow notifications Product news, exclusive offers, and activity reminders. Offers & Updates @@ -1687,11 +1686,13 @@ Failed to freeze the card. Try again later. Freeze Your card is frozen. + Unfreeze Get Help Reason: %s %s · %s MCC %s Other + PIN-code Unable to use on rooted devices Completed Declined @@ -1742,7 +1743,6 @@ Add card to Google Pay Add card to Apple Pay PIN code - Share your address or show QR-code Technical issues detected. Please try again later or contact support. Receive unavailable now Replace card @@ -1751,7 +1751,6 @@ Card name Reveal Show details - Swap any asset in your portfolio for card Card details Please try again later Unfreeze Card @@ -1769,6 +1768,7 @@ Change Current limit We couldn\'t load your daily limit. Please try again. + Reload to try again Daily limit unavailable You can change it again anytime you like Daily limit is set @@ -1854,7 +1854,6 @@ Use your card or ring to renew session Use your card or ring to renew session Renew session - Renew session Payment account session expired Use USDC for everyday payments Tangem Pay is temporarily unreachable @@ -1864,7 +1863,6 @@ Use crypto from your wallet to top up your payment account Swap from Tangem Wallet USDC on Polygon network - Click the button below to restore access Funds from refunded purchases won’t be returned your on-chain Polygon balance or be available for withdrawal, but will stay on your card balance for purchases Please note Your PIN code diff --git a/core/ui/src/main/res/drawable/ic_card_pin_24.xml b/core/ui/src/main/res/drawable/ic_card_pin_24.xml new file mode 100644 index 0000000000..be0bd72728 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_card_pin_24.xml @@ -0,0 +1,33 @@ + + + + + + + diff --git a/core/ui/src/main/res/drawable/ic_close_20.xml b/core/ui/src/main/res/drawable/ic_close_20.xml new file mode 100644 index 0000000000..7ff6a2f80b --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_close_20.xml @@ -0,0 +1,24 @@ + + + + diff --git a/core/ui/src/main/res/drawable/ic_edit_card_20.xml b/core/ui/src/main/res/drawable/ic_edit_card_20.xml new file mode 100644 index 0000000000..5f7371d0da --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_edit_card_20.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/core/ui/src/main/res/drawable/ic_freeze_24.xml b/core/ui/src/main/res/drawable/ic_freeze_24.xml new file mode 100644 index 0000000000..c0febaec3b --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_freeze_24.xml @@ -0,0 +1,24 @@ + + + + diff --git a/core/ui/src/main/res/drawable/ic_limit_new_20.xml b/core/ui/src/main/res/drawable/ic_limit_new_20.xml new file mode 100644 index 0000000000..91a177c4d1 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_limit_new_20.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/core/ui/src/main/res/drawable/ic_visa_card_details_24.xml b/core/ui/src/main/res/drawable/ic_visa_card_details_24.xml new file mode 100644 index 0000000000..26a48d4fd1 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_visa_card_details_24.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/core/ui/src/main/res/drawable/ic_warning_20.xml b/core/ui/src/main/res/drawable/ic_warning_20.xml new file mode 100644 index 0000000000..7d038c3701 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_warning_20.xml @@ -0,0 +1,30 @@ + + + + + + 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 c70080b726..b4d70fd998 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 @@ -1,13 +1,18 @@ package com.tangem.features.tangempay.entity +import androidx.annotation.DrawableRes import androidx.compose.runtime.Immutable import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.features.tangempay.details.impl.R import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf +import com.tangem.core.ui.R as CoreUiR @Immutable internal data class TangemPayCardPageUM( val settings: ImmutableList, + val settingsV2: ImmutableList, val onBackClick: () -> Unit, val dailyLimitState: TangemPayDailyLimitBlockState, val addToWalletBlockState: AddToWalletBlockState? = null, @@ -15,7 +20,11 @@ internal data class TangemPayCardPageUM( ) { companion object { fun stub( - addToWalletBlockState: AddToWalletBlockState? = AddToWalletBlockState(onClick = {}, onClickClose = {}), + addToWalletBlockState: AddToWalletBlockState? = AddToWalletBlockState( + onClick = {}, + onClickClose = {}, + shouldUseMagicEffect = false, + ), settings: ImmutableList = persistentListOf( TangemPayCardPageSetting(TextReference.Str("Pin Code")) {}, TangemPayCardPageSetting(TextReference.Str("Freeze Card")) {}, @@ -23,9 +32,11 @@ internal data class TangemPayCardPageUM( ), isReissueInProgress: Boolean = false, dailyLimitState: TangemPayDailyLimitBlockState = TangemPayDailyLimitBlockState.Content.stub(), + settingsV2: ImmutableList = TangemPayCardPageSettingV2.stubList(), ) = TangemPayCardPageUM( addToWalletBlockState = addToWalletBlockState, settings = settings, + settingsV2 = settingsV2, onBackClick = {}, isReissueInProgress = isReissueInProgress, dailyLimitState = dailyLimitState, @@ -38,4 +49,49 @@ internal data class TangemPayCardPageSetting( val title: TextReference, val testTag: String? = null, val onSettingClick: () -> Unit, -) \ No newline at end of file +) + +@Immutable +internal data class TangemPayCardPageSettingV2( + val id: Id, + val title: TextReference, + val isLoading: Boolean = false, + val isEnabled: Boolean = true, + val testTag: String? = null, + val onClick: () -> Unit, + @param:DrawableRes val iconRes: Int, +) { + + enum class Id { + Details, Freeze, ChangePin + } + + companion object { + fun stubList(isFrozen: Boolean = false): ImmutableList = persistentListOf( + TangemPayCardPageSettingV2( + id = Id.Details, + title = resourceReference(R.string.details_title), + onClick = {}, + iconRes = CoreUiR.drawable.ic_visa_card_details_24, + ), + TangemPayCardPageSettingV2( + id = Id.Freeze, + title = resourceReference( + if (isFrozen) { + R.string.tangem_pay_freeze_card_unfreeze + } else { + R.string.tangem_pay_freeze_card_freeze + }, + ), + onClick = {}, + iconRes = CoreUiR.drawable.ic_freeze_24, + ), + TangemPayCardPageSettingV2( + id = Id.ChangePin, + title = resourceReference(R.string.tangem_pay_pin_code_title), + onClick = {}, + iconRes = CoreUiR.drawable.ic_card_pin_24, + ), + ) + } +} \ 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 c16071cc32..e51533434b 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 @@ -1,5 +1,6 @@ package com.tangem.features.tangempay.entity +import androidx.compose.runtime.Immutable import androidx.compose.ui.text.input.TextFieldValue import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig @@ -34,6 +35,7 @@ internal data class TangemPayCardDetailsUM( val isActionsAvailable: Boolean = false, ) +@Immutable internal sealed interface DisplayNameState { val displayName: String @@ -61,6 +63,7 @@ internal sealed interface DisplayNameState { } } +@Immutable internal sealed class TangemPayDetailsBalanceBlockState { abstract val actionButtons: ImmutableList @@ -94,4 +97,5 @@ internal sealed class TangemPayDetailsBalanceBlockState { internal data class AddToWalletBlockState( val onClick: () -> Unit, val onClickClose: () -> Unit, + val shouldUseMagicEffect: Boolean, ) \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayCardDetailsBlockModel.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayCardDetailsBlockModel.kt index 0dd19c41f5..27bebaf727 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayCardDetailsBlockModel.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayCardDetailsBlockModel.kt @@ -66,7 +66,7 @@ internal class TangemPayCardDetailsBlockModel @Inject constructor( displayName = card.displayName, isEditingNameEnabled = params.isEditingNameEnabled, onEditNameClick = ::startEditingDisplayName, - onReveal = ::revealCardDetails, + onReveal = ::requestReveal, onCopy = ::copyData, ) @@ -99,7 +99,7 @@ internal class TangemPayCardDetailsBlockModel @Inject constructor( ) { val card = status.requireCardWithId(cardId) if (card.isReissuing) { - hideCardDetails() + requestHide() } card.displayName?.let { uiState.update(TangemPayCardDetailsUpdateNameTransformer(it)) } uiState.update { uiState -> @@ -124,18 +124,26 @@ internal class TangemPayCardDetailsBlockModel @Inject constructor( .launchIn(modelScope) } + private fun requestReveal() { + modelScope.launch { cardDetailsEventListener.send(CardDetailsEvent.Show) } + } + + private fun requestHide() { + modelScope.launch { cardDetailsEventListener.send(CardDetailsEvent.Hide) } + } + private fun revealCardDetails() { analytics.send(TangemPayAnalyticsEvents.ViewCardDetailsClicked()) modelScope.launch { uiState.transformerUpdate( - transformer = DetailsRevealProgressStateTransformer(onClickHide = ::hideCardDetails), + transformer = DetailsRevealProgressStateTransformer(onClickHide = ::requestHide), ) cardDetailsRepository.revealCardDetails(params.userWalletId) .onRight { cardDetails -> uiState.transformerUpdate( transformer = DetailsRevealedStateTransformer( details = cardDetails, - onClickHide = ::hideCardDetails, + onClickHide = ::requestHide, ), ) launchShowDetailsTimer() @@ -150,11 +158,11 @@ internal class TangemPayCardDetailsBlockModel @Inject constructor( private fun launchShowDetailsTimer() { modelScope.launch { delay(SHOW_DETAILS_TIME) - hideCardDetails() + requestHide() }.saveIn(showCardDetailsTimerJobHolder) } - fun hideCardDetails() { + private fun hideCardDetails() { revealCardDetailsJobHolder.cancel() uiState.transformerUpdate(transformer = DetailsHiddenStateTransformer(stateFactory)) } 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 64c2fb2f5e..f5d80cda1e 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 @@ -12,6 +12,7 @@ import com.tangem.core.decompose.model.Model 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.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.format.bigdecimal.fiat @@ -39,6 +40,8 @@ import com.tangem.features.tangempay.components.TangemPayCardPageComponent import com.tangem.features.tangempay.components.ViewPinListener import com.tangem.features.tangempay.details.impl.R import com.tangem.features.tangempay.entity.* +import com.tangem.features.tangempay.model.listener.CardDetailsEvent +import com.tangem.features.tangempay.model.listener.CardDetailsEventListener import com.tangem.features.tangempay.navigation.TangemPayCardDetailsInnerRoute import com.tangem.features.tangempay.utils.TangemPayMessagesFactory import com.tangem.features.tangempay.utils.cryptoCurrency @@ -49,9 +52,11 @@ import com.tangem.utils.coroutines.JobHolder import com.tangem.utils.coroutines.saveIn import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import javax.inject.Inject +import com.tangem.core.ui.R as CoreUiR @Suppress("LongParameterList") @Stable @@ -65,6 +70,8 @@ internal class TangemPayCardPageModel @Inject constructor( private val cardDetailsRepository: TangemPayCardDetailsRepository, private val uiMessageSender: UiMessageSender, private val changeCardFrozenStateUseCase: ChangeCardFrozenStateUseCase, + private val designFeatureToggles: DesignFeatureToggles, + private val cardDetailsEventListener: CardDetailsEventListener, ) : Model(), ViewPinListener, ReissueCardListener, AddFundsListener { private val params: TangemPayCardPageComponent.Params = paramsContainer.require() @@ -82,6 +89,7 @@ internal class TangemPayCardPageModel @Inject constructor( onBackClick = router::pop, dailyLimitState = TangemPayDailyLimitBlockState.Loading, settings = persistentListOf(), + settingsV2 = persistentListOf(), ), ) @@ -90,6 +98,7 @@ internal class TangemPayCardPageModel @Inject constructor( init { analytics.send(TangemPayAnalyticsEvents.CardManagementScreenOpened()) fetchAddToWalletBanner() + modelScope.launch { subscribeOnDetailsState() } paymentAccountStatusSupplier.invoke(userWalletId) .onEach { state -> @@ -115,6 +124,7 @@ internal class TangemPayCardPageModel @Inject constructor( uiState.copy( dailyLimitState = dailyLimitState, settings = buildSettings(card), + settingsV2 = buildSettingsV2(card), isReissueInProgress = card.isReissuing, ) } @@ -126,6 +136,7 @@ internal class TangemPayCardPageModel @Inject constructor( } private fun buildSettings(card: TangemPayCard): ImmutableList { + if (designFeatureToggles.isRedesignEnabled) return persistentListOf() return persistentListOf( TangemPayCardPageSetting( title = TextReference.Res(R.string.tangempay_card_details_change_pin), @@ -150,6 +161,64 @@ internal class TangemPayCardPageModel @Inject constructor( ) } + private suspend fun subscribeOnDetailsState() { + if (!designFeatureToggles.isRedesignEnabled) return + cardDetailsEventListener.event.collect { event -> + val isDetailsShown = event == CardDetailsEvent.Show + uiState.update { state -> + state.copy( + settingsV2 = state.settingsV2 + .map { setting -> + if (setting.id == TangemPayCardPageSettingV2.Id.Details) { + setting.copy(isEnabled = !isDetailsShown) + } else { + setting + } + } + .toImmutableList(), + ) + } + } + } + + private fun buildSettingsV2(card: TangemPayCard): ImmutableList { + if (!designFeatureToggles.isRedesignEnabled) return persistentListOf() + return persistentListOf( + TangemPayCardPageSettingV2( + id = TangemPayCardPageSettingV2.Id.Details, + title = TextReference.Res(R.string.details_title), + onClick = ::onClickViewDetails, + iconRes = CoreUiR.drawable.ic_visa_card_details_24, + ), + TangemPayCardPageSettingV2( + id = TangemPayCardPageSettingV2.Id.Freeze, + title = TextReference.Res( + if (card.isFrozen) { + R.string.tangem_pay_freeze_card_unfreeze + } else { + R.string.tangem_pay_freeze_card_freeze + }, + ), + onClick = { onClickFreezeOrUnfreezeCard(card.isFrozen) }, + iconRes = CoreUiR.drawable.ic_freeze_24, + testTag = TangemPayTestTags.FREEZE_CARD_ROW, + ), + TangemPayCardPageSettingV2( + id = TangemPayCardPageSettingV2.Id.ChangePin, + title = TextReference.Res(R.string.tangempay_card_details_change_pin), + onClick = { onClickChangePIN(card.hasPinCode) }, + iconRes = CoreUiR.drawable.ic_card_pin_24, + testTag = TangemPayTestTags.CHANGE_PIN_ROW, + ), + ) + } + + private fun onClickViewDetails() { + modelScope.launch(dispatchers.default) { + cardDetailsEventListener.send(CardDetailsEvent.Show) + } + } + private fun onClickLimitChange() { analytics.send(TangemPayAnalyticsEvents.LimitChangeClicked()) router.push(TangemPayCardDetailsInnerRoute.LimitSetup) @@ -284,6 +353,7 @@ internal class TangemPayCardPageModel @Inject constructor( addToWalletBlockState = AddToWalletBlockState( onClick = ::onClickAddToWallet, onClickClose = ::onClickCloseBanner, + shouldUseMagicEffect = false, ), ) } diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsAddToWalletBannerTransformer.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsAddToWalletBannerTransformer.kt index 3fe7bddbf8..a1ba351878 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsAddToWalletBannerTransformer.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsAddToWalletBannerTransformer.kt @@ -15,7 +15,11 @@ internal class DetailsAddToWalletBannerTransformer( addToWalletBlockState = if (isDone) { null } else { - AddToWalletBlockState(onClick = onClickBanner, onClickClose = onClickCloseBanner) + AddToWalletBlockState( + onClick = onClickBanner, + onClickClose = onClickCloseBanner, + shouldUseMagicEffect = true, + ) }, ) } diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayAddToWalletBlock.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayAddToWalletBlock.kt index 50c72f534f..8388e6a2c4 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayAddToWalletBlock.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayAddToWalletBlock.kt @@ -18,7 +18,12 @@ import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.tangem.core.ui.ds.message.TangemMessage +import com.tangem.core.ui.ds.message.TangemMessageEffect +import com.tangem.core.ui.extensions.clickableSingle +import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.core.ui.res.LocalRedesignEnabled import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.features.tangempay.details.impl.R @@ -32,6 +37,15 @@ private const val GRADIENT_RADIUS = 200F @Composable internal fun TangemPayAddToWalletBlock(state: AddToWalletBlockState, modifier: Modifier = Modifier) { + if (LocalRedesignEnabled.current) { + TangemPayAddToWalletBlockV2(state = state, modifier = modifier) + } else { + TangemPayAddToWalletBlockV1(state = state, modifier = modifier) + } +} + +@Composable +internal fun TangemPayAddToWalletBlockV1(state: AddToWalletBlockState, modifier: Modifier = Modifier) { Box( modifier = modifier .fillMaxWidth() @@ -55,7 +69,9 @@ internal fun TangemPayAddToWalletBlock(state: AddToWalletBlockState, modifier: M Image( painter = painterResource(R.drawable.img_google_wallet_48), contentDescription = null, - modifier = Modifier.size(36.dp).clip(CircleShape), + modifier = Modifier + .size(36.dp) + .clip(CircleShape), ) Spacer(Modifier.width(12.dp)) @@ -97,11 +113,26 @@ internal fun TangemPayAddToWalletBlock(state: AddToWalletBlockState, modifier: M } } +@Composable +internal fun TangemPayAddToWalletBlockV2(state: AddToWalletBlockState, modifier: Modifier = Modifier) { + TangemMessage( + modifier = modifier.clickableSingle(onClick = state.onClick), + onCloseClick = state.onClickClose, + title = resourceReference(R.string.tangempay_card_details_open_wallet_notification_title), + subtitle = resourceReference(R.string.tangempay_card_details_open_wallet_notification_subtitle), + messageEffect = if (state.shouldUseMagicEffect) { + TangemMessageEffect.Magic + } else { + TangemMessageEffect.None + }, + ) +} + @Preview(showBackground = true) @Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable private fun PreviewTangemPayAddToWalletBlock() { TangemThemePreview { - TangemPayAddToWalletBlock(AddToWalletBlockState({}, {})) + TangemPayAddToWalletBlock(AddToWalletBlockState(onClick = {}, onClickClose = {}, shouldUseMagicEffect = false)) } } \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayCardDetailsBlock.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayCardDetailsBlock.kt index 725002b084..1098f6470e 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayCardDetailsBlock.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayCardDetailsBlock.kt @@ -1,5 +1,6 @@ package com.tangem.features.tangempay.ui +import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.EaseInOut import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.tween @@ -13,10 +14,7 @@ import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.* -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -26,9 +24,11 @@ import androidx.compose.ui.graphics.Brush 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.platform.LocalDensity import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.TextFieldValue @@ -37,18 +37,20 @@ 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.dp +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.SpacerW4 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 import com.tangem.core.ui.components.buttons.common.TangemButtonSize +import com.tangem.core.ui.ds.image.TangemIconUM +import com.tangem.core.ui.ds2.button.TangemButton import com.tangem.core.ui.extensions.conditional import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringResourceSafe -import com.tangem.core.ui.res.TangemTheme -import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.res.* import com.tangem.core.ui.test.TangemPayTestTags import com.tangem.domain.models.pay.TangemPayCardFrozenState import com.tangem.features.tangempay.details.impl.R @@ -129,23 +131,7 @@ private fun TangemPayCardDetailsHiddenBlock(state: TangemPayCardDetailsUM, modif painter = painterResource(id = imageResId), contentDescription = null, ) - - Row( - modifier = Modifier.padding(16.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Icon( - painter = painterResource(R.drawable.ic_cloud_fill_16), - tint = TangemTheme.colors.icon.constant, - contentDescription = null, - ) - SpacerW4() - Text( - text = stringResourceSafe(R.string.tangempay_digital_card), - style = TangemTheme.typography.subtitle2, - color = TangemTheme.colors.text.constantWhite, - ) - } + CardTopBlock() if (state.isActionsAvailable) { ConstraintLayout( @@ -167,18 +153,11 @@ private fun TangemPayCardDetailsHiddenBlock(state: TangemPayCardDetailsUM, modif }, ) } - - Text( - text = state.numberShort, - style = TangemTheme.typography.subtitle2, - color = TangemTheme.colors.text.constantWhite, - modifier = Modifier - .constrainAs(cardNumberRef) { - start.linkTo(parent.start) - bottom.linkTo(parent.bottom) - } - .padding(bottom = 8.dp), + CardNumberBlock( + numberShort = state.numberShort, + cardNumberRef = cardNumberRef, ) + when (state.cardFrozenState) { TangemPayCardFrozenState.Frozen -> Icon( modifier = Modifier @@ -209,23 +188,93 @@ private fun TangemPayCardDetailsHiddenBlock(state: TangemPayCardDetailsUM, modif ) TangemPayCardFrozenState.Unfrozen -> Unit } - - TangemPayCardDetailsCustomButton( + AnimatedVisibility( modifier = Modifier .constrainAs(buttonRef) { end.linkTo(parent.end) bottom.linkTo(parent.bottom) } .testTag(TangemPayTestTags.CARD_DETAILS_SHOW_BUTTON), - text = stringResourceSafe(id = R.string.tangempay_card_details_show_details), - onClick = state.onClick, - showProgress = state.isLoading, - ) + visible = !LocalRedesignEnabled.current || state.isLoading, + ) { + TangemPayCardDetailsCustomButton( + text = stringResourceSafe(id = R.string.tangempay_card_details_show_details), + onClick = state.onClick, + showProgress = state.isLoading, + ) + } } } } } +@Composable +private fun CardTopBlock(modifier: Modifier = Modifier) { + Row( + modifier = modifier.padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp), + ) { + if (LocalRedesignEnabled.current) { + Text( + text = stringResourceSafe(R.string.tangempay_digital_card), + style = TangemTheme.typography3.body.medium, + color = TangemTheme.colors3.text.staticDark.primary, + ) + Icon( + modifier = Modifier.size(TangemTheme.dimens2.x5), + imageVector = ImageVector.vectorResource(R.drawable.ic_cloud_fill_16), + tint = TangemTheme.colors3.icon.staticDark, + contentDescription = null, + ) + } else { + Icon( + painter = painterResource(R.drawable.ic_cloud_fill_16), + tint = TangemTheme.colors.icon.constant, + contentDescription = null, + ) + Text( + text = stringResourceSafe(R.string.tangempay_digital_card), + style = TangemTheme.typography.subtitle2, + color = TangemTheme.colors.text.constantWhite, + ) + } + } +} + +@Composable +private fun ConstraintLayoutScope.CardNumberBlock( + numberShort: String, + cardNumberRef: ConstrainedLayoutReference, + modifier: Modifier = Modifier, +) { + if (LocalRedesignEnabled.current) { + Text( + text = numberShort, + style = TangemTheme.typography3.body.medium, + color = TangemTheme.colors3.text.staticDark.primary, + modifier = modifier + .constrainAs(cardNumberRef) { + start.linkTo(parent.start) + bottom.linkTo(parent.bottom) + } + .padding(bottom = 8.dp), + ) + } else { + Text( + text = numberShort, + style = TangemTheme.typography.subtitle2, + color = TangemTheme.colors.text.constantWhite, + modifier = modifier + .constrainAs(cardNumberRef) { + start.linkTo(parent.start) + bottom.linkTo(parent.bottom) + } + .padding(bottom = 8.dp), + ) + } +} + @Composable private fun CardDisplayName(state: DisplayNameState, modifier: Modifier = Modifier) { when (state) { @@ -236,36 +285,67 @@ private fun CardDisplayName(state: DisplayNameState, modifier: Modifier = Modifi @Composable private fun DisplayOnlyCardDisplayName(state: DisplayNameState.Display, modifier: Modifier = Modifier) { - Row( - modifier = modifier.conditional( - condition = state.isEditingEnabled, - modifier = { clickable(onClick = state.onClick) }, - ), - horizontalArrangement = Arrangement.spacedBy(6.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - text = state.displayName, - style = TangemTheme.typography.caption1.copy(color = TangemTheme.colors.text.constantWhite), - maxLines = 1, - ) - if (state.isEditingEnabled) { - Icon( - painter = painterResource(id = R.drawable.ic_edit_new_12), - contentDescription = null, - modifier = Modifier.size(10.dp), - tint = TangemTheme.colors.text.constantWhite, + if (LocalRedesignEnabled.current) { + Row( + modifier = modifier.conditional( + condition = state.isEditingEnabled, + modifier = { clickable(onClick = state.onClick) }, + ), + horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = state.displayName, + style = TangemTheme.typography3.body.medium, + color = TangemTheme.colors3.text.staticDark.secondary, + maxLines = 1, ) + if (state.isEditingEnabled) { + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_edit_card_20), + contentDescription = null, + modifier = Modifier.size(TangemTheme.dimens2.x5), + tint = TangemTheme.colors3.icon.staticDark, + ) + } + } + } else { + Row( + modifier = modifier.conditional( + condition = state.isEditingEnabled, + modifier = { clickable(onClick = state.onClick) }, + ), + horizontalArrangement = Arrangement.spacedBy(6.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = state.displayName, + style = TangemTheme.typography.caption1.copy(color = TangemTheme.colors.text.constantWhite), + maxLines = 1, + ) + if (state.isEditingEnabled) { + Icon( + painter = painterResource(id = R.drawable.ic_edit_new_12), + contentDescription = null, + modifier = Modifier.size(10.dp), + tint = TangemTheme.colors.text.constantWhite, + ) + } } } } @Composable private fun EditingCardDisplayName(state: DisplayNameState.Editing, modifier: Modifier = Modifier) { + val isRedesignEnabled = LocalRedesignEnabled.current val focusRequester = remember { FocusRequester() } val placeholder = stringResourceSafe(R.string.tangempay_card_edit_name_placeholder) - val textStyle = TangemTheme.typography.caption1.copy(color = TangemTheme.colors.text.constantWhite) + val textStyle = if (isRedesignEnabled) { + TangemTheme.typography3.body.medium.copy(color = TangemTheme.colors3.text.staticDark.primary) + } else { + TangemTheme.typography.caption1.copy(color = TangemTheme.colors.text.constantWhite) + } val textMeasurer = rememberTextMeasurer() val measuredText = state.editingValue.text.ifEmpty { placeholder } val textWidthDp = with(LocalDensity.current) { @@ -280,7 +360,11 @@ private fun EditingCardDisplayName(state: DisplayNameState.Editing, modifier: Mo .focusRequester(focusRequester), textStyle = textStyle, singleLine = true, - cursorBrush = SolidColor(TangemTheme.colors.text.constantWhite), + cursorBrush = if (isRedesignEnabled) { + SolidColor(TangemTheme.colors3.text.staticDark.primary) + } else { + SolidColor(TangemTheme.colors.text.constantWhite) + }, keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), keyboardActions = KeyboardActions( onDone = if (state.isSubmitEnabled) { @@ -294,7 +378,13 @@ private fun EditingCardDisplayName(state: DisplayNameState.Editing, modifier: Mo if (state.editingValue.text.isEmpty()) { Text( text = placeholder, - style = textStyle.copy(color = TangemTheme.colors.text.tertiary), + style = textStyle.copy( + color = if (isRedesignEnabled) { + TangemTheme.colors3.text.staticDark.secondary + } else { + TangemTheme.colors.text.tertiary + }, + ), ) } innerTextField() @@ -317,10 +407,7 @@ private fun TangemPayCardDetailsShownBlock( onHideDetails: () -> Unit, modifier: Modifier = Modifier, ) { - Column( - modifier = modifier - .fillMaxSize(), - ) { + Column(modifier = modifier.fillMaxSize()) { CardDetailsTextContainer( modifier = Modifier .padding(top = 16.dp, bottom = 12.dp, start = 16.dp, end = 16.dp) @@ -361,14 +448,31 @@ private fun TangemPayCardDetailsShownBlock( Spacer(modifier = Modifier.weight(1f)) Row { SpacerWMax() - TangemPayCardDetailsCustomButton( - modifier = Modifier - .padding(end = 16.dp, bottom = 8.dp) - .testTag(TangemPayTestTags.CARD_DETAILS_HIDE_BUTTON), - text = stringResourceSafe(id = R.string.tangempay_card_details_hide_details), - onClick = onHideDetails, - showProgress = false, - ) + if (LocalRedesignEnabled.current) { + // Must use dark theme locally for button cause card is dark + CompositionLocalProvider(LocalIsInDarkTheme provides true) { + TangemThemeRedesign { + TangemButton( + modifier = Modifier + .padding(end = 16.dp, bottom = 8.dp) + .testTag(TangemPayTestTags.CARD_DETAILS_HIDE_BUTTON), + variant = TangemButton.Variant.Material, + size = TangemButton.Size.X8, + onClick = onHideDetails, + iconStart = TangemIconUM.Icon(iconRes = R.drawable.ic_close_20), + ) + } + } + } else { + TangemPayCardDetailsCustomButton( + modifier = Modifier + .padding(end = 16.dp, bottom = 8.dp) + .testTag(TangemPayTestTags.CARD_DETAILS_HIDE_BUTTON), + text = stringResourceSafe(id = R.string.tangempay_card_details_hide_details), + onClick = onHideDetails, + showProgress = false, + ) + } } } } 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 d2ed4eb835..6b2374624b 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 @@ -16,6 +16,7 @@ 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.ui.Alignment import androidx.compose.ui.Modifier @@ -26,8 +27,10 @@ import androidx.compose.ui.util.fastForEach import com.tangem.core.ui.components.appbar.AppBarWithBackButton import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.core.ui.res.LocalRedesignEnabled import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.res.TangemThemePreviewRedesign import com.tangem.domain.models.pay.TangemPayCardFrozenState import com.tangem.features.tangempay.components.cardDetails.PreviewTangemPayCardDetailsBlockComponent import com.tangem.features.tangempay.components.cardDetails.TangemPayCardDetailsBlockComponent @@ -44,6 +47,7 @@ internal fun TangemPayCardPageScreen( cardDetailsState: TangemPayCardDetailsUM, modifier: Modifier = Modifier, ) { + val isRedesignEnabled = LocalRedesignEnabled.current Scaffold( modifier = modifier, topBar = { @@ -53,7 +57,11 @@ internal fun TangemPayCardPageScreen( ) }, contentWindowInsets = ScaffoldDefaults.contentWindowInsets.exclude(WindowInsets.navigationBars), - containerColor = TangemTheme.colors.background.secondary, + containerColor = if (isRedesignEnabled) { + TangemTheme.colors3.bg.primary + } else { + TangemTheme.colors.background.secondary + }, ) { scaffoldPaddings -> val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() } LazyColumn( @@ -73,6 +81,14 @@ internal fun TangemPayCardPageScreen( state = cardDetailsState, ) } + if (isRedesignEnabled && state.settingsV2.isNotEmpty()) { + cardPageItem("Settings buttons") { + TangemPayCardPageSettingsButtonsBlock( + modifier = Modifier.fillMaxWidth(), + settings = state.settingsV2, + ) + } + } if (state.isReissueInProgress) { cardPageItem(key = "Reissue") { TangemPayReplacingCardBlock() @@ -104,6 +120,7 @@ private fun TangemPayCardPageSettingsBlock( settings: ImmutableList, modifier: Modifier = Modifier, ) { + if (LocalRedesignEnabled.current) return Column( modifier = modifier .fillMaxWidth() @@ -176,11 +193,27 @@ private fun LazyListScope.cardPageItem( @Preview @Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable -private fun preview() = TangemThemePreview { - TangemPayCardPageScreen( - state = TangemPayCardPageUM.stub(), - cardDetailsBlockComponent = PreviewTangemPayCardDetailsBlockComponent( - TangemPayCardDetailsUM( +private fun TangemPayCardPageScreenPreviewV1() { + TangemThemePreview { + TangemPayCardPageScreen( + state = TangemPayCardPageUM.stub(), + cardDetailsBlockComponent = PreviewTangemPayCardDetailsBlockComponent( + TangemPayCardDetailsUM( + number = "•••• •••• •••• 1245", + numberShort = "··1245", + expiry = "••/••", + cvv = "•••", + onCopy = { _, _ -> }, + onClick = {}, + cardFrozenState = TangemPayCardFrozenState.Unfrozen, + displayNameState = DisplayNameState.Display( + displayName = "Tangem Pay Card", + onClick = {}, + isEditingEnabled = true, + ), + ), + ), + cardDetailsState = TangemPayCardDetailsUM( number = "•••• •••• •••• 1245", numberShort = "··1245", expiry = "••/••", @@ -191,23 +224,52 @@ private fun preview() = TangemThemePreview { displayNameState = DisplayNameState.Display( displayName = "Tangem Pay Card", onClick = {}, - isEditingEnabled = true, + isEditingEnabled = false, ), ), - ), - cardDetailsState = TangemPayCardDetailsUM( - number = "•••• •••• •••• 1245", - numberShort = "··1245", - expiry = "••/••", - cvv = "•••", - onCopy = { _, _ -> }, - onClick = {}, - cardFrozenState = TangemPayCardFrozenState.Unfrozen, - displayNameState = DisplayNameState.Display( - displayName = "Tangem Pay Card", - onClick = {}, - isEditingEnabled = false, - ), - ), - ) + ) + } +} + +@Preview +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun TangemPayCardPageScreenPreviewV2() { + TangemThemePreviewRedesign { + CompositionLocalProvider(LocalRedesignEnabled provides true) { + TangemPayCardPageScreen( + state = TangemPayCardPageUM.stub(), + cardDetailsBlockComponent = PreviewTangemPayCardDetailsBlockComponent( + TangemPayCardDetailsUM( + number = "•••• •••• •••• 1245", + numberShort = "··1245", + expiry = "••/••", + cvv = "•••", + onCopy = { _, _ -> }, + onClick = {}, + cardFrozenState = TangemPayCardFrozenState.Unfrozen, + displayNameState = DisplayNameState.Display( + displayName = "Tangem Pay Card", + onClick = {}, + isEditingEnabled = true, + ), + ), + ), + cardDetailsState = TangemPayCardDetailsUM( + number = "•••• •••• •••• 1245", + numberShort = "··1245", + expiry = "••/••", + cvv = "•••", + onCopy = { _, _ -> }, + onClick = {}, + cardFrozenState = TangemPayCardFrozenState.Unfrozen, + displayNameState = DisplayNameState.Display( + displayName = "Tangem Pay Card", + onClick = {}, + isEditingEnabled = false, + ), + ), + ) + } + } } \ No newline at end of file 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 new file mode 100644 index 0000000000..ce36ac6ad5 --- /dev/null +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayCardPageSettingsButtonsBlock.kt @@ -0,0 +1,94 @@ +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.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 kotlinx.collections.immutable.ImmutableList + +@Composable +internal fun TangemPayCardPageSettingsButtonsBlock( + settings: ImmutableList, + modifier: Modifier = Modifier, +) { + if (settings.isEmpty()) return + Row( + modifier = modifier.padding(vertical = TangemTheme.dimens2.x6), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + ) { + settings.fastForEach { setting -> + TangemPaySettingButton( + setting = setting, + modifier = Modifier.then(if (setting.testTag != null) Modifier.testTag(setting.testTag) else Modifier), + ) + } + } +} + +@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 +private fun TangemPayCardPageSettingsButtonsBlockPreview() { + TangemThemePreviewRedesign { + TangemPayCardPageSettingsButtonsBlock( + modifier = Modifier + .fillMaxWidth() + .background(TangemTheme.colors3.bg.secondary), + settings = TangemPayCardPageSettingV2.stubList(), + ) + } +} + +@Preview(showBackground = true, name = "Frozen card") +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, name = "Frozen card — dark") +@Composable +private fun TangemPayCardPageSettingsButtonsBlockFrozenPreview() { + TangemThemePreviewRedesign { + TangemPayCardPageSettingsButtonsBlock( + modifier = Modifier + .fillMaxWidth() + .background(TangemTheme.colors3.bg.secondary), + settings = TangemPayCardPageSettingV2.stubList(isFrozen = true), + ) + } +} \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDailyLimitBlock.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDailyLimitBlock.kt index b24d7d31bb..e81d92a24c 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDailyLimitBlock.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDailyLimitBlock.kt @@ -4,6 +4,7 @@ import android.content.res.Configuration 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.LocalMinimumInteractiveComponentSize import androidx.compose.material3.Text @@ -11,7 +12,11 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.layout.layoutId import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -19,15 +24,31 @@ import com.tangem.core.ui.components.* import com.tangem.core.ui.components.buttons.common.TangemButtonSize import com.tangem.core.ui.components.notifications.Notification import com.tangem.core.ui.components.notifications.NotificationConfig +import com.tangem.core.ui.ds.row.TangemRowContainer +import com.tangem.core.ui.ds.row.TangemRowLayoutId +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.resourceReference import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.core.ui.res.LocalRedesignEnabled import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.res.TangemThemePreviewRedesign import com.tangem.features.tangempay.details.impl.R import com.tangem.features.tangempay.entity.TangemPayDailyLimitBlockState @Composable internal fun TangemPayDailyLimitBlock(state: TangemPayDailyLimitBlockState, modifier: Modifier = Modifier) { + if (LocalRedesignEnabled.current) { + CurrentLimitBlockV2(state, modifier) + } else { + TangemPayDailyLimitBlockV1(state, modifier) + } +} + +@Composable +private fun TangemPayDailyLimitBlockV1(state: TangemPayDailyLimitBlockState, modifier: Modifier = Modifier) { Column( modifier = modifier .fillMaxWidth() @@ -43,12 +64,12 @@ internal fun TangemPayDailyLimitBlock(state: TangemPayDailyLimitBlockState, modi color = TangemTheme.colors.text.tertiary, ) SpacerH4() - CurrentLimitBlock(state) + CurrentLimitBlockV1(state) } } @Composable -private fun CurrentLimitBlock(state: TangemPayDailyLimitBlockState) { +private fun CurrentLimitBlockV1(state: TangemPayDailyLimitBlockState) { Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, @@ -107,8 +128,147 @@ private fun CurrentLimitBlock(state: TangemPayDailyLimitBlockState) { } } +@Composable +private fun CurrentLimitBlockV2(state: TangemPayDailyLimitBlockState, modifier: Modifier = Modifier) { + TangemRowContainer( + modifier = modifier + .clip(RoundedCornerShape(TangemTheme.dimens2.x6)) + .background(color = TangemTheme.colors3.bg.secondary), + contentPadding = PaddingValues(TangemTheme.dimens2.x4), + ) { + LimitHeadIcon( + modifier = Modifier.layoutId(TangemRowLayoutId.HEAD), + state = state, + ) + + TitleLimit( + modifier = Modifier + .padding(start = TangemTheme.dimens2.x3) + .layoutId(TangemRowLayoutId.START_TOP), + state = state, + ) + + SubtitleLimit( + modifier = Modifier + .padding(start = TangemTheme.dimens2.x3) + .layoutId(TangemRowLayoutId.START_BOTTOM), + state = state, + ) + + if (state is TangemPayDailyLimitBlockState.Content) { + CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) { + TangemButton( + modifier = Modifier + .padding(start = TangemTheme.dimens2.x3) + .layoutId(TangemRowLayoutId.TAIL), + variant = TangemButton.Variant.Secondary, + text = resourceReference(R.string.common_edit), + onClick = state.onChangeClick, + size = TangemButton.Size.X10, + ) + } + } + } +} + +@Composable +private fun LimitHeadIcon(state: TangemPayDailyLimitBlockState, modifier: Modifier = Modifier) { + Box( + modifier = modifier + .size(TangemTheme.dimens2.x10) + .background( + color = when (state) { + is TangemPayDailyLimitBlockState.Content, + TangemPayDailyLimitBlockState.Loading, + -> TangemTheme.colors3.bg.status.infoSubtle + TangemPayDailyLimitBlockState.Error -> TangemTheme.colors3.bg.status.warningSubtle + }, + shape = CircleShape, + ), + contentAlignment = Alignment.Center, + ) { + when (state) { + is TangemPayDailyLimitBlockState.Content, + TangemPayDailyLimitBlockState.Loading, + -> { + Icon( + imageVector = ImageVector.vectorResource(R.drawable.ic_limit_new_20), + contentDescription = null, + tint = TangemTheme.colors3.icon.brand, + ) + } + TangemPayDailyLimitBlockState.Error -> { + Icon( + imageVector = ImageVector.vectorResource(R.drawable.ic_warning_20), + contentDescription = null, + tint = TangemTheme.colors3.icon.status.warning, + ) + } + } + } +} + +@Composable +private fun TitleLimit(state: TangemPayDailyLimitBlockState, modifier: Modifier = Modifier) { + when (state) { + is TangemPayDailyLimitBlockState.Content, + TangemPayDailyLimitBlockState.Loading, + -> { + Text( + modifier = modifier, + text = stringResourceSafe(R.string.tangempay_card_page_daily_limit_title), + style = TangemTheme.typography3.body.medium, + color = TangemTheme.colors3.text.secondary, + ) + } + TangemPayDailyLimitBlockState.Error -> { + Text( + modifier = modifier, + text = stringResourceSafe(R.string.tangempay_card_page_daily_limit_error_title), + style = TangemTheme.typography3.body.medium, + color = TangemTheme.colors3.text.primary, + ) + } + } +} + +@Composable +private fun SubtitleLimit(state: TangemPayDailyLimitBlockState, modifier: Modifier = Modifier) { + when (state) { + TangemPayDailyLimitBlockState.Error -> { + Text( + modifier = modifier, + text = stringResourceSafe(R.string.tangempay_card_page_daily_limit_error_subtitle), + style = TangemTheme.typography3.body.medium, + color = TangemTheme.colors3.text.secondary, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + } + is TangemPayDailyLimitBlockState.Content -> { + Text( + modifier = modifier, + text = state.limit, + style = TangemTheme.typography3.body.medium, + color = TangemTheme.colors3.text.primary, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + } + TangemPayDailyLimitBlockState.Loading -> { + TextShimmer( + radius = TangemTheme.dimens2.x25, + modifier = modifier, + style = TextShimmerStyle.BODY, + text = "$50,000", + ) + } + } +} + @Composable internal fun TangemPayDailyLimitErrorBlock(modifier: Modifier = Modifier) { + if (LocalRedesignEnabled.current) return Notification( config = NotificationConfig( title = resourceReference(R.string.tangempay_card_page_daily_limit_error_title), @@ -123,13 +283,33 @@ internal fun TangemPayDailyLimitErrorBlock(modifier: Modifier = Modifier) { @Preview @Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable -private fun preview() = TangemThemePreview { - Column( - verticalArrangement = Arrangement.spacedBy(16.dp), - ) { - TangemPayDailyLimitBlock(state = TangemPayDailyLimitBlockState.Content.stub()) - TangemPayDailyLimitBlock(state = TangemPayDailyLimitBlockState.Error) - TangemPayDailyLimitBlock(state = TangemPayDailyLimitBlockState.Loading) - TangemPayDailyLimitErrorBlock() +private fun Preview() { + TangemThemePreview { + Column( + verticalArrangement = Arrangement.spacedBy(16.dp), + ) { + TangemPayDailyLimitBlock(state = TangemPayDailyLimitBlockState.Content.stub()) + TangemPayDailyLimitBlock(state = TangemPayDailyLimitBlockState.Error) + TangemPayDailyLimitBlock(state = TangemPayDailyLimitBlockState.Loading) + TangemPayDailyLimitErrorBlock() + } + } +} + +@Preview +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun PreviewV2() { + TangemThemePreviewRedesign { + CompositionLocalProvider(LocalRedesignEnabled provides true) { + Column( + verticalArrangement = Arrangement.spacedBy(16.dp), + ) { + TangemPayDailyLimitBlock(state = TangemPayDailyLimitBlockState.Content.stub()) + TangemPayDailyLimitBlock(state = TangemPayDailyLimitBlockState.Error) + TangemPayDailyLimitBlock(state = TangemPayDailyLimitBlockState.Loading) + TangemPayDailyLimitErrorBlock() + } + } } } \ No newline at end of file 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 0ffd7f0888..55d6159978 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 @@ -457,7 +457,11 @@ private class TangemPayDetailsUMProvider : CollectionPreviewParameterProvider