From e2b5e5b07b107e18c853bb7f0f99f95cd4035150 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sat, 9 Nov 2024 08:43:41 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../routing/impl/DefaultRoutingComponent.kt | 2 +- .../wallets/usecase/GetCardImageUseCase.kt | 4 +- features/onboarding-v2/impl/build.gradle.kts | 1 + .../DefaultOnboardingMultiWalletComponent.kt | 21 +- .../impl/child/backup/ui/MultiWalletBackup.kt | 20 +- .../impl/child/backup/ui/Wallet1Artwork.kt | 21 - .../ui/MultiWalletCreateWallet.kt | 22 +- .../v2/multiwallet/impl/model/Dialogs.kt | 16 - .../impl/model/OnboardingMultiWalletModel.kt | 25 +- .../impl/ui/OnboardingMultiWallet.kt | 78 ++- .../v2/multiwallet/impl/ui/WalletArtwork.kt | 462 ++++++++++++++++++ .../impl/ui/state/OnboardingMultiWalletUM.kt | 20 +- 12 files changed, 539 insertions(+), 153 deletions(-) delete mode 100644 features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/backup/ui/Wallet1Artwork.kt delete mode 100644 features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/model/Dialogs.kt create mode 100644 features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/ui/WalletArtwork.kt diff --git a/app/src/main/java/com/tangem/tap/routing/impl/DefaultRoutingComponent.kt b/app/src/main/java/com/tangem/tap/routing/impl/DefaultRoutingComponent.kt index c3cd524d98..77283b4c75 100644 --- a/app/src/main/java/com/tangem/tap/routing/impl/DefaultRoutingComponent.kt +++ b/app/src/main/java/com/tangem/tap/routing/impl/DefaultRoutingComponent.kt @@ -27,7 +27,7 @@ internal class DefaultRoutingComponent @AssistedInject constructor( override val stack: Value> = childStack( source = navigationProvider.getOrCreateTyped(), - serializer = AppRoute.serializer(), + serializer = AppRoute.serializer(), // TODO Maybe set this to null for AppRoute.Onboarding case. Need to check initialConfiguration = getInitialRoute(), handleBackButton = false, childFactory = ::child, diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetCardImageUseCase.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetCardImageUseCase.kt index 3d92875a5b..67080c0682 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetCardImageUseCase.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetCardImageUseCase.kt @@ -38,6 +38,8 @@ class GetCardImageUseCase(private val verifier: OnlineCardVerifier = OnlineCardV } } + fun getDefaultFallbackUrl(): String = Artwork.DEFAULT_IMG_URL + private fun getFallbackArtworkUrl(cardId: String): String { return when { cardId.startsWith(Artwork.SERGIO_CARD_ID) -> Artwork.SERGIO_CARD_URL @@ -45,7 +47,7 @@ class GetCardImageUseCase(private val verifier: OnlineCardVerifier = OnlineCardV else -> when (TwinsHelper.getTwinCardNumber(cardId)) { TwinCardNumber.First -> Artwork.TWIN_CARD_1_URL TwinCardNumber.Second -> Artwork.TWIN_CARD_2_URL - else -> Artwork.DEFAULT_IMG_URL + else -> getDefaultFallbackUrl() } } } diff --git a/features/onboarding-v2/impl/build.gradle.kts b/features/onboarding-v2/impl/build.gradle.kts index ba4fb5017f..fac172d9d1 100644 --- a/features/onboarding-v2/impl/build.gradle.kts +++ b/features/onboarding-v2/impl/build.gradle.kts @@ -29,6 +29,7 @@ dependencies { implementation(projects.domain.feedback) implementation(projects.domain.core) implementation(projects.domain.card) + implementation(projects.domain.wallets) /** Tangem libraries */ implementation(projects.libs.tangemSdkApi) diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/DefaultOnboardingMultiWalletComponent.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/DefaultOnboardingMultiWalletComponent.kt index 9b8676533f..846e1f0d53 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/DefaultOnboardingMultiWalletComponent.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/DefaultOnboardingMultiWalletComponent.kt @@ -3,6 +3,7 @@ package com.tangem.features.onboarding.v2.multiwallet.impl import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.arkivanov.decompose.extensions.compose.jetpack.stack.Children import com.arkivanov.decompose.extensions.compose.jetpack.stack.animation.slide import com.arkivanov.decompose.extensions.compose.jetpack.stack.animation.stackAnimation @@ -21,6 +22,7 @@ import com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.MultiWall import com.tangem.features.onboarding.v2.multiwallet.impl.child.createwallet.MultiWalletCreateWalletComponent import com.tangem.features.onboarding.v2.multiwallet.impl.model.OnboardingMultiWalletModel import com.tangem.features.onboarding.v2.multiwallet.impl.model.OnboardingMultiWalletState +import com.tangem.features.onboarding.v2.multiwallet.impl.ui.OnboardingMultiWallet import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -95,13 +97,20 @@ internal class DefaultOnboardingMultiWalletComponent @AssistedInject constructor @Composable override fun Content(modifier: Modifier) { val stackState by childStack.subscribeAsState() + val state by model.uiState.collectAsStateWithLifecycle() - Children( - stack = stackState, - animation = stackAnimation(slide()), - ) { - it.instance.Content(Modifier) - } + OnboardingMultiWallet( + state = state, + modifier = modifier, + childContent = { mdfr -> + Children( + stack = stackState, + animation = stackAnimation(slide()), + ) { + it.instance.Content(mdfr) + } + }, + ) } @AssistedFactory diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/backup/ui/MultiWalletBackup.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/backup/ui/MultiWalletBackup.kt index 5c397a1811..a8a1e22522 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/backup/ui/MultiWalletBackup.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/backup/ui/MultiWalletBackup.kt @@ -1,14 +1,10 @@ package com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.ui -import androidx.compose.foundation.background import androidx.compose.foundation.layout.* -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll 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 import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -26,22 +22,8 @@ fun MultiWalletBackup(state: MultiWalletBackupUM, modifier: Modifier = Modifier) Column( modifier = modifier .fillMaxSize() - .navigationBarsPadding() - .verticalScroll(rememberScrollState()), + .navigationBarsPadding(), ) { - Box( - Modifier - .fillMaxWidth() - .weight(1f), - ) { - Box( - Modifier - .align(Alignment.Center) - .size(100.dp) - .background(Color.Black), - ) - } - when (state) { is MultiWalletBackupUM.Wallet1 -> Wallet1Content(state) is MultiWalletBackupUM.Wallet2 -> TODO() diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/backup/ui/Wallet1Artwork.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/backup/ui/Wallet1Artwork.kt deleted file mode 100644 index 6051542ddf..0000000000 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/backup/ui/Wallet1Artwork.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.ui - -import androidx.compose.foundation.layout.Box -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview -import com.tangem.core.ui.res.TangemThemePreview - -@Composable -fun Wallet1Artwork(modifier: Modifier = Modifier) { - Box(modifier) { - } -} - -@Preview -@Composable -private fun Preview() { - TangemThemePreview { - Wallet1Artwork() - } -} \ No newline at end of file diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/createwallet/ui/MultiWalletCreateWallet.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/createwallet/ui/MultiWalletCreateWallet.kt index 8fb08b976c..b20e13c3af 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/createwallet/ui/MultiWalletCreateWallet.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/createwallet/ui/MultiWalletCreateWallet.kt @@ -1,13 +1,8 @@ package com.tangem.features.onboarding.v2.multiwallet.impl.child.createwallet.ui -import androidx.compose.foundation.background import androidx.compose.foundation.layout.* -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -42,22 +37,9 @@ fun MultiWalletCreateWallet(state: MultiWalletCreateWalletUM, modifier: Modifier Column( modifier = modifier .fillMaxSize() - .navigationBarsPadding() - .verticalScroll(rememberScrollState()), + .navigationBarsPadding(), + verticalArrangement = Arrangement.Bottom, ) { - Box( - Modifier - .fillMaxWidth() - .weight(1f), - ) { - Box( - Modifier - .align(Alignment.Center) - .size(100.dp) - .background(Color.Black), - ) - } - PrimaryButtonIconEnd( modifier = Modifier .padding(start = 16.dp, end = 16.dp, bottom = 12.dp) diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/model/Dialogs.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/model/Dialogs.kt deleted file mode 100644 index f6905984f6..0000000000 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/model/Dialogs.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.tangem.features.onboarding.v2.multiwallet.impl.model - -import com.tangem.core.ui.extensions.resourceReference -import com.tangem.features.onboarding.v2.impl.R -import com.tangem.features.onboarding.v2.multiwallet.impl.ui.state.OnboardingMultiWalletUM - -internal fun resetCardDialog(onConfirm: () -> Unit, onDismiss: () -> Unit, onDismissButtonClick: () -> Unit) = - OnboardingMultiWalletUM.Dialog( - title = resourceReference(R.string.onboarding_activation_error_title), - description = resourceReference(R.string.onboarding_activation_error_message), - confirmButtonText = resourceReference(R.string.common_ok), - dismissButtonText = resourceReference(R.string.common_support), - onDismiss = onDismiss, - onConfirm = onConfirm, - onDismissButtonClick = onDismissButtonClick, - ) \ No newline at end of file diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/model/OnboardingMultiWalletModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/model/OnboardingMultiWalletModel.kt index 1497b73c2d..5a8c2f5fca 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/model/OnboardingMultiWalletModel.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/model/OnboardingMultiWalletModel.kt @@ -4,11 +4,11 @@ import com.tangem.core.decompose.di.ComponentScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.domain.models.scan.CardDTO +import com.tangem.domain.wallets.usecase.GetCardImageUseCase import com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletComponent +import com.tangem.features.onboarding.v2.multiwallet.impl.ui.state.OnboardingMultiWalletUM import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import javax.inject.Inject @@ -17,8 +17,9 @@ internal class OnboardingMultiWalletModel @Inject constructor( paramsContainer: ParamsContainer, override val dispatchers: CoroutineDispatcherProvider, ) : Model() { - private val params = paramsContainer.require() + private val getCardImageUseCase = GetCardImageUseCase() + private val _uiState = MutableStateFlow(OnboardingMultiWalletUM()) val state = MutableStateFlow( OnboardingMultiWalletState( @@ -27,12 +28,15 @@ internal class OnboardingMultiWalletModel @Inject constructor( ), ) + val uiState = _uiState.asStateFlow() + init { // TODO add analytics // if (!manager.isActivationStarted(notNullCard.cardId)) { // Analytics.send(Onboarding.Started()) // } initScreenTitleSub() + loadCardArtwork() } private fun getInitialStep(): OnboardingMultiWalletState.Step { @@ -60,4 +64,17 @@ internal class OnboardingMultiWalletModel @Inject constructor( } } } + + private fun loadCardArtwork() { + modelScope.launch { + val artwork = + getCardImageUseCase.invoke(params.scanResponse.card.cardId, params.scanResponse.card.cardPublicKey) + + if (artwork != getCardImageUseCase.getDefaultFallbackUrl()) { + _uiState.update { + it.copy(artworkUrl = artwork) + } + } + } + } } \ No newline at end of file diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/ui/OnboardingMultiWallet.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/ui/OnboardingMultiWallet.kt index d2de011033..9a3b0efcec 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/ui/OnboardingMultiWallet.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/ui/OnboardingMultiWallet.kt @@ -1,79 +1,63 @@ package com.tangem.features.onboarding.v2.multiwallet.impl.ui +import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource +import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import com.tangem.core.ui.components.BasicDialog -import com.tangem.core.ui.components.DialogButtonUM -import com.tangem.core.ui.components.PrimaryButtonIconEnd -import com.tangem.core.ui.components.SecondaryButton -import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemThemePreview -import com.tangem.features.onboarding.v2.impl.R import com.tangem.features.onboarding.v2.multiwallet.impl.ui.state.OnboardingMultiWalletUM @Composable -internal fun OnboardingMultiWallet(state: OnboardingMultiWalletUM, modifier: Modifier = Modifier) { - if (state.dialog != null) { - BasicDialog( - title = state.dialog.title.resolveReference(), - message = state.dialog.description.resolveReference(), - confirmButton = DialogButtonUM( - title = state.dialog.confirmButtonText.resolveReference(), - onClick = state.dialog.onConfirm, - ), - dismissButton = DialogButtonUM( - title = state.dialog.dismissButtonText.resolveReference(), - onClick = state.dialog.onDismissButtonClick, - ), - onDismissDialog = state.dialog.onDismiss, - ) - } - +@Suppress("MagicNumber") +internal fun OnboardingMultiWallet( + state: OnboardingMultiWalletUM, + modifier: Modifier = Modifier, + childContent: @Composable (Modifier) -> Unit = {}, +) { Column( modifier = modifier .fillMaxSize() - .navigationBarsPadding() .verticalScroll(rememberScrollState()), ) { - Box(Modifier.weight(1f)) - - PrimaryButtonIconEnd( + WalletArtworks( + url = state.artworkUrl, modifier = Modifier - .padding(start = 16.dp, end = 16.dp, bottom = 12.dp) + .weight(.56f) + .padding(horizontal = 34.dp) + .padding(top = 20.dp) .fillMaxWidth(), - iconResId = R.drawable.ic_tangem_24, - text = stringResource(R.string.onboarding_create_wallet_button_create_wallet), - onClick = state.onCreateWalletClick, + state = WalletArtworksState.Folded, ) - SecondaryButton( - modifier = Modifier - .padding(start = 16.dp, end = 16.dp, bottom = 16.dp) - .fillMaxWidth(), - text = stringResource(R.string.onboarding_create_wallet_options_button_options), - onClick = state.onOtherOptionsClick, - ) + Box( + modifier = Modifier.weight(.44f), + contentAlignment = Alignment.BottomStart, + ) { + childContent(Modifier) + } } } -@Preview +@Preview(showBackground = true, widthDp = 360, heightDp = 640) @Composable private fun Preview() { TangemThemePreview { OnboardingMultiWallet( - state = OnboardingMultiWalletUM( - onCreateWalletClick = {}, - showSeedPhraseOption = true, - onBack = {}, - onOtherOptionsClick = {}, - dialog = null, - ), + state = OnboardingMultiWalletUM(artworkUrl = null), + childContent = { md -> + Box( + modifier = md + .fillMaxWidth() + .height(200.dp) + .background(Color.DarkGray), + ) + }, ) } } \ No newline at end of file diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/ui/WalletArtwork.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/ui/WalletArtwork.kt new file mode 100644 index 0000000000..fa5c1643a7 --- /dev/null +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/ui/WalletArtwork.kt @@ -0,0 +1,462 @@ +@file:Suppress("MagicNumber", "UnnecessaryParentheses", "CyclomaticComplexMethod", "LongMethod") +package com.tangem.features.onboarding.v2.multiwallet.impl.ui + +import androidx.compose.animation.core.Transition +import androidx.compose.animation.core.animateFloat +import androidx.compose.animation.core.updateTransition +import androidx.compose.foundation.layout.* +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.util.fastForEach +import androidx.compose.ui.zIndex +import coil.compose.AsyncImage +import coil.request.ImageRequest +import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.features.onboarding.v2.impl.R +import kotlinx.coroutines.delay +import java.util.concurrent.TimeUnit + +sealed class WalletArtworksState { + data object Folded : WalletArtworksState() + + data object Fan : WalletArtworksState() + + data class Stack( + val threeCards: Boolean = true, + ) : WalletArtworksState() + + data class Unfold( + val step: Step = Step.First, + ) : WalletArtworksState() { + enum class Step { + First, Second, Third + } + } + + data class Leapfrog( + val threeCards: Boolean = true, + val step: Step = Step.Second, + ) : WalletArtworksState() { + enum class Step { + Second, Third + } + } +} + +@Composable +fun WalletArtworks(url: String?, state: WalletArtworksState, modifier: Modifier = Modifier) { + BoxWithConstraints( + modifier + .heightIn(min = 180.dp) + .widthIn(max = 450.dp), + ) { + val animationWidth = maxWidth + val density = LocalDensity.current.density + + var animationState by remember { + mutableStateOf( + state.toTransitionSetState( + maxWidthDp = animationWidth.value, + density = density, + maxHeightDp = maxHeight.value, + ).first(), + ) + } + + val transition1 = updateTransition(animationState.walletCard1, label = "transition") + val transition2 = updateTransition(animationState.walletCard2, label = "transition") + val transition3 = updateTransition(animationState.walletCard3, label = "transition") + + LaunchedEffect(state) { + val animationSetState = state.toTransitionSetState( + maxWidthDp = animationWidth.value, + density = density, + maxHeightDp = maxHeight.value, + ) + animationSetState.fastForEach { + animationState = it + val maxTime = maxOf( + transition1.totalDurationNanos, + transition2.totalDurationNanos, + transition3.totalDurationNanos, + ) + delay(TimeUnit.NANOSECONDS.toMillis(maxTime)) + } + } + + AnimatedWalletCards( + url = url, + modifier = Modifier + .widthIn(max = 450.dp) + .matchParentSize(), + transition1 = transition1, + transition2 = transition2, + transition3 = transition3, + ) + } +} + +private data class CardsTransitionState( + val walletCard1: WalletCardTransitionState, + val walletCard2: WalletCardTransitionState, + val walletCard3: WalletCardTransitionState, +) + +private data class WalletCardTransitionState( + val alpha: Float = 1f, + val xTranslation: Float = 0f, + val yTranslation: Float = 0f, + val xScale: Float = 1f, + val yScale: Float = 1f, + val rotation: Float = 0f, + val zIndex: Float = 0f, +) + +@Composable +private fun AnimatedWalletCards( + url: String?, + transition1: Transition, + transition2: Transition, + transition3: Transition, + modifier: Modifier = Modifier, +) { + val alpha1 by transition1.animateFloat(label = "alpha1") { it.alpha } + val translationX1 by transition1.animateFloat(label = "translationX1") { it.xTranslation } + val translationY1 by transition1.animateFloat(label = "translationY1") { it.yTranslation } + val rotation1 by transition1.animateFloat(label = "rotation1") { it.rotation } + val scaleX1 by transition1.animateFloat(label = "scaleX1") { it.xScale } + val scaleY1 by transition1.animateFloat(label = "scaleY1") { it.yScale } + val zIndex1 = transition1.currentState.zIndex + + val alpha2 by transition2.animateFloat(label = "alpha2") { it.alpha } + val translationX2 by transition2.animateFloat(label = "translationX2") { it.xTranslation } + val translationY2 by transition2.animateFloat(label = "translationY2") { it.yTranslation } + val rotation2 by transition2.animateFloat(label = "rotation2") { it.rotation } + val scaleX2 by transition2.animateFloat(label = "scaleX2") { it.xScale } + val scaleY2 by transition2.animateFloat(label = "scaleY2") { it.yScale } + val zIndex2 = transition2.currentState.zIndex + + val alpha3 by transition3.animateFloat(label = "alpha3") { it.alpha } + val translationX3 by transition3.animateFloat(label = "translationX3") { it.xTranslation } + val translationY3 by transition3.animateFloat(label = "translationY3") { it.yTranslation } + val rotation3 by transition3.animateFloat(label = "rotation3") { it.rotation } + val scaleX3 by transition3.animateFloat(label = "scaleX3") { it.xScale } + val scaleY3 by transition3.animateFloat(label = "scaleY3") { it.yScale } + val zIndex3 = transition3.targetState.zIndex + + Box(modifier) { + WalletCard( + modifier = Modifier + .zIndex(zIndex3) + .matchParentSize() + .graphicsLayer { + translationX = translationX3 + translationY = translationY3 + } + .graphicsLayer { + alpha = alpha3 + scaleX = scaleX3 + scaleY = scaleY3 + rotationZ = rotation3 + }, + url = url, + ) + + WalletCard( + modifier = Modifier + .zIndex(zIndex2) + .matchParentSize() + .graphicsLayer { + translationX = translationX2 + translationY = translationY2 + } + .graphicsLayer { + alpha = alpha2 + rotationZ = rotation2 + + scaleX = scaleX2 + scaleY = scaleY2 + }, + url = url, + ) + + WalletCard( + modifier = Modifier + .zIndex(zIndex1) + .matchParentSize() + .graphicsLayer { + scaleX = scaleX1 + scaleY = scaleY1 + } + .graphicsLayer { + alpha = alpha1 + translationX = translationX1 + translationY = translationY1 + rotationZ = rotation1 + }, + url = url, + ) + } +} + +@Composable +private fun WalletCard(url: String?, modifier: Modifier = Modifier) { + AsyncImage( + modifier = modifier, + model = ImageRequest.Builder(LocalContext.current) + .data(url) + .crossfade(true) + .build(), + placeholder = painterResource(R.drawable.card_placeholder_black), + error = painterResource(R.drawable.card_placeholder_black), + fallback = painterResource(R.drawable.card_placeholder_black), + contentDescription = null, + ) +} + +private fun WalletArtworksState.toTransitionSetState(maxWidthDp: Float, maxHeightDp: Float, density: Float) = + when (this) { + is WalletArtworksState.Folded -> listOf( + CardsTransitionState( + WalletCardTransitionState(), + WalletCardTransitionState(), + WalletCardTransitionState(), + ), + ) + is WalletArtworksState.Stack -> listOf( + CardsTransitionState( + walletCard1 = WalletCardTransitionState(), + walletCard2 = WalletCardTransitionState(), + walletCard3 = WalletCardTransitionState(), + ), + CardsTransitionState( + walletCard1 = WalletCardTransitionState().copy( + alpha = 1f, + xTranslation = 0f, + yTranslation = 0f, + rotation = 0f, + xScale = 1f, + yScale = 1f, + ), + walletCard2 = WalletCardTransitionState().copy( + alpha = 0.65f, + xTranslation = 0f, + yTranslation = ((maxHeightDp * (1 - 0.83f)) / 4 + 12) * density, + rotation = 0f, + xScale = 0.83f, + yScale = 0.83f, + ), + walletCard3 = WalletCardTransitionState().copy( + alpha = if (threeCards) 0.55f else 0f, + xTranslation = 0f, + yTranslation = (maxHeightDp * (1 - 0.72f) / 4 + 24) * density, + rotation = 0f, + xScale = 0.72f, + yScale = 0.72f, + ), + ), + ) + is WalletArtworksState.Unfold -> listOfNotNull( + CardsTransitionState( + walletCard1 = WalletCardTransitionState(), + walletCard2 = WalletCardTransitionState().copy( + alpha = 0.65f, + ), + walletCard3 = WalletCardTransitionState().copy( + alpha = 0.55f, + ), + ).takeIf { step == WalletArtworksState.Unfold.Step.First }, + CardsTransitionState( + walletCard1 = WalletCardTransitionState().copy( + rotation = 5f, + ), + walletCard2 = WalletCardTransitionState().copy( + alpha = when (step) { + WalletArtworksState.Unfold.Step.First -> 0.65f + WalletArtworksState.Unfold.Step.Second -> 1f + WalletArtworksState.Unfold.Step.Third -> 1f + }, + rotation = -6f, + xTranslation = 0f, + yTranslation = -(maxHeightDp * (1 - 0.88f) / 4) * density, + xScale = 0.88f, + yScale = 0.88f, + ), + walletCard3 = WalletCardTransitionState().copy( + alpha = when (step) { + WalletArtworksState.Unfold.Step.First -> 0.55f + WalletArtworksState.Unfold.Step.Second -> 0.55f + WalletArtworksState.Unfold.Step.Third -> 1f + }, + rotation = -16f, + xTranslation = 0f, + yTranslation = -(maxHeightDp * (1 - 0.81f) / 4 + 2) * density, + xScale = 0.81f, + yScale = 0.81f, + ), + ), + ) + WalletArtworksState.Fan -> { + listOf( + CardsTransitionState( + walletCard1 = WalletCardTransitionState().copy( + xTranslation = maxWidthDp * 0.4f * density, + yTranslation = 35 * density, + rotation = 75f, + xScale = .85f, + yScale = .85f, + ), + walletCard2 = WalletCardTransitionState().copy( + xTranslation = maxWidthDp * -0.4f * density, + yTranslation = -5 * density, + rotation = 75f, + xScale = .85f, + yScale = .85f, + ), + walletCard3 = WalletCardTransitionState().copy( + rotation = 105f, + xScale = .85f, + yScale = .85f, + ), + ), + ) + } + is WalletArtworksState.Leapfrog -> { + val translationY = -(maxHeightDp / 2) * density + val secondCardPlace = WalletCardTransitionState().copy( + alpha = 0.65f, + xTranslation = 0f, + yTranslation = ((maxHeightDp * (1 - 0.83f)) / 4 + 12) * density, + rotation = 0f, + xScale = 0.83f, + yScale = 0.83f, + ) + val thirdCardPlace = WalletCardTransitionState().copy( + alpha = if (threeCards) 0.55f else 0f, + xTranslation = 0f, + yTranslation = (maxHeightDp * (1 - 0.72f) / 4 + 24) * density, + rotation = 0f, + xScale = 0.72f, + yScale = 0.72f, + ) + + if (step == WalletArtworksState.Leapfrog.Step.Second) { + listOf( + CardsTransitionState( + walletCard1 = WalletCardTransitionState(), + walletCard2 = secondCardPlace, + walletCard3 = thirdCardPlace, + ), + CardsTransitionState( + walletCard1 = WalletCardTransitionState().copy( + yTranslation = translationY, + ), + walletCard2 = WalletCardTransitionState().copy( + alpha = 1f, + zIndex = 4f, + ), + walletCard3 = thirdCardPlace, + ), + CardsTransitionState( + walletCard1 = WalletCardTransitionState().copy( + zIndex = 1f, + ), + walletCard2 = WalletCardTransitionState().copy( + zIndex = 4f, + ), + walletCard3 = secondCardPlace, + ), + ) + } else { + listOf( + CardsTransitionState( + walletCard1 = WalletCardTransitionState().copy( + zIndex = 2f, + ), + walletCard2 = WalletCardTransitionState().copy( + zIndex = 3f, + ), + walletCard3 = secondCardPlace, + ), + CardsTransitionState( + walletCard1 = WalletCardTransitionState().copy( + yTranslation = translationY, + ), + walletCard2 = WalletCardTransitionState().copy( + zIndex = 3f, + yTranslation = translationY, + ), + walletCard3 = WalletCardTransitionState().copy( + alpha = 1f, + zIndex = 4f, + ), + ), + CardsTransitionState( + walletCard1 = WalletCardTransitionState().copy( + zIndex = 1f, + ), + walletCard2 = WalletCardTransitionState().copy( + zIndex = 4f, + ), + walletCard3 = WalletCardTransitionState().copy( + zIndex = 5f, + ), + ), + ) + } + } + } + +@Preview(showBackground = true, widthDp = 360, heightDp = 640) +@Composable +private fun Preview() { + TangemThemePreview { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center, + ) { + var state: WalletArtworksState by remember { mutableStateOf(WalletArtworksState.Folded) } + + WalletArtworks( + url = null, + state = state, + modifier = Modifier.fillMaxWidth(), + ) + + var index by remember { mutableIntStateOf(0) } + + Button( + modifier = Modifier + .zIndex(5f) + .align(Alignment.TopStart), + onClick = { + val list = listOf( + WalletArtworksState.Folded, + WalletArtworksState.Stack(), + WalletArtworksState.Fan, + WalletArtworksState.Unfold(step = WalletArtworksState.Unfold.Step.First), + WalletArtworksState.Unfold(step = WalletArtworksState.Unfold.Step.Second), + WalletArtworksState.Unfold(step = WalletArtworksState.Unfold.Step.Third), + WalletArtworksState.Stack(), + WalletArtworksState.Leapfrog(step = WalletArtworksState.Leapfrog.Step.Second), + WalletArtworksState.Leapfrog(step = WalletArtworksState.Leapfrog.Step.Third), + ) + + state = list[index % list.size] + index++ + }, + ) { + Text("Animate") + } + } + } +} \ No newline at end of file diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/ui/state/OnboardingMultiWalletUM.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/ui/state/OnboardingMultiWalletUM.kt index 8ae99c8da0..1e4884ea7a 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/ui/state/OnboardingMultiWalletUM.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/ui/state/OnboardingMultiWalletUM.kt @@ -1,21 +1,5 @@ package com.tangem.features.onboarding.v2.multiwallet.impl.ui.state -import com.tangem.core.ui.extensions.TextReference - internal data class OnboardingMultiWalletUM( - val onCreateWalletClick: () -> Unit, - val showSeedPhraseOption: Boolean, - val onOtherOptionsClick: () -> Unit, - val onBack: () -> Unit, - val dialog: Dialog?, -) { - data class Dialog( - val title: TextReference, - val description: TextReference, - val dismissButtonText: TextReference, - val confirmButtonText: TextReference, - val onConfirm: () -> Unit, - val onDismissButtonClick: () -> Unit, - val onDismiss: () -> Unit, - ) -} \ No newline at end of file + val artworkUrl: String? = null, +) \ No newline at end of file