Updated on 2026-08-14
This commit is contained in:
parent
918ab4613f
commit
819a80a444
16 changed files with 414 additions and 48 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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 ->
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue