Updated on 2026-08-14
This commit is contained in:
parent
8ede48416d
commit
c637c9a194
10 changed files with 607 additions and 8 deletions
|
|
@ -1876,6 +1876,8 @@
|
|||
<string name="tangempay_service_unavailable_title">Service temporarily unavailable</string>
|
||||
<string name="tangempay_service_unreachable_try_later">The service is currently unreachable. Please try again later.</string>
|
||||
<string name="tangempay_set_pin_code">Set \nPIN code</string>
|
||||
<string name="tangempay_set_pin_header">Set up new PIN</string>
|
||||
<string name="tangempay_set_pin_title">Set PIN</string>
|
||||
<string name="tangempay_status_deactivated">Account closed</string>
|
||||
<string name="tangempay_status_replacing">Replacing your card</string>
|
||||
<string name="tangempay_sync_needed">Use your card or ring to renew session</string>
|
||||
|
|
@ -1888,7 +1890,7 @@
|
|||
<string name="tangempay_topup_receive_body">Send USDC Polygon to your account’s address </string>
|
||||
<string name="tangempay_topup_receive_title">From another wallet or exchange</string>
|
||||
<string name="tangempay_topup_swap_body">Use crypto from your wallet to top up your payment account</string>
|
||||
<string name="tangempay_topup_swap_title">Swap from Tangem Wallet</string>
|
||||
<string name="tangempay_topup_swap_title">From your Tangem Wallet</string>
|
||||
<string name="tangempay_usdc_on_polygon_network">USDC on Polygon network</string>
|
||||
<string name="tangempay_withdrawal_note_description">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</string>
|
||||
<string name="tangempay_withdrawal_note_title">Please note</string>
|
||||
|
|
|
|||
|
|
@ -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<TangemPayCardDetailsInnerRoute>()
|
||||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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<TangemPayViewPinComponent.Params>()
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 = {})
|
||||
}
|
||||
}
|
||||
|
|
@ -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<TangemPayChangePinUM>(
|
||||
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 = {},
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
@ -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<TangemBottomSheetConfigContent.Empty>(
|
||||
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 = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue