diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/model/CardSettingsModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/model/CardSettingsModel.kt index 773aa32d80..894ab1be67 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/model/CardSettingsModel.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/model/CardSettingsModel.kt @@ -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() @@ -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 diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreen.kt b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreen.kt index f68ae0c367..dd3093deb9 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreen.kt @@ -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, diff --git a/app/src/main/res/layout/layout_backup_access_code_info.xml b/app/src/main/res/layout/layout_backup_access_code_info.xml index c53bb8fc3d..5095c1eca0 100644 --- a/app/src/main/res/layout/layout_backup_access_code_info.xml +++ b/app/src/main/res/layout/layout_backup_access_code_info.xml @@ -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" diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemAnimations.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemAnimations.kt index 28d531895d..b1ca7da16a 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemAnimations.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemAnimations.kt @@ -22,9 +22,9 @@ object TangemAnimations { forwardWhen: (initial: S, target: S) -> Boolean, ): AnimatedContentTransitionScope.() -> ContentTransform = { val direction = if (forwardWhen(initialState, targetState)) { - AnimatedContentTransitionScope.SlideDirection.End - } else { AnimatedContentTransitionScope.SlideDirection.Start + } else { + AnimatedContentTransitionScope.SlideDirection.End } slideIntoContainer(towards = direction, animationSpec = tween()) diff --git a/app/src/main/res/drawable/ic_accepted.xml b/core/ui/src/main/res/drawable/ic_accepted_20.xml similarity index 100% rename from app/src/main/res/drawable/ic_accepted.xml rename to core/ui/src/main/res/drawable/ic_accepted_20.xml diff --git a/core/ui/src/main/res/drawable/ic_access_code.xml b/core/ui/src/main/res/drawable/ic_access_code_119.xml similarity index 100% rename from core/ui/src/main/res/drawable/ic_access_code.xml rename to core/ui/src/main/res/drawable/ic_access_code_119.xml diff --git a/app/src/main/res/drawable/ic_unticked.xml b/core/ui/src/main/res/drawable/ic_unticked_20.xml similarity index 100% rename from app/src/main/res/drawable/ic_unticked.xml rename to core/ui/src/main/res/drawable/ic_unticked_20.xml diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/accesscode/ui/MultiWalletAccessCodeBS.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/accesscode/ui/MultiWalletAccessCodeBS.kt index 8ac18706c6..2e7c8e89c6 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/accesscode/ui/MultiWalletAccessCodeBS.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/accesscode/ui/MultiWalletAccessCodeBS.kt @@ -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) { diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/accesscode/ui/MultiWalletAccessCodeIntro.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/accesscode/ui/MultiWalletAccessCodeIntro.kt index 3f0fe0f290..2e6166a337 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/accesscode/ui/MultiWalletAccessCodeIntro.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/accesscode/ui/MultiWalletAccessCodeIntro.kt @@ -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, ) diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/seedphrase/ui/MultiWalletSeedPhrase.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/seedphrase/ui/MultiWalletSeedPhrase.kt index 3ac5e6a920..985a7b68e4 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/seedphrase/ui/MultiWalletSeedPhrase.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/seedphrase/ui/MultiWalletSeedPhrase.kt @@ -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 -> diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/DefaultOnboardingTwinComponent.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/DefaultOnboardingTwinComponent.kt index f8130f2d71..ded3238c83 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/DefaultOnboardingTwinComponent.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/DefaultOnboardingTwinComponent.kt @@ -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 = 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 diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/model/OnboardingTwinModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/model/OnboardingTwinModel.kt index ae6846a3ec..1fb3811bcf 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/model/OnboardingTwinModel.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/model/OnboardingTwinModel.kt @@ -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 { - 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 { + 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)) diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/ui/OnboardingTwin.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/ui/OnboardingTwin.kt index 5ea7351de4..c4535a2f95 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/ui/OnboardingTwin.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/ui/OnboardingTwin.kt @@ -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 }, + ), + ) + } } \ No newline at end of file diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/ui/TwinWalletArtwork.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/ui/TwinWalletArtwork.kt index 2326dd841b..5fba3214f8 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/ui/TwinWalletArtwork.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/ui/TwinWalletArtwork.kt @@ -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, diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/ui/state/OnboardingTwinUM.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/ui/state/OnboardingTwinUM.kt index 88bd97bcbc..89ffc6efc6 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/ui/state/OnboardingTwinUM.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/ui/state/OnboardingTwinUM.kt @@ -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) } diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/accesscode/ui/OnboardingVisaAccessCode.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/accesscode/ui/OnboardingVisaAccessCode.kt index d80ef6209b..090ce03d84 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/accesscode/ui/OnboardingVisaAccessCode.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/accesscode/ui/OnboardingVisaAccessCode.kt @@ -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,