diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index 87d1ef2075..e41b7491e8 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -1876,6 +1876,8 @@
Service temporarily unavailable
The service is currently unreachable. Please try again later.
Set \nPIN code
+ Set up new PIN
+ Set PIN
Account closed
Replacing your card
Use your card or ring to renew session
@@ -1888,7 +1890,7 @@
Send USDC Polygon to your account’s address
From another wallet or exchange
Use crypto from your wallet to top up your payment account
- Swap from Tangem Wallet
+ From your Tangem Wallet
USDC on Polygon network
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
diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayCardPageComponent.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayCardPageComponent.kt
index 44ded62254..ccae6d4b0d 100644
--- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayCardPageComponent.kt
+++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayCardPageComponent.kt
@@ -17,6 +17,7 @@ import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.decompose.navigation.inner.InnerRouter
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.models.account.AccountStatus
+import com.tangem.features.tangempay.TangemPayFeatureToggles
import com.tangem.features.tangempay.limit.setup.TangemPayCardLimitSetupComponent
import com.tangem.features.tangempay.limit.setup.TangemPayCardLimitSetupSuccessComponent
import com.tangem.features.tangempay.navigation.TangemPayCardDetailsInnerRoute
@@ -29,6 +30,7 @@ internal class TangemPayCardPageComponent @AssistedInject constructor(
@Assisted private val appComponentContext: AppComponentContext,
@Assisted private val params: Params,
private val tokenReceiveComponentFactory: TokenReceiveComponent.Factory,
+ private val tangemPayFeatureToggles: TangemPayFeatureToggles,
) : ComposableContentComponent, AppComponentContext by appComponentContext {
private val stackNavigation = StackNavigation()
@@ -72,7 +74,11 @@ internal class TangemPayCardPageComponent @AssistedInject constructor(
params = TangemPayDetailsContainerComponent.Params(initialStatus = params.initialStatus),
)
TangemPayCardDetailsInnerRoute.ChangePINSuccess -> TangemPayChangePinSuccessComponent(
- appComponentContext = childByContext(componentContext = componentContext, router = innerRouter),
+ appComponentContext = childByContext(
+ componentContext = componentContext,
+ router = innerRouter,
+ ),
+ isRedesignEnabled = tangemPayFeatureToggles.isRedesignEnabled,
)
TangemPayCardDetailsInnerRoute.AddToWallet -> TangemPayAddToWalletComponent(
appComponentContext = childByContext(componentContext = componentContext, router = innerRouter),
diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayChangePinComponent.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayChangePinComponent.kt
index 7b3ecc0c87..220fb0ccd2 100644
--- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayChangePinComponent.kt
+++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayChangePinComponent.kt
@@ -11,6 +11,7 @@ import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.security.DisableScreenshotsDisposableEffect
import com.tangem.features.tangempay.model.TangemPayChangePinModel
import com.tangem.features.tangempay.ui.TangemPayChangePinScreen
+import com.tangem.features.tangempay.ui.TangemPayChangePinScreenV2
internal class TangemPayChangePinComponent(
private val appComponentContext: AppComponentContext,
@@ -24,9 +25,16 @@ internal class TangemPayChangePinComponent(
val state by model.uiState.collectAsStateWithLifecycle()
BackHandler(onBack = router::pop)
DisableScreenshotsDisposableEffect()
- TangemPayChangePinScreen(
- state = state,
- onBackClick = router::pop,
- )
+ if (model.isRedesignEnabled()) {
+ TangemPayChangePinScreenV2(
+ state = state,
+ onBackClick = router::pop,
+ )
+ } else {
+ TangemPayChangePinScreen(
+ state = state,
+ onBackClick = router::pop,
+ )
+ }
}
}
\ No newline at end of file
diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayChangePinSuccessComponent.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayChangePinSuccessComponent.kt
index eccf167f98..292202be67 100644
--- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayChangePinSuccessComponent.kt
+++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayChangePinSuccessComponent.kt
@@ -7,15 +7,21 @@ import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.features.tangempay.navigation.TangemPayCardDetailsInnerRoute
import com.tangem.features.tangempay.ui.TangemPayChangePinCodeSuccessScreen
+import com.tangem.features.tangempay.ui.TangemPayChangePinCodeSuccessScreenV2
internal class TangemPayChangePinSuccessComponent(
private val appComponentContext: AppComponentContext,
+ private val isRedesignEnabled: Boolean,
) : AppComponentContext by appComponentContext, ComposableContentComponent {
@Composable
override fun Content(modifier: Modifier) {
BackHandler(onBack = ::backToDetails)
- TangemPayChangePinCodeSuccessScreen(onClick = ::backToDetails)
+ if (isRedesignEnabled) {
+ TangemPayChangePinCodeSuccessScreenV2(onClose = ::backToDetails)
+ } else {
+ TangemPayChangePinCodeSuccessScreen(onClick = ::backToDetails)
+ }
}
private fun backToDetails() {
diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayViewPinComponent.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayViewPinComponent.kt
index f12eb316e9..48f67173fe 100644
--- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayViewPinComponent.kt
+++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayViewPinComponent.kt
@@ -11,6 +11,7 @@ import com.tangem.core.ui.security.DisableScreenshotsDisposableEffect
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.features.tangempay.model.TangemPayViewPinModel
import com.tangem.features.tangempay.ui.TangemPayViewPinContent
+import com.tangem.features.tangempay.ui.TangemPayViewPinContentV2
internal class TangemPayViewPinComponent(
appComponentContext: AppComponentContext,
@@ -28,7 +29,11 @@ internal class TangemPayViewPinComponent(
val state by model.uiState.collectAsStateWithLifecycle()
BackHandler(onBack = ::dismiss)
DisableScreenshotsDisposableEffect()
- TangemPayViewPinContent(state = state)
+ if (model.isRedesignEnabled()) {
+ TangemPayViewPinContentV2(state = state)
+ } else {
+ TangemPayViewPinContent(state = state)
+ }
}
data class Params(
diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayChangePinModel.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayChangePinModel.kt
index 4194b5f69d..f8d8643db7 100644
--- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayChangePinModel.kt
+++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayChangePinModel.kt
@@ -12,6 +12,7 @@ import com.tangem.core.ui.message.ToastMessage
import com.tangem.domain.pay.model.SetPinResult
import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
+import com.tangem.features.tangempay.TangemPayFeatureToggles
import com.tangem.features.tangempay.components.TangemPayDetailsContainerComponent
import com.tangem.features.tangempay.details.impl.R
import com.tangem.features.tangempay.entity.TangemPayChangePinUM
@@ -27,6 +28,7 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject
+@Suppress("LongParameterList")
@Stable
@ModelScoped
internal class TangemPayChangePinModel @Inject constructor(
@@ -36,6 +38,7 @@ internal class TangemPayChangePinModel @Inject constructor(
private val router: Router,
private val cardDetailsRepository: TangemPayCardDetailsRepository,
private val analytics: AnalyticsEventHandler,
+ private val featureToggles: TangemPayFeatureToggles,
) : Model() {
private val params: TangemPayDetailsContainerComponent.Params = paramsContainer.require()
@@ -46,8 +49,15 @@ internal class TangemPayChangePinModel @Inject constructor(
analytics.send(TangemPayAnalyticsEvents.ChangePinScreenShown())
}
+ fun isRedesignEnabled(): Boolean = featureToggles.isRedesignEnabled
+
private fun onPinCodeChange(pin: String) {
uiState.update(transformer = PinCodeChangeTransformer(newPin = pin))
+ val state = uiState.value
+ // In the redesign there is no submit button: a valid full PIN is submitted automatically.
+ if (featureToggles.isRedesignEnabled && state.submitButtonEnabled && !state.submitButtonLoading) {
+ onClickSubmit()
+ }
}
private fun onClickSubmit() {
diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayViewPinModel.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayViewPinModel.kt
index a610d66541..9b02ad1ed5 100644
--- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayViewPinModel.kt
+++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayViewPinModel.kt
@@ -7,6 +7,7 @@ import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
+import com.tangem.features.tangempay.TangemPayFeatureToggles
import com.tangem.features.tangempay.components.TangemPayViewPinComponent
import com.tangem.features.tangempay.entity.TangemPayViewPinUM
import com.tangem.features.tangempay.model.transformers.TangemPayViewPinErrorStateTransformer
@@ -25,6 +26,7 @@ internal class TangemPayViewPinModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val cardDetailsRepository: TangemPayCardDetailsRepository,
private val analytics: AnalyticsEventHandler,
+ private val featureToggles: TangemPayFeatureToggles,
) : Model() {
private val params = paramsContainer.require()
@@ -37,6 +39,8 @@ internal class TangemPayViewPinModel @Inject constructor(
getCardPin()
}
+ fun isRedesignEnabled(): Boolean = featureToggles.isRedesignEnabled
+
private fun getCardPin() {
modelScope.launch {
cardDetailsRepository.getPin(userWalletId = params.walletId, cardId = params.cardId)
diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayChangePinCodeSuccessScreenV2.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayChangePinCodeSuccessScreenV2.kt
new file mode 100644
index 0000000000..6f0c1d5c40
--- /dev/null
+++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayChangePinCodeSuccessScreenV2.kt
@@ -0,0 +1,98 @@
+package com.tangem.features.tangempay.ui
+
+import androidx.compose.foundation.layout.*
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.blur
+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.platform.testTag
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.tangem.core.ui.components.SpacerH
+import com.tangem.core.ui.components.SpacerHMax
+import com.tangem.core.ui.ds2.button.TangemButton
+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.TangemThemePreviewRedesign
+import com.tangem.core.ui.res.generated.icons.Icons
+import com.tangem.core.ui.res.generated.icons.ic_success_24
+import com.tangem.core.ui.test.TangemPayTestTags
+import com.tangem.features.tangempay.details.impl.R
+
+private const val BG_YELLOW_COLOR = 0x52DFAF12
+
+@Suppress("MagicNumber")
+@Composable
+internal fun TangemPayChangePinCodeSuccessScreenV2(onClose: () -> Unit, modifier: Modifier = Modifier) {
+ Box(modifier = modifier.fillMaxSize()) {
+ Box(
+ modifier = Modifier
+ .matchParentSize()
+ .blur(192.dp)
+ .drawBehind {
+ val w = size.width
+ drawRect(
+ brush = Brush.radialGradient(
+ colors = listOf(
+ Color(BG_YELLOW_COLOR),
+ Color.Transparent,
+ ),
+ center = Offset(w / 2f, -w * .1f),
+ radius = w + w * .2f,
+ tileMode = TileMode.Clamp,
+ ),
+ )
+ },
+ )
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .windowInsetsPadding(WindowInsets.systemBars)
+ .padding(top = 72.dp, start = 24.dp, end = 24.dp),
+ ) {
+ Icon(
+ modifier = Modifier.size(28.dp),
+ imageVector = Icons.ic_success_24,
+ tint = TangemTheme.colors3.icon.primary,
+ contentDescription = null,
+ )
+ SpacerH(TangemTheme.dimens2.x4)
+ Text(
+ modifier = Modifier.testTag(TangemPayTestTags.PIN_SUCCESS_TITLE),
+ text = stringResourceSafe(R.string.tangempay_card_details_change_pin_success_title),
+ style = TangemTheme.typography3.heading.medium,
+ color = TangemTheme.colors3.text.primary,
+ )
+ Text(
+ modifier = Modifier.testTag(TangemPayTestTags.PIN_SUCCESS_DESCRIPTION),
+ text = stringResourceSafe(R.string.tangempay_card_details_change_pin_success_description),
+ style = TangemTheme.typography3.heading.medium,
+ color = TangemTheme.colors3.text.secondary,
+ )
+ SpacerHMax()
+ TangemButton(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(vertical = TangemTheme.dimens2.x3),
+ onClick = onClose,
+ size = TangemButton.Size.X12,
+ text = resourceReference(R.string.common_close),
+ )
+ }
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun Preview() {
+ TangemThemePreviewRedesign {
+ TangemPayChangePinCodeSuccessScreenV2(onClose = {})
+ }
+}
\ No newline at end of file
diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayChangePinScreenV2.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayChangePinScreenV2.kt
new file mode 100644
index 0000000000..26c4737f33
--- /dev/null
+++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayChangePinScreenV2.kt
@@ -0,0 +1,239 @@
+package com.tangem.features.tangempay.ui
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.KeyboardActions
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.graphics.Color.Companion.Transparent
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.platform.LocalSoftwareKeyboardController
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.tooling.preview.PreviewParameter
+import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
+import com.tangem.core.ui.components.SpacerH
+import com.tangem.core.ui.components.SpacerH4
+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.ds2.loader.TangemLoader
+import com.tangem.core.ui.extensions.resolveReference
+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.generated.icons.Icons
+import com.tangem.core.ui.res.generated.icons.ic_cross_20
+import com.tangem.core.ui.test.TangemPayTestTags
+import com.tangem.features.tangempay.details.impl.R
+import com.tangem.features.tangempay.entity.TangemPayChangePinUM
+import kotlinx.coroutines.delay
+
+@Composable
+internal fun TangemPayChangePinScreenV2(
+ state: TangemPayChangePinUM,
+ onBackClick: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ Column(
+ modifier = modifier
+ .fillMaxSize()
+ .statusBarsPadding(),
+ ) {
+ TangemTopBar(
+ title = resourceReference(R.string.tangempay_set_pin_title),
+ endContent = {
+ TangemButton(
+ iconStart = TangemIconUM.Icon(imageVector = Icons.ic_cross_20),
+ onClick = onBackClick,
+ size = TangemButton.Size.X11,
+ variant = TangemButton.Variant.Material,
+ )
+ },
+ )
+ SpacerH(TangemTheme.dimens2.x4)
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(top = TangemTheme.dimens2.x12)
+ .padding(horizontal = TangemTheme.dimens2.x9),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Text(
+ text = stringResourceSafe(R.string.tangempay_set_pin_header),
+ style = TangemTheme.typography3.body.medium,
+ color = TangemTheme.colors3.text.secondary,
+ textAlign = TextAlign.Center,
+ modifier = Modifier.testTag(TangemPayTestTags.PIN_SCREEN_TITLE),
+ )
+ SpacerH(TangemTheme.dimens2.x6)
+ PinCodeSection(state)
+ SpacerH(TangemTheme.dimens2.x6)
+ AnimatedVisibility(state.submitButtonLoading) {
+ TangemLoader()
+ }
+ }
+ }
+}
+
+@Composable
+private fun PinCodeSection(state: TangemPayChangePinUM, modifier: Modifier = Modifier) {
+ val focusRequester = remember { FocusRequester() }
+ Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier) {
+ PinCode(
+ value = state.pinCode,
+ onValueChange = state.onPinCodeChange,
+ focusRequester = focusRequester,
+ readOnly = state.submitButtonLoading,
+ )
+
+ AnimatedVisibility(
+ visible = state.error != null,
+ ) {
+ val error = remember(this) { requireNotNull(state.error) }
+ Column {
+ SpacerH4()
+ Text(
+ text = error.resolveReference(),
+ style = TangemTheme.typography3.caption.medium,
+ color = TangemTheme.colors3.text.status.warning,
+ textAlign = TextAlign.Center,
+ modifier = Modifier.testTag(TangemPayTestTags.PIN_ERROR_MESSAGE),
+ )
+ }
+ }
+ }
+
+ LaunchedEffect(Unit) {
+ delay(timeMillis = 300)
+ focusRequester.requestFocus()
+ }
+}
+
+@Composable
+private fun PinCode(
+ value: String,
+ onValueChange: (String) -> Unit,
+ modifier: Modifier = Modifier,
+ numbersCount: Int = 4,
+ readOnly: Boolean = false,
+ focusRequester: FocusRequester = remember { FocusRequester() },
+) {
+ val keyboardController = LocalSoftwareKeyboardController.current
+
+ // Hide the keyboard while the PIN is being submitted so it can't be edited mid-request.
+ LaunchedEffect(readOnly) {
+ if (readOnly) keyboardController?.hide()
+ }
+
+ BasicTextField(
+ value = value,
+ onValueChange = { text ->
+ if (text.length <= numbersCount && !readOnly) {
+ onValueChange(text)
+ }
+ },
+ readOnly = readOnly,
+ modifier = modifier
+ .focusRequester(focusRequester)
+ .clickable(enabled = !readOnly) {
+ focusRequester.requestFocus()
+ keyboardController?.show()
+ }
+ .testTag(TangemPayTestTags.PIN_INPUT_FIELD),
+ textStyle = TangemTheme.typography3.heading.medium.copy(color = Transparent),
+ keyboardOptions = KeyboardOptions(
+ keyboardType = KeyboardType.NumberPassword,
+ imeAction = ImeAction.Done,
+ ),
+ keyboardActions = KeyboardActions(onDone = { keyboardController?.hide() }),
+ cursorBrush = SolidColor(Transparent),
+ decorationBox = { innerTextField ->
+ Box(
+ modifier = Modifier.fillMaxWidth(),
+ contentAlignment = Alignment.Center,
+ ) {
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ repeat(numbersCount) { index ->
+ val digit = value.getOrNull(index)?.toString()
+ val isActive = !readOnly && index == value.length
+ PinDigitBox(
+ modifier = Modifier.size(
+ width = TangemTheme.dimens2.x14,
+ height = TangemTheme.dimens2.x16,
+ ),
+ digit = digit,
+ backgroundColor = TangemTheme.colors3.bg.opaque.primary,
+ borderColor = if (isActive) {
+ TangemTheme.colors3.border.status.info
+ } else {
+ TangemTheme.colors3.border.secondary
+ },
+ textColor = TangemTheme.colors3.text.primary,
+ textStyle = TangemTheme.typography3.heading.medium,
+ )
+ }
+ }
+ innerTextField()
+ }
+ },
+ )
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun TangemPayChangePinScreenV2Preview(
+ @PreviewParameter(TangemPayChangePinUMPreviewProvider::class) state: TangemPayChangePinUM,
+) {
+ TangemThemePreview {
+ TangemPayChangePinScreenV2(
+ state = state,
+ onBackClick = {},
+ )
+ }
+}
+
+private class TangemPayChangePinUMPreviewProvider : CollectionPreviewParameterProvider(
+ collection = listOf(
+ TangemPayChangePinUM(
+ pinCode = "",
+ error = null,
+ onPinCodeChange = {},
+ submitButtonLoading = false,
+ submitButtonEnabled = false,
+ onSubmitClick = {},
+ ),
+ TangemPayChangePinUM(
+ pinCode = "1111",
+ error = resourceReference(R.string.visa_onboarding_pin_validation_error_message),
+ onPinCodeChange = {},
+ submitButtonLoading = false,
+ submitButtonEnabled = false,
+ onSubmitClick = {},
+ ),
+ TangemPayChangePinUM(
+ pinCode = "2580",
+ error = null,
+ onPinCodeChange = {},
+ submitButtonLoading = false,
+ submitButtonEnabled = true,
+ onSubmitClick = {},
+ ),
+ ),
+)
\ No newline at end of file
diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayViewPinContentV2.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayViewPinContentV2.kt
new file mode 100644
index 0000000000..b8d79d40e1
--- /dev/null
+++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayViewPinContentV2.kt
@@ -0,0 +1,221 @@
+package com.tangem.features.tangempay.ui
+
+import android.content.res.Configuration
+import androidx.compose.animation.AnimatedContent
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.togetherWith
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color.Companion.Transparent
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import com.tangem.core.ui.components.SpacerH
+import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
+import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
+import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
+import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetType
+import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetContent
+import com.tangem.core.ui.ds.image.TangemIconUM
+import com.tangem.core.ui.ds.topbar.TangemTopBar
+import com.tangem.core.ui.ds.topbar.TangemTopBarType
+import com.tangem.core.ui.ds2.button.TangemButton
+import com.tangem.core.ui.ds2.loader.TangemLoader
+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.TangemThemePreviewRedesign
+import com.tangem.features.tangempay.details.impl.R
+import com.tangem.features.tangempay.entity.TangemPayViewPinUM
+
+@Composable
+internal fun TangemPayViewPinContentV2(state: TangemPayViewPinUM) {
+ TangemBottomSheet(
+ config = TangemBottomSheetConfig(
+ isShown = true,
+ onDismissRequest = state.onDismiss,
+ content = TangemBottomSheetConfigContent.Empty,
+ ),
+ onBack = state.onDismiss,
+ type = TangemBottomSheetType.Modal,
+ containerColor = TangemTheme.colors3.bg.secondary,
+ title = {
+ TangemTopBar(
+ type = TangemTopBarType.BottomSheet,
+ endContent = {
+ TangemButton(
+ iconStart = TangemIconUM.Icon(iconRes = R.drawable.ic_close_24),
+ onClick = state.onDismiss,
+ size = TangemButton.Size.X11,
+ variant = TangemButton.Variant.Material,
+ )
+ },
+ )
+ },
+ content = {
+ AnimatedContent(
+ targetState = state,
+ contentKey = { um -> um::class.java },
+ transitionSpec = { fadeIn() togetherWith fadeOut() },
+ ) { animatedState ->
+ when (animatedState) {
+ is TangemPayViewPinUM.Content -> {
+ PinSuccessContent(animatedState)
+ }
+ is TangemPayViewPinUM.Error -> {
+ MessageBottomSheetContent(animatedState.errorMessage)
+ }
+ is TangemPayViewPinUM.Loading -> {
+ PinLoadingContent()
+ }
+ }
+ }
+ },
+ )
+}
+
+@Composable
+private fun PinSuccessContent(state: TangemPayViewPinUM.Content, modifier: Modifier = Modifier) {
+ Column(
+ modifier = modifier
+ .padding(horizontal = TangemTheme.dimens2.x4)
+ .padding(top = TangemTheme.dimens2.x12)
+ .fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Text(
+ text = stringResourceSafe(R.string.tangempay_card_details_view_pin_code_title),
+ style = TangemTheme.typography3.heading.small,
+ color = TangemTheme.colors3.text.primary,
+ textAlign = TextAlign.Center,
+ )
+
+ SpacerH(TangemTheme.dimens2.x2)
+
+ Text(
+ text = stringResourceSafe(R.string.tangempay_card_details_view_pin_code_description),
+ style = TangemTheme.typography3.subheading.medium,
+ color = TangemTheme.colors3.text.secondary,
+ textAlign = TextAlign.Center,
+ )
+
+ SpacerH(TangemTheme.dimens2.x8)
+
+ PinCode(value = state.pin)
+
+ SpacerH(TangemTheme.dimens2.x12)
+
+ TangemButton(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(vertical = TangemTheme.dimens2.x4),
+ size = TangemButton.Size.X12,
+ text = resourceReference(R.string.tangempay_change_pin_code),
+ onClick = state.onClickChangePin,
+ )
+ }
+}
+
+@Composable
+private fun PinLoadingContent(modifier: Modifier = Modifier) {
+ Column(
+ modifier = modifier
+ .padding(horizontal = TangemTheme.dimens2.x4)
+ .padding(top = TangemTheme.dimens2.x12)
+ .fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Text(
+ text = stringResourceSafe(R.string.tangempay_card_details_view_pin_code_title),
+ style = TangemTheme.typography3.heading.small,
+ color = TangemTheme.colors3.text.primary,
+ textAlign = TextAlign.Center,
+ )
+
+ SpacerH(TangemTheme.dimens2.x2)
+
+ Text(
+ text = stringResourceSafe(R.string.tangempay_card_details_view_pin_code_description),
+ style = TangemTheme.typography3.subheading.medium,
+ color = TangemTheme.colors3.text.secondary,
+ textAlign = TextAlign.Center,
+ )
+
+ SpacerH(TangemTheme.dimens2.x8)
+
+ TangemLoader(
+ modifier = Modifier.padding(vertical = TangemTheme.dimens2.x5),
+ )
+
+ SpacerH(TangemTheme.dimens2.x12)
+
+ TangemButton(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(vertical = TangemTheme.dimens2.x4),
+ size = TangemButton.Size.X12,
+ text = resourceReference(R.string.tangempay_change_pin_code),
+ onClick = {},
+ isEnabled = false,
+ )
+ }
+}
+
+@Composable
+private fun PinCode(value: String, modifier: Modifier = Modifier, numbersCount: Int = 4) {
+ BasicTextField(
+ enabled = false,
+ value = value,
+ onValueChange = {},
+ modifier = modifier,
+ textStyle = TangemTheme.typography3.heading.medium.copy(color = Transparent),
+ cursorBrush = SolidColor(Transparent),
+ decorationBox = { innerTextField ->
+ Box(
+ modifier = Modifier.fillMaxWidth(),
+ contentAlignment = Alignment.Center,
+ ) {
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ repeat(numbersCount) { index ->
+ val digit = value.getOrNull(index)?.toString()
+ PinDigitBox(
+ modifier = Modifier.size(
+ width = TangemTheme.dimens2.x14,
+ height = TangemTheme.dimens2.x16,
+ ),
+ digit = digit,
+ backgroundColor = TangemTheme.colors3.bg.opaque.primary,
+ borderColor = TangemTheme.colors3.border.secondary,
+ textColor = TangemTheme.colors3.text.primary,
+ textStyle = TangemTheme.typography3.heading.medium,
+ )
+ }
+ }
+ innerTextField()
+ }
+ },
+ )
+}
+
+@Preview(showBackground = true)
+@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun TangemPayViewPinContentPreview() {
+ TangemThemePreviewRedesign {
+ TangemPayViewPinContentV2(
+ state = TangemPayViewPinUM.Content(
+ pin = "1234",
+ onClickChangePin = {},
+ onDismiss = {},
+ ),
+ )
+ }
+}
\ No newline at end of file