Updated on 2026-08-14

This commit is contained in:
Tangem 2024-11-07 11:56:45 +03:00
parent 342b6a2f50
commit ec40334d8a
26 changed files with 527 additions and 50 deletions

View file

@ -52,6 +52,7 @@ import com.tangem.domain.tokens.GetPolkadotCheckHasResetUseCase
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.feature.qrscanning.QrScanningRouter
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
import com.tangem.features.pushnotifications.api.navigation.PushNotificationsRouter
import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
import com.tangem.features.send.api.navigation.SendRouter
@ -59,6 +60,7 @@ import com.tangem.features.staking.api.navigation.StakingRouter
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
import com.tangem.features.wallet.navigation.WalletRouter
import com.tangem.operations.backup.BackupService
import com.tangem.sdk.api.BackupServiceHolder
import com.tangem.sdk.api.TangemSdkManager
import com.tangem.sdk.extensions.init
import com.tangem.tap.common.ActivityResultCallbackHolder
@ -185,6 +187,12 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
@Inject
lateinit var shouldInitiallyAskPermissionUseCase: ShouldInitiallyAskPermissionUseCase
@Inject
lateinit var backupServiceHolder: BackupServiceHolder
@Inject
lateinit var onboardingV2FeatureToggles: OnboardingV2FeatureToggles
internal val viewModel: MainViewModel by viewModels()
private lateinit var appThemeModeFlow: SharedFlow<AppThemeMode>
@ -294,7 +302,14 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
cardSdkOwner.register(activity = this)
tangemSdkManager = injectedTangemSdkManager
appStateHolder.tangemSdkManager = tangemSdkManager
if (onboardingV2FeatureToggles.isOnboardingV2Enabled) {
backupServiceHolder.createAndSetService(cardSdkConfigRepository.sdk, this)
backupService = backupServiceHolder.backupService.get()!! // will be deleted eventually
} else {
backupService = BackupService.init(cardSdkConfigRepository.sdk, this)
}
lockUserWalletsTimer = LockUserWalletsTimer(
owner = this,
settingsRepository = settingsRepository,
@ -550,6 +565,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
}
}
// TODO add onboarding v2 handling
store.dispatch(BackupAction.CheckForUnfinishedBackup)
}

View file

@ -0,0 +1,17 @@
package com.tangem.tap.di
import com.tangem.sdk.api.BackupServiceHolder
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal object TangemSdkModule {
@Provides
@Singleton
fun provideBackupServiceHolder(): BackupServiceHolder = BackupServiceHolder()
}

View file

@ -67,7 +67,10 @@ object OnboardingHelper {
fun whereToNavigate(scanResponse: ScanResponse): AppRoute {
if (store.inject(DaggerGraphState::onboardingV2FeatureToggles).isOnboardingV2Enabled) {
return AppRoute.Onboarding(scanResponse)
return AppRoute.Onboarding(
scanResponse = scanResponse,
startFromBackup = false,
)
}
return when (val type = scanResponse.productType) {

View file

@ -218,7 +218,10 @@ internal class ChildFactory @Inject constructor(
is AppRoute.Onboarding -> {
route.asComponentChild(
contextProvider = contextProvider(route, contextFactory),
params = OnboardingEntryComponent.Params(route.scanResponse),
params = OnboardingEntryComponent.Params(
scanResponse = route.scanResponse,
startBackupFlow = route.startFromBackup,
),
componentFactory = onboardingEntryComponentFactory,
)
}

View file

@ -300,5 +300,8 @@ sealed class AppRoute(val path: String) : Route {
) : AppRoute(path = "/sell_crypto/${userWalletId.stringValue}")
// Onboarding V2
data class Onboarding(val scanResponse: ScanResponse) : AppRoute(path = "/onboarding_v2")
data class Onboarding(
val scanResponse: ScanResponse,
val startFromBackup: Boolean,
) : AppRoute(path = "/onboarding_v2${if (startFromBackup) "/backup" else ""}")
}

View file

@ -8,6 +8,7 @@ interface OnboardingEntryComponent : ComposableContentComponent {
data class Params(
val scanResponse: ScanResponse,
val startBackupFlow: Boolean,
)
interface Factory : ComponentFactory<Params, OnboardingEntryComponent>

View file

@ -28,6 +28,7 @@ dependencies {
implementation(projects.domain.models)
implementation(projects.domain.feedback)
implementation(projects.domain.core)
implementation(projects.domain.card)
/** Tangem libraries */
implementation(projects.libs.tangemSdkApi)
@ -54,6 +55,7 @@ dependencies {
implementation(deps.kotlin.immutable.collections)
implementation(deps.kotlin.serialization)
implementation(deps.timber)
implementation(deps.firebase.crashlytics)
/** DI */
implementation(deps.hilt.android)

View file

@ -40,12 +40,12 @@ internal class OnboardingEntryModel @Inject constructor(
return when (scanResponse.productType) {
ProductType.Note -> TODO()
ProductType.Twins -> TODO()
ProductType.Wallet -> OnboardingRoute.Wallet12(
ProductType.Wallet -> OnboardingRoute.MultiWallet(
scanResponse = scanResponse,
withSeedPhraseFlow = false,
titleProvider = titleProvider,
)
ProductType.Wallet2 -> OnboardingRoute.Wallet12(
ProductType.Wallet2 -> OnboardingRoute.MultiWallet(
scanResponse = scanResponse,
withSeedPhraseFlow = true,
titleProvider = titleProvider,

View file

@ -5,12 +5,12 @@ import com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletCo
import javax.inject.Inject
internal class OnboardingChildFactory @Inject constructor(
private val wallet12ComponentFactory: OnboardingMultiWalletComponent.Factory,
private val multiWalletComponentFactory: OnboardingMultiWalletComponent.Factory,
) {
fun createChild(route: OnboardingRoute, childContext: AppComponentContext): Any {
return when (route) {
is OnboardingRoute.Wallet12 -> wallet12ComponentFactory.create(
is OnboardingRoute.MultiWallet -> multiWalletComponentFactory.create(
context = childContext,
params = OnboardingMultiWalletComponent.Params(
scanResponse = route.scanResponse,

View file

@ -8,7 +8,7 @@ sealed class OnboardingRoute : Route {
data object None : OnboardingRoute()
data class Wallet12(
data class MultiWallet(
val titleProvider: TitleProvider,
val scanResponse: ScanResponse,
val withSeedPhraseFlow: Boolean,

View file

@ -31,7 +31,7 @@ internal inline fun OnboardingEntry(
animation = stackAnimation(slide()),
) {
when (it.configuration) {
is OnboardingRoute.Wallet12 -> {
is OnboardingRoute.MultiWallet -> {
(it.instance as OnboardingMultiWalletComponent).Content(
modifier = modifier,
)

View file

@ -32,12 +32,11 @@ internal class DefaultOnboardingMultiWalletComponent @AssistedInject constructor
) : OnboardingMultiWalletComponent, AppComponentContext by context {
private val model: OnboardingMultiWalletModel = getOrCreateModel(params)
private val stackNavigation = StackNavigation<OnboardingMultiWalletState.Step>()
override val innerNavigation: InnerNavigation = object : InnerNavigation {
override val state = MutableStateFlow(
Wallet12InnerNavigationState(1, 5),
MultiWalletInnerNavigationState(1, 5),
)
override fun pop(onComplete: (Boolean) -> Unit) {
@ -83,8 +82,10 @@ internal class DefaultOnboardingMultiWalletComponent @AssistedInject constructor
private fun onStepDone() {
when (model.state.value.currentStep) {
OnboardingMultiWalletState.Step.CreateWallet ->
OnboardingMultiWalletState.Step.CreateWallet -> {
stackNavigation.push(OnboardingMultiWalletState.Step.AddBackupDevice)
// innerNavigation.state.value TODO
}
OnboardingMultiWalletState.Step.AddBackupDevice -> TODO()
OnboardingMultiWalletState.Step.FinishBackup -> TODO()
OnboardingMultiWalletState.Step.Done -> TODO()
@ -112,7 +113,7 @@ internal class DefaultOnboardingMultiWalletComponent @AssistedInject constructor
}
}
data class Wallet12InnerNavigationState(
data class MultiWalletInnerNavigationState(
override val stackSize: Int,
override val stackMaxSize: Int?,
) : InnerNavigationState

View file

@ -12,6 +12,7 @@ import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChild
import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildComponent
import com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.model.MultiWalletBackupModel
import com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.ui.MultiWalletBackup
import kotlinx.coroutines.launch
@Suppress("UnusedPrivateMember")
class MultiWalletBackupComponent(
@ -26,6 +27,10 @@ class MultiWalletBackupComponent(
params.parentParams.titleProvider.changeTitle(
text = resourceReference(R.string.onboarding_navbar_title_creating_backup),
)
componentScope.launch {
model.onDoneFlow.collect { onDone() }
}
}
@Composable

View file

@ -0,0 +1,48 @@
package com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.model
import android.graphics.Bitmap
import com.tangem.common.card.Card
// TODO maybe delete some fields in the future
data class BackupState(
val primaryCardId: String? = null,
val primaryCardBatchId: String? = null,
val backupCardsNumber: Int = 0,
val backupCards: List<Card> = emptyList(),
val backupCardIds: List<CardId> = emptyList(),
val backupCardBatchIds: List<CardId> = emptyList(),
@Transient
val backupCardsArtworks: Map<CardId, Bitmap> = emptyMap(),
val accessCode: String = "",
val accessCodeError: AccessCodeError? = null,
val backupStep: BackupStep? = null,
val maxBackupCards: Int = 2,
val canSkipBackup: Boolean = true,
val isInterruptedBackup: Boolean = false,
val showBtnLoading: Boolean = false,
val hasBackupError: Boolean = false,
val startedSource: BackupStartedSource = BackupStartedSource.Onboarding,
val hasRing: Boolean = false,
)
enum class BackupStartedSource {
Onboarding, CreateBackup
}
enum class AccessCodeError {
CodeTooShort, CodesDoNotMatch
}
typealias CardId = String
sealed class BackupStep {
data object InitBackup : BackupStep()
data object ScanOriginCard : BackupStep()
data object AddBackupCards : BackupStep()
data object SetAccessCode : BackupStep()
data object EnterAccessCode : BackupStep()
data object ReenterAccessCode : BackupStep()
data object WritePrimaryCard : BackupStep()
data class WriteBackupCard(val cardNumber: Int) : BackupStep()
data object Finished : BackupStep()
}

View file

@ -1,31 +1,201 @@
package com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.model
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemSdkError
import com.tangem.core.decompose.di.ComponentScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.models.scan.ProductType
import com.tangem.features.onboarding.v2.impl.R
import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildParams
import com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.ui.backupCardAttestationFailedDialog
import com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.ui.resetBackupCardDialog
import com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.ui.state.MultiWalletBackupUM
import com.tangem.operations.backup.BackupService
import com.tangem.sdk.api.BackupServiceHolder
import com.tangem.sdk.api.TangemSdkManager
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject
@ComponentScoped
class MultiWalletBackupModel @Inject constructor(
paramsContainer: ParamsContainer,
override val dispatchers: CoroutineDispatcherProvider,
private val backupServiceHolder: BackupServiceHolder,
private val cardSdkConfigRepository: CardSdkConfigRepository,
private val tangemSdkManager: TangemSdkManager,
) : Model() {
@Suppress("UnusedPrivateMember")
private val params = paramsContainer.require<MultiWalletChildParams>()
private val scanResponse
get() = params.multiWalletState.value.currentScanResponse
private val backupService
get() = backupServiceHolder.backupService.get()!!
private val state = MutableStateFlow(BackupState())
private val _uiState = MutableStateFlow(
MultiWalletBackupUM(
finalizeButtonEnabled = false,
when (scanResponse.productType) {
ProductType.Wallet -> getWallet1State()
ProductType.Wallet2,
ProductType.Ring,
-> MultiWalletBackupUM.Wallet2(
isRing = scanResponse.productType == ProductType.Ring,
backupAdded = false, // backupService.hasIncompletedBackup,
onAddBackupClick = {},
onFinalizeButtonClick = {},
),
)
else -> error("Type: ${scanResponse.productType.name} is not supported!")
},
)
val uiState: StateFlow<MultiWalletBackupUM> = _uiState
val onDoneFlow = MutableSharedFlow<Unit>()
private fun getWallet1State(): MultiWalletBackupUM.Wallet1 {
return when (backupService.currentState) {
BackupService.State.Preparing -> {
MultiWalletBackupUM.Wallet1(
title = resourceReference(R.string.onboarding_title_no_backup_cards),
bodyText = resourceReference(R.string.onboarding_subtitle_no_backup_cards),
showFinalizeButton = false,
addBackupButtonEnabled = true,
addBackupButtonLoading = false,
artworkNumberOfBackupCards = 0,
onAddBackupClick = ::startBackupWallet1,
onFinalizeButtonClick = {},
onSkipButtonClick = { onDoneFlow.tryEmit(Unit) },
)
}
is BackupService.State.FinalizingBackupCard -> TODO()
BackupService.State.FinalizingPrimaryCard -> TODO()
BackupService.State.Finished -> TODO()
}
}
private fun startBackupWallet1() {
backupService.discardSavedBackup()
val primaryCard = scanResponse.primaryCard
if (primaryCard != null) {
backupService.setPrimaryCard(primaryCard)
addBackupCardWithService()
} else {
// TODO wallet 2 ??? or from main?
// BackupAction.StartAddingPrimaryCard
}
}
private fun setNumberOfBackupCardsWallet1(number: Int) {
// set state for Wallet1 for adding backup cards and disable button if there is more than 2 backup cards
_uiState.update { st ->
if (st !is MultiWalletBackupUM.Wallet1) return@update st
when (number) {
0 -> {
st.copy(
title = resourceReference(R.string.onboarding_title_no_backup_cards),
bodyText = resourceReference(R.string.onboarding_subtitle_no_backup_cards),
artworkNumberOfBackupCards = 0,
addBackupButtonEnabled = true,
)
}
1 -> {
st.copy(
title = resourceReference(R.string.onboarding_title_one_backup_card),
bodyText = resourceReference(R.string.onboarding_subtitle_one_backup_card),
artworkNumberOfBackupCards = 1,
addBackupButtonEnabled = true,
showFinalizeButton = true,
)
}
2 -> {
st.copy(
title = resourceReference(R.string.onboarding_title_two_backup_cards),
bodyText = resourceReference(R.string.onboarding_subtitle_two_backup_cards),
artworkNumberOfBackupCards = 2,
addBackupButtonEnabled = false,
)
}
else -> st.copy(addBackupButtonEnabled = false)
}
}
}
private fun addBackupCardWithService() {
updateWallet1State { st ->
st.copy(addBackupButtonLoading = true)
}
backupService.addBackupCard { result ->
backupService.skipCompatibilityChecks = false
cardSdkConfigRepository.sdk.config.filter.cardIdFilter = null
updateWallet1State { st ->
st.copy(addBackupButtonLoading = false)
}
when (result) {
is CompletionResult.Success -> {
// TODO change artwork
state.update {
it.copy(
backupCards = it.backupCards + result.data,
backupCardsNumber = it.backupCardsNumber + 1,
)
}
setNumberOfBackupCardsWallet1(state.value.backupCardsNumber)
}
is CompletionResult.Failure -> {
when (val error = result.error) {
is TangemSdkError.BackupFailedNotEmptyWallets -> {
updateWallet1State { st ->
st.copy(
dialog = resetBackupCardDialog(
onReset = { resetBackupCard(cardId = error.cardId) },
onDismiss = { updateWallet1State { it.copy(dialog = null) } },
),
)
}
}
is TangemSdkError.IssuerSignatureLoadingFailed -> {
updateWallet1State { st ->
st.copy(
dialog = backupCardAttestationFailedDialog(
onDismiss = { updateWallet1State { it.copy(dialog = null) } },
),
)
}
}
else -> FirebaseCrashlytics.getInstance().recordException(result.error)
}
}
}
}
}
private fun resetBackupCard(cardId: String) {
modelScope.launch {
tangemSdkManager.resetToFactorySettings(
cardId = cardId,
allowsRequestAccessCodeFromRepository = false,
)
}
}
private fun updateWallet1State(block: (MultiWalletBackupUM.Wallet1) -> MultiWalletBackupUM.Wallet1) {
_uiState.update { state ->
if (state !is MultiWalletBackupUM.Wallet1) return@update state
block(state)
}
}
}

View file

@ -0,0 +1,27 @@
package com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.ui
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.features.onboarding.v2.impl.R
import com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.ui.state.MultiWalletBackupUMDialog
internal fun resetBackupCardDialog(onReset: () -> Unit, onDismiss: () -> Unit) = MultiWalletBackupUMDialog(
title = resourceReference(R.string.common_attention),
bodyText = resourceReference(R.string.onboarding_linking_error_card_with_wallets),
confirmText = resourceReference(R.string.common_cancel),
cancelText = resourceReference(R.string.card_settings_action_sheet_reset),
warningCancelColor = true,
onDismiss = onDismiss,
onConfirm = onDismiss,
onCancel = { onReset(); onDismiss() },
)
internal fun backupCardAttestationFailedDialog(onDismiss: () -> Unit) = MultiWalletBackupUMDialog(
title = resourceReference(R.string.common_error),
bodyText = resourceReference(R.string.issuer_signature_loading_failed),
confirmText = resourceReference(R.string.common_ok),
cancelText = null,
warningCancelColor = true,
onDismiss = onDismiss,
onConfirm = onDismiss,
onCancel = null,
)

View file

@ -4,15 +4,19 @@ 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
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.PrimaryButtonIconEnd
import com.tangem.core.ui.components.SecondaryButton
import com.tangem.core.ui.components.*
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringReference
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.multiwallet.impl.child.backup.ui.state.MultiWalletBackupUM
@ -38,11 +42,90 @@ fun MultiWalletBackup(state: MultiWalletBackupUM, modifier: Modifier = Modifier)
)
}
when (state) {
is MultiWalletBackupUM.Wallet1 -> Wallet1Content(state)
is MultiWalletBackupUM.Wallet2 -> TODO()
}
}
val dialog = state.dialog
if (dialog != null) {
BasicDialog(
title = dialog.title.resolveReference(),
message = dialog.bodyText.resolveReference(),
confirmButton = DialogButtonUM(
title = dialog.confirmText.resolveReference(),
onClick = dialog.onConfirm,
),
dismissButton = if (dialog.cancelText != null && dialog.onCancel != null) {
DialogButtonUM(
title = dialog.cancelText.resolveReference(),
warning = dialog.warningCancelColor,
onClick = dialog.onCancel,
)
} else {
null
},
onDismissDialog = dialog.onDismiss,
)
}
}
@Composable
private fun ColumnScope.Wallet1Content(state: MultiWalletBackupUM.Wallet1) {
Column(
Modifier
.fillMaxWidth()
.padding(start = 32.dp, end = 32.dp, bottom = 74.dp)
.weight(1f),
horizontalAlignment = Alignment.CenterHorizontally,
) {
// artwork
Text(
text = state.title.resolveReference(),
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
modifier = Modifier.padding(top = 16.dp),
)
Text(
text = state.bodyText.resolveReference(),
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.secondary,
textAlign = TextAlign.Center,
modifier = Modifier.padding(top = 12.dp),
)
}
if (state.showFinalizeButton) {
SecondaryButtonIconEnd(
modifier = Modifier
.padding(start = 16.dp, end = 16.dp, bottom = 12.dp)
.fillMaxWidth(),
iconResId = R.drawable.ic_tangem_24,
enabled = state.addBackupButtonEnabled,
showProgress = state.addBackupButtonLoading,
text = stringResource(R.string.onboarding_button_add_backup_card),
onClick = state.onAddBackupClick,
)
PrimaryButton(
modifier = Modifier
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
.fillMaxWidth(),
text = stringResource(R.string.onboarding_button_finalize_backup),
onClick = state.onFinalizeButtonClick,
)
} else {
PrimaryButtonIconEnd(
modifier = Modifier
.padding(start = 16.dp, end = 16.dp, bottom = 12.dp)
.fillMaxWidth(),
iconResId = R.drawable.ic_tangem_24,
enabled = state.addBackupButtonEnabled,
showProgress = state.addBackupButtonLoading,
text = stringResource(R.string.onboarding_button_add_backup_card),
onClick = state.onAddBackupClick,
)
@ -51,20 +134,33 @@ fun MultiWalletBackup(state: MultiWalletBackupUM, modifier: Modifier = Modifier)
modifier = Modifier
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
.fillMaxWidth(),
enabled = state.finalizeButtonEnabled,
text = stringResource(R.string.onboarding_button_finalize_backup),
onClick = state.onFinalizeButtonClick,
text = stringResource(R.string.onboarding_button_skip_backup),
onClick = state.onSkipButtonClick,
)
}
}
@Preview
@Composable
private fun Preview() {
private fun PreviewWallet1() {
TangemThemePreview {
MultiWalletBackup(
state = MultiWalletBackupUM(
finalizeButtonEnabled = true,
state = MultiWalletBackupUM.Wallet1(
title = stringReference("Title"),
bodyText = stringReference("Body body body"),
),
)
}
}
@Preview
@Composable
private fun PreviewWallet2() {
TangemThemePreview {
MultiWalletBackup(
state = MultiWalletBackupUM.Wallet2(
isRing = false,
backupAdded = true,
onAddBackupClick = {},
onFinalizeButtonClick = {},
),

View file

@ -0,0 +1,21 @@
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()
}
}

View file

@ -1,7 +1,45 @@
package com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.ui.state
data class MultiWalletBackupUM(
val finalizeButtonEnabled: Boolean,
import androidx.annotation.IntRange
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.stringReference
@Immutable
sealed class MultiWalletBackupUM {
abstract val dialog: MultiWalletBackupUMDialog?
data class Wallet1(
val title: TextReference = stringReference(""),
val bodyText: TextReference = stringReference(""),
val addBackupButtonEnabled: Boolean = true,
val addBackupButtonLoading: Boolean = false,
val showFinalizeButton: Boolean = false,
val oneBackupAdded: Boolean = false,
@IntRange(0, 2) val artworkNumberOfBackupCards: Int = 0, // highlights cards on 0, 1, 2
val onAddBackupClick: () -> Unit = {},
val onFinalizeButtonClick: () -> Unit = {},
val onSkipButtonClick: () -> Unit = {},
override val dialog: MultiWalletBackupUMDialog? = null,
) : MultiWalletBackupUM()
data class Wallet2(
val isRing: Boolean,
val backupAdded: Boolean,
val onAddBackupClick: () -> Unit,
val onFinalizeButtonClick: () -> Unit,
override val dialog: MultiWalletBackupUMDialog? = null,
) : MultiWalletBackupUM()
}
data class MultiWalletBackupUMDialog(
val title: TextReference,
val bodyText: TextReference,
val confirmText: TextReference,
val cancelText: TextReference?,
val warningCancelColor: Boolean = false,
val onConfirm: () -> Unit,
val onCancel: (() -> Unit)?,
val onDismiss: () -> Unit,
)

View file

@ -4,13 +4,20 @@ import com.tangem.core.ui.extensions.resourceReference
import com.tangem.features.onboarding.v2.impl.R
import com.tangem.features.onboarding.v2.multiwallet.impl.child.createwallet.ui.state.MultiWalletCreateWalletUM
internal fun resetCardDialog(onConfirm: () -> Unit, onDismiss: () -> Unit, onDismissButtonClick: () -> Unit) =
internal fun resetCardDialog(onConfirm: () -> Unit, dismiss: () -> Unit, onDismissButtonClick: () -> Unit) =
MultiWalletCreateWalletUM.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,
confirmButtonText = resourceReference(R.string.common_support),
dismissButtonText = resourceReference(R.string.common_ok),
dismissWarningColor = true,
onDismiss = dismiss,
onConfirm = {
onConfirm()
dismiss()
},
onDismissButtonClick = {
onDismissButtonClick()
dismiss()
},
)

View file

@ -5,6 +5,7 @@ import com.tangem.common.core.TangemSdkError
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.card.repository.CardRepository
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.FeedbackEmailType
@ -26,6 +27,7 @@ class MultiWalletCreateWalletModel @Inject constructor(
private val tangemSdkManager: TangemSdkManager,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val getCardInfoUseCase: GetCardInfoUseCase,
private val cardRepository: CardRepository,
) : Model() {
private val params = paramsContainer.require<MultiWalletChildParams>()
@ -63,6 +65,7 @@ class MultiWalletCreateWalletModel @Inject constructor(
)
}
cardRepository.startCardActivation(cardId = result.data.card.cardId)
onDone.emit(Unit)
// TODO
// Analytics.send(Onboarding.CreateWallet.WalletCreatedSuccessfully())
@ -82,14 +85,9 @@ class MultiWalletCreateWalletModel @Inject constructor(
_uiState.update { state ->
state.copy(
dialog = resetCardDialog(
onConfirm = {
_uiState.update { it.copy(dialog = null) }
resetCard()
},
onDismiss = {
_uiState.update { it.copy(dialog = null) }
},
onDismissButtonClick = ::navigateToSupportScreen,
onConfirm = ::navigateToSupportScreen,
dismiss = { _uiState.update { it.copy(dialog = null) } },
onDismissButtonClick = ::resetCard,
),
)
}

View file

@ -32,6 +32,7 @@ fun MultiWalletCreateWallet(state: MultiWalletCreateWalletUM, modifier: Modifier
),
dismissButton = DialogButtonUM(
title = state.dialog.dismissButtonText.resolveReference(),
warning = state.dialog.dismissWarningColor,
onClick = state.dialog.onDismissButtonClick,
),
onDismissDialog = state.dialog.onDismiss,

View file

@ -13,6 +13,7 @@ data class MultiWalletCreateWalletUM(
val description: TextReference,
val dismissButtonText: TextReference,
val confirmButtonText: TextReference,
val dismissWarningColor: Boolean = false,
val onConfirm: () -> Unit,
val onDismissButtonClick: () -> Unit,
val onDismiss: () -> Unit,

View file

@ -28,6 +28,10 @@ internal class OnboardingMultiWalletModel @Inject constructor(
)
init {
// TODO add analytics
// if (!manager.isActivationStarted(notNullCard.cardId)) {
// Analytics.send(Onboarding.Started())
// }
initScreenTitleSub()
}
@ -49,11 +53,10 @@ internal class OnboardingMultiWalletModel @Inject constructor(
params.titleProvider.changeTitle(title)
modelScope.launch {
state
.map { it.currentStep }
state.map { it.currentStep }
.collectLatest { step ->
val title = screenTitleByStep(step)
params.titleProvider.changeTitle(title)
val stepTitle = screenTitleByStep(step)
params.titleProvider.changeTitle(stepTitle)
}
}
}

View file

@ -6,7 +6,6 @@ data class OnboardingMultiWalletState(
val currentStep: Step,
val currentScanResponse: ScanResponse,
) {
enum class Step {
CreateWallet, AddBackupDevice, FinishBackup, Done
}

View file

@ -0,0 +1,17 @@
package com.tangem.sdk.api
import androidx.activity.ComponentActivity
import com.tangem.TangemSdk
import com.tangem.operations.backup.BackupService
import com.tangem.sdk.extensions.init
import java.lang.ref.WeakReference
class BackupServiceHolder {
lateinit var backupService: WeakReference<BackupService>
private set
fun createAndSetService(tangemSdk: TangemSdk, activity: ComponentActivity) {
backupService = WeakReference(BackupService.init(tangemSdk, activity))
}
}