Updated on 2026-08-14

This commit is contained in:
Tangem 2025-03-27 12:51:16 +03:00
parent 918ab4613f
commit 819a80a444
16 changed files with 414 additions and 48 deletions

View file

@ -20,6 +20,7 @@ import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
import com.tangem.sdk.api.TangemSdkManager
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Settings
@ -55,6 +56,7 @@ internal class CardSettingsModel @Inject constructor(
private val getUserWalletUseCase: GetUserWalletUseCase,
private val cardSdkConfigRepository: CardSdkConfigRepository,
private val settingsRepository: SettingsRepository,
private val onboardingV2FeatureToggles: OnboardingV2FeatureToggles,
) : Model() {
private val params = paramsContainer.require<CardSettingsComponent.Params>()
@ -198,13 +200,24 @@ internal class CardSettingsModel @Inject constructor(
}
if (scanResponse.cardTypesResolver.isTangemTwins()) {
// needs to prepare twin state if it's twin cards (depends on onboarding refactoring)
store.dispatch(TwinCardsAction.IfTwinsPrepareState(scanResponse))
store.dispatch(TwinCardsAction.SetMode(CreateTwinWalletMode.RecreateWallet(scanResponse)))
if (onboardingV2FeatureToggles.isTwinRefactoringEnabled) {
store.dispatchNavigationAction {
push(
AppRoute.Onboarding(
scanResponse = scanResponse,
mode = AppRoute.Onboarding.Mode.RecreateWalletTwin,
),
)
}
} else {
// needs to prepare twin state if it's twin cards (depends on onboarding refactoring)
store.dispatch(TwinCardsAction.IfTwinsPrepareState(scanResponse))
store.dispatch(TwinCardsAction.SetMode(CreateTwinWalletMode.RecreateWallet(scanResponse)))
cardSettingsInteractor.clear()
cardSettingsInteractor.clear()
store.dispatchNavigationAction { push(AppRoute.OnboardingTwins) }
store.dispatchNavigationAction { push(AppRoute.OnboardingTwins) }
}
} else {
val card = scanResponse.card

View file

@ -140,9 +140,9 @@ private fun ConditionCheckBox(checkedState: Boolean, onCheckedChange: (Boolean)
Icon(
painter = painterResource(
if (checked) {
R.drawable.ic_accepted
R.drawable.ic_accepted_20
} else {
R.drawable.ic_unticked
R.drawable.ic_unticked_20
},
),
contentDescription = null,

View file

@ -9,7 +9,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/access_code_info_protect_margin_top"
android:src="@drawable/ic_access_code"
android:src="@drawable/ic_access_code_119"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"

View file

@ -22,9 +22,9 @@ object TangemAnimations {
forwardWhen: (initial: S, target: S) -> Boolean,
): AnimatedContentTransitionScope<S>.() -> ContentTransform = {
val direction = if (forwardWhen(initialState, targetState)) {
AnimatedContentTransitionScope.SlideDirection.End
} else {
AnimatedContentTransitionScope.SlideDirection.Start
} else {
AnimatedContentTransitionScope.SlideDirection.End
}
slideIntoContainer(towards = direction, animationSpec = tween())

View file

@ -36,7 +36,7 @@ private fun Content(state: MultiWalletAccessCodeUM, modifier: Modifier = Modifie
modifier = Modifier.weight(1f),
targetState = state.step,
transitionSpec = TangemAnimations.AnimatedContent
.slide { initial, target -> initial.ordinal > target.ordinal },
.slide { initial, target -> target.ordinal > initial.ordinal },
label = "AnimatedContent",
) { step ->
when (step) {

View file

@ -31,7 +31,7 @@ internal fun MultiWalletAccessCodeIntro(modifier: Modifier = Modifier) {
color = TangemTheme.colors.text.primary1,
)
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_access_code),
imageVector = ImageVector.vectorResource(R.drawable.ic_access_code_119),
tint = TangemTheme.colors.icon.informative,
contentDescription = null,
)

View file

@ -12,7 +12,7 @@ internal fun MultiWalletSeedPhrase(state: MultiWalletSeedPhraseUM, modifier: Mod
AnimatedContent(
modifier = modifier.navigationBarsPadding(),
targetState = state,
transitionSpec = TangemAnimations.AnimatedContent.slide { from, to -> to.order < from.order },
transitionSpec = TangemAnimations.AnimatedContent.slide { from, to -> to.order > from.order },
contentKey = { st -> st::class.java },
label = "animatedContent",
) { st ->

View file

@ -3,20 +3,27 @@ package com.tangem.features.onboarding.v2.twin.impl
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.decompose.navigation.inner.InnerNavigation
import com.tangem.core.decompose.navigation.inner.InnerNavigationHolder
import com.tangem.core.decompose.navigation.inner.InnerNavigationState
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.features.onboarding.v2.impl.R
import com.tangem.features.onboarding.v2.twin.api.OnboardingTwinComponent
import com.tangem.features.onboarding.v2.twin.impl.model.OnboardingTwinModel
import com.tangem.features.onboarding.v2.twin.impl.ui.OnboardingTwin
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.StateFlow
internal class DefaultOnboardingTwinComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
@Assisted params: OnboardingTwinComponent.Params,
) : OnboardingTwinComponent, AppComponentContext by appComponentContext {
) : OnboardingTwinComponent, AppComponentContext by appComponentContext, InnerNavigationHolder {
private val model: OnboardingTwinModel = getOrCreateModel(params)
@ -24,11 +31,30 @@ internal class DefaultOnboardingTwinComponent @AssistedInject constructor(
params.titleProvider.changeTitle(resourceReference(R.string.twins_recreate_toolbar))
}
override val innerNavigation: InnerNavigation = object : InnerNavigation {
override val state: StateFlow<TwinInnerNavigationState> = model.innerNavigationState
override fun pop(onComplete: (Boolean) -> Unit) {
model.onBack()
}
}
@Composable
override fun Content(modifier: Modifier) {
BackHandler { model.onBack() }
// TODO
val state by model.uiState.collectAsStateWithLifecycle()
OnboardingTwin(
state = state,
modifier = modifier,
)
}
data class TwinInnerNavigationState(
override val stackSize: Int,
) : InnerNavigationState {
override val stackMaxSize: Int? = 5
}
@AssistedFactory

View file

@ -19,7 +19,6 @@ import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBotto
import com.tangem.core.ui.components.bottomsheets.tokenreceive.mapToAddressModels
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.toWrappedList
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.datasource.local.config.issuers.IssuersConfigStorage
@ -44,6 +43,7 @@ import com.tangem.features.onboarding.v2.common.ui.interruptBackupDialog
import com.tangem.features.onboarding.v2.impl.R
import com.tangem.features.onboarding.v2.twin.api.OnboardingTwinComponent
import com.tangem.features.onboarding.v2.twin.api.OnboardingTwinComponent.Params.Mode
import com.tangem.features.onboarding.v2.twin.impl.DefaultOnboardingTwinComponent
import com.tangem.features.onboarding.v2.twin.impl.ui.TwinWalletArtworkUM
import com.tangem.features.onboarding.v2.twin.impl.ui.state.OnboardingTwinUM
import com.tangem.sdk.api.TangemSdkManager
@ -58,6 +58,7 @@ import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import timber.log.Timber
import java.math.BigDecimal
import javax.inject.Inject
@Suppress("LongParameterList", "LargeClass")
@ -87,7 +88,10 @@ internal class OnboardingTwinModel @Inject constructor(
private val _uiState = MutableStateFlow(
when (params.mode) {
Mode.WelcomeOnly -> {
OnboardingTwinUM.Welcome(onContinueClick = ::saveWalletAndDone)
OnboardingTwinUM.Welcome(
pairCardNumber = firstCardTwinNumber.pairNumber().number,
onContinueClick = ::saveWalletAndDone,
)
}
Mode.RecreateWallet -> {
OnboardingTwinUM.ResetWarning(
@ -100,12 +104,16 @@ internal class OnboardingTwinModel @Inject constructor(
)
}
Mode.CreateWallet -> {
OnboardingTwinUM.Welcome(onContinueClick = ::navigateToFirstScan)
OnboardingTwinUM.Welcome(
pairCardNumber = firstCardTwinNumber.pairNumber().number,
onContinueClick = ::navigateToFirstScan,
)
}
},
)
val uiState = _uiState.asStateFlow()
val innerNavigationState = MutableStateFlow(DefaultOnboardingTwinComponent.TwinInnerNavigationState(1))
init {
if (_uiState.value is OnboardingTwinUM.Welcome) {
@ -144,15 +152,15 @@ internal class OnboardingTwinModel @Inject constructor(
}
private fun createFirstWallet() {
setLoading(true)
modelScope.launch {
setLoading(false)
val result = tangemSdkManager.createFirstTwinWallet(
cardId = params.scanResponse.card.cardId,
initialMessage = Message(
tangemSdkManager.getString(
R.string.twins_recreate_title_format,
wrappedList(firstCardTwinNumber.number),
firstCardTwinNumber.number,
),
),
)
@ -183,6 +191,10 @@ internal class OnboardingTwinModel @Inject constructor(
},
)
}
innerNavigationState.update {
it.copy(stackSize = 2)
}
}
}
}
@ -206,7 +218,7 @@ internal class OnboardingTwinModel @Inject constructor(
initialMessage = Message(
tangemSdkManager.getString(
R.string.twins_recreate_title_format,
wrappedList(secondCardNumber),
secondCardNumber,
),
),
)
@ -230,14 +242,16 @@ internal class OnboardingTwinModel @Inject constructor(
},
)
}
innerNavigationState.update {
it.copy(stackSize = 3)
}
}
}
}
}
private fun createThirdWallet(secondCardPublicKey: ByteArray) {
setLoading(true)
modelScope.launch {
val result = tangemSdkManager.finalizeTwin(
secondCardPublicKey = secondCardPublicKey,
@ -246,7 +260,7 @@ internal class OnboardingTwinModel @Inject constructor(
initialMessage = Message(
tangemSdkManager.getString(
R.string.twins_recreate_title_format,
wrappedList(firstCardTwinNumber.number),
firstCardTwinNumber.number,
),
),
)
@ -276,15 +290,21 @@ internal class OnboardingTwinModel @Inject constructor(
return@launch
}
fetchCurrencyStatusUseCase(
userWalletsListManager.save(userWallet, canOverride = true)
fetchCurrencyStatusUseCase.invoke(
userWalletId = userWallet.walletId,
refresh = true,
)
).onLeft {
Timber.e("Unable to fetch currency status: $it")
setLoading(false)
}
val cryptoCurrencyStatus =
getPrimaryCurrencyStatusUpdatesUseCase.invoke(userWallet.walletId).firstOrNull()?.getOrNull()
?: run {
setLoading(false)
Timber.e("Unable to get currency status")
return@launch
}
@ -303,20 +323,28 @@ internal class OnboardingTwinModel @Inject constructor(
onShowAddressClick = { onShowAddressClick(cryptoCurrencyStatus) },
isLoading = true,
)
innerNavigationState.update {
it.copy(stackSize = 4)
}
}.saveIn(cryptoCurrencyStatusJobHolder)
}
}
}
private fun applyCryptoCurrencyStatusToState(status: CryptoCurrencyStatus) {
update<OnboardingTwinUM.TopUp> {
it.copy(
balance = (status.value as? CryptoCurrencyStatus.Loaded)?.amount
?.format { crypto(status.currency) } ?: "",
onBuyCryptoClick = { onBuyCryptoClick(status) },
onShowAddressClick = { onShowAddressClick(status) },
isLoading = false,
)
val amount = (status.value as? CryptoCurrencyStatus.Loaded)?.amount ?: return
if (amount > BigDecimal.ZERO) {
params.modelCallbacks.onDone()
} else {
update<OnboardingTwinUM.TopUp> {
it.copy(
balance = BigDecimal.ZERO.format { crypto(status.currency) },
onBuyCryptoClick = { onBuyCryptoClick(status) },
onShowAddressClick = { onShowAddressClick(status) },
isLoading = false,
)
}
}
}
@ -386,7 +414,6 @@ internal class OnboardingTwinModel @Inject constructor(
}
private fun showCardVerificationFailedDialog(error: TangemError) {
setLoading(false)
if (error !is TangemSdkError.CardVerificationFailed) return
analyticsEventHandler.send(OnboardingEvent.OfflineAttestationFailed(AnalyticsParam.ScreensSources.Onboarding))

View file

@ -1,20 +1,68 @@
package com.tangem.features.onboarding.v2.twin.impl.ui
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Text
import androidx.compose.material3.Icon
import androidx.compose.material3.IconToggleButton
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.PrimaryButton
import com.tangem.core.ui.components.PrimaryButtonIconEnd
import com.tangem.core.ui.components.SecondaryButton
import com.tangem.core.ui.components.SpacerH16
import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheet
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemAnimations
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.onboarding.v2.impl.R
import com.tangem.features.onboarding.v2.twin.impl.ui.state.OnboardingTwinUM
@Suppress("UnusedPrivateMember")
@Suppress("UnusedPrivateMember", "MagicNumber")
@Composable
internal fun OnboardingTwin(state: OnboardingTwinUM, modifier: Modifier = Modifier) {
Column(modifier = modifier) {
Box {
// TODO
Column(
modifier = modifier
.fillMaxSize()
.navigationBarsPadding(),
) {
TwinWalletArtworks(
modifier = Modifier
.padding(top = 64.dp)
.padding(horizontal = 24.dp)
.weight(.48f)
.fillMaxWidth(),
state = state.artwork,
balance = (state as? OnboardingTwinUM.TopUp)?.balance ?: "",
isRefreshing = state.isLoading,
onRefreshBalanceClick = {
(state as? OnboardingTwinUM.TopUp)?.onRefreshClick()
},
)
AnimatedContent(
modifier = Modifier.weight(.52f),
targetState = state,
transitionSpec = TangemAnimations.AnimatedContent
.slide { initial, target -> target.stepIndex > initial.stepIndex },
contentKey = { it::class },
) { st ->
when (st) {
is OnboardingTwinUM.ResetWarning -> ResetWarning(st)
is OnboardingTwinUM.ScanCard -> ScanCard(st)
is OnboardingTwinUM.TopUp -> TopUp(st)
is OnboardingTwinUM.Welcome -> Welcome(st)
}
}
}
@ -23,10 +71,256 @@ internal fun OnboardingTwin(state: OnboardingTwinUM, modifier: Modifier = Modifi
}
}
@Preview
@Suppress("LongMethod")
@Composable
private fun Preview() {
private fun ResetWarning(state: OnboardingTwinUM.ResetWarning, modifier: Modifier = Modifier) {
Column(
modifier = modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Column(
modifier = Modifier
.padding(start = 32.dp, end = 32.dp, bottom = 16.dp)
.weight(1f)
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Text(
text = stringResourceSafe(R.string.common_warning),
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
style = TangemTheme.typography.h2,
)
SpacerH16()
Text(
text = stringResourceSafe(R.string.twins_recreate_warning),
color = TangemTheme.colors.text.secondary,
textAlign = TextAlign.Center,
style = TangemTheme.typography.body1,
)
SpacerH16()
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
onClick = { state.onAcceptClick(!state.acceptToggle) },
),
) {
IconToggleButton(checked = state.acceptToggle, onCheckedChange = state.onAcceptClick) {
AnimatedContent(targetState = state.acceptToggle, label = "Update checked state") { checked ->
Icon(
painter = painterResource(
if (checked) {
R.drawable.ic_accepted_20
} else {
R.drawable.ic_unticked_20
},
),
contentDescription = null,
tint = if (checked) {
TangemTheme.colors.control.checked
} else {
TangemTheme.colors.icon.secondary
},
)
}
}
Text(
text = stringResourceSafe(R.string.common_understand),
color = TangemTheme.colors.text.secondary,
textAlign = TextAlign.Center,
style = TangemTheme.typography.body1,
)
}
}
PrimaryButton(
modifier = Modifier
.padding(start = 16.dp, end = 16.dp, bottom = 12.dp)
.fillMaxWidth(),
text = stringResourceSafe(R.string.common_continue),
enabled = state.acceptToggle,
onClick = state.onContinueClick,
)
}
}
@Composable
private fun TopUp(state: OnboardingTwinUM.TopUp, modifier: Modifier = Modifier) {
Column(
modifier = modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Column(
modifier = Modifier
.padding(start = 32.dp, end = 32.dp, bottom = 16.dp)
.weight(1f)
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Text(
text = stringResourceSafe(R.string.onboarding_topup_title),
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
style = TangemTheme.typography.h2,
)
SpacerH16()
Text(
text = stringResourceSafe(R.string.onboarding_top_up_body),
color = TangemTheme.colors.text.secondary,
textAlign = TextAlign.Center,
style = TangemTheme.typography.body1,
)
}
PrimaryButton(
modifier = Modifier
.padding(start = 16.dp, end = 16.dp, bottom = 12.dp)
.fillMaxWidth(),
text = stringResourceSafe(R.string.onboarding_top_up_button_but_crypto),
onClick = state.onBuyCryptoClick,
)
SecondaryButton(
modifier = Modifier
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
.fillMaxWidth(),
text = stringResourceSafe(R.string.onboarding_top_up_button_show_wallet_address),
onClick = state.onShowAddressClick,
)
}
}
@Composable
private fun ScanCard(state: OnboardingTwinUM.ScanCard, modifier: Modifier = Modifier) {
Column(
modifier = modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Column(
modifier = Modifier
.padding(start = 32.dp, end = 32.dp, bottom = 16.dp)
.weight(1f)
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Text(
text = stringResourceSafe(R.string.twins_recreate_title_format, state.cardNumber),
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
style = TangemTheme.typography.h2,
)
SpacerH16()
Text(
text = stringResourceSafe(R.string.onboarding_twins_interrupt_warning),
color = TangemTheme.colors.text.secondary,
textAlign = TextAlign.Center,
style = TangemTheme.typography.body1,
)
}
PrimaryButtonIconEnd(
modifier = Modifier
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
.fillMaxWidth(),
text = stringResourceSafe(R.string.onboarding_button_backup_card_format, state.cardNumber),
showProgress = state.isLoading,
iconResId = R.drawable.ic_tangem_24,
onClick = state.onScanClick,
)
}
}
@Composable
private fun Welcome(state: OnboardingTwinUM.Welcome, modifier: Modifier = Modifier) {
Column(
modifier = modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Column(
modifier = Modifier
.padding(start = 32.dp, end = 32.dp, bottom = 16.dp)
.weight(1f)
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Text(
text = stringResourceSafe(R.string.twins_onboarding_subtitle),
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
style = TangemTheme.typography.h2,
)
SpacerH16()
Text(
text = stringResourceSafe(R.string.twins_onboarding_description_format, state.pairCardNumber),
color = TangemTheme.colors.text.secondary,
textAlign = TextAlign.Center,
style = TangemTheme.typography.body1,
)
}
PrimaryButton(
modifier = Modifier
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
.fillMaxWidth(),
text = stringResourceSafe(R.string.common_continue),
onClick = state.onContinueClick,
)
}
}
@Preview(showBackground = true)
@Composable
private fun PreviewTopUp() {
TangemThemePreview {
OnboardingTwin(OnboardingTwinUM.TopUp())
}
}
@Preview(showBackground = true)
@Composable
private fun PreviewWelcome() {
TangemThemePreview {
OnboardingTwin(OnboardingTwinUM.Welcome())
}
}
@Preview(showBackground = true)
@Composable
private fun PreviewScanCard() {
TangemThemePreview {
OnboardingTwin(OnboardingTwinUM.ScanCard())
}
}
@Preview(showBackground = true)
@Composable
private fun PreviewResetWarning() {
TangemThemePreview {
var toggle by remember { mutableStateOf(false) }
OnboardingTwin(
OnboardingTwinUM.ResetWarning(
acceptToggle = toggle,
onAcceptClick = { toggle = it },
),
)
}
}

View file

@ -116,7 +116,7 @@ internal fun TwinWalletArtworks(
) {
Box(
modifier = Modifier
.padding(vertical = 24.dp, horizontal = 32.dp)
.padding(vertical = 24.dp, horizontal = 16.dp)
.fillMaxSize()
.background(
TangemTheme.colors.button.secondary,

View file

@ -13,6 +13,7 @@ internal sealed class OnboardingTwinUM {
data class Welcome(
override val isLoading: Boolean = false,
val pairCardNumber: Int = 2,
val onContinueClick: () -> Unit = {},
) : OnboardingTwinUM() {
override val stepIndex = 0
@ -31,7 +32,7 @@ internal sealed class OnboardingTwinUM {
data class ScanCard(
override val isLoading: Boolean = false,
val artworkStep: TwinWalletArtworkUM.Leapfrog.Step,
val artworkStep: TwinWalletArtworkUM.Leapfrog.Step = TwinWalletArtworkUM.Leapfrog.Step.FirstCard,
val step: Step = Step.First,
val onScanClick: () -> Unit = {},
) : OnboardingTwinUM() {
@ -40,6 +41,11 @@ internal sealed class OnboardingTwinUM {
First, Second, Third
}
val cardNumber: Int = when (artworkStep) {
TwinWalletArtworkUM.Leapfrog.Step.FirstCard -> 1
TwinWalletArtworkUM.Leapfrog.Step.SecondCard -> 2
}
override val stepIndex: Int = 1
override val artwork: TwinWalletArtworkUM = TwinWalletArtworkUM.Leapfrog(artworkStep)
}

View file

@ -44,7 +44,7 @@ internal fun OnboardingVisaAccessCode(state: OnboardingVisaAccessCodeUM, modifie
.weight(1f),
targetState = state.step,
transitionSpec = TangemAnimations.AnimatedContent
.slide { initial, target -> initial.ordinal > target.ordinal },
.slide { initial, target -> target.ordinal > initial.ordinal },
) { step ->
Content(
state = state,