Updated on 2026-08-14
This commit is contained in:
parent
342b6a2f50
commit
ec40334d8a
26 changed files with 527 additions and 50 deletions
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
17
app/src/main/java/com/tangem/tap/di/TangemSdkModule.kt
Normal file
17
app/src/main/java/com/tangem/tap/di/TangemSdkModule.kt
Normal 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()
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ""}")
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ interface OnboardingEntryComponent : ComposableContentComponent {
|
|||
|
||||
data class Params(
|
||||
val scanResponse: ScanResponse,
|
||||
val startBackupFlow: Boolean,
|
||||
)
|
||||
|
||||
interface Factory : ComponentFactory<Params, OnboardingEntryComponent>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
|
|
@ -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 = {},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
|
|
@ -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()
|
||||
},
|
||||
)
|
||||
|
|
@ -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,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ data class OnboardingMultiWalletState(
|
|||
val currentStep: Step,
|
||||
val currentScanResponse: ScanResponse,
|
||||
) {
|
||||
|
||||
enum class Step {
|
||||
CreateWallet, AddBackupDevice, FinishBackup, Done
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue