Updated on 2026-08-14
This commit is contained in:
parent
022c4bcfcd
commit
d71d29b72b
30 changed files with 295 additions and 104 deletions
|
|
@ -24,6 +24,7 @@ import com.tangem.domain.feedback.GetCardInfoUseCase
|
||||||
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||||
import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase
|
import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase
|
||||||
import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase
|
import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase
|
||||||
|
import com.tangem.domain.onboarding.repository.OnboardingRepository
|
||||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||||
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
|
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
|
||||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||||
|
|
@ -119,6 +120,8 @@ interface ApplicationEntryPoint {
|
||||||
|
|
||||||
fun getOnboardingV2FeatureToggles(): OnboardingV2FeatureToggles
|
fun getOnboardingV2FeatureToggles(): OnboardingV2FeatureToggles
|
||||||
|
|
||||||
|
fun getOnboardingRepository(): OnboardingRepository
|
||||||
|
|
||||||
fun getCoroutineDispatcherProvider(): CoroutineDispatcherProvider
|
fun getCoroutineDispatcherProvider(): CoroutineDispatcherProvider
|
||||||
|
|
||||||
fun getExcludedBlockchains(): ExcludedBlockchains
|
fun getExcludedBlockchains(): ExcludedBlockchains
|
||||||
|
|
|
||||||
|
|
@ -586,7 +586,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO add onboarding v2 handling
|
|
||||||
store.dispatch(BackupAction.CheckForUnfinishedBackup)
|
store.dispatch(BackupAction.CheckForUnfinishedBackup)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import com.tangem.domain.feedback.GetCardInfoUseCase
|
||||||
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||||
import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase
|
import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase
|
||||||
import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase
|
import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase
|
||||||
|
import com.tangem.domain.onboarding.repository.OnboardingRepository
|
||||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||||
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
|
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
|
||||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||||
|
|
@ -188,6 +189,9 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
|
||||||
private val onboardingV2FeatureToggles: OnboardingV2FeatureToggles
|
private val onboardingV2FeatureToggles: OnboardingV2FeatureToggles
|
||||||
get() = entryPoint.getOnboardingV2FeatureToggles()
|
get() = entryPoint.getOnboardingV2FeatureToggles()
|
||||||
|
|
||||||
|
private val onboardingRepository: OnboardingRepository
|
||||||
|
get() = entryPoint.getOnboardingRepository()
|
||||||
|
|
||||||
private val dispatchers: CoroutineDispatcherProvider
|
private val dispatchers: CoroutineDispatcherProvider
|
||||||
get() = entryPoint.getCoroutineDispatcherProvider()
|
get() = entryPoint.getCoroutineDispatcherProvider()
|
||||||
|
|
||||||
|
|
@ -273,6 +277,7 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
|
||||||
onrampFeatureToggles = onrampFeatureToggles,
|
onrampFeatureToggles = onrampFeatureToggles,
|
||||||
environmentConfigStorage = environmentConfigStorage,
|
environmentConfigStorage = environmentConfigStorage,
|
||||||
onboardingV2FeatureToggles = onboardingV2FeatureToggles,
|
onboardingV2FeatureToggles = onboardingV2FeatureToggles,
|
||||||
|
onboardingRepository = onboardingRepository,
|
||||||
excludedBlockchains = excludedBlockchains,
|
excludedBlockchains = excludedBlockchains,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -148,8 +148,14 @@ class DialogManager : StoreSubscriber<GlobalState> {
|
||||||
is BackupDialog.AttestationFailed -> AttestationFailedDialog.create(context)
|
is BackupDialog.AttestationFailed -> AttestationFailedDialog.create(context)
|
||||||
is BackupDialog.AddMoreBackupCards -> AddMoreBackupCardsDialog.create(context)
|
is BackupDialog.AddMoreBackupCards -> AddMoreBackupCardsDialog.create(context)
|
||||||
is BackupDialog.BackupInProgress -> BackupInProgressDialog.create(context)
|
is BackupDialog.BackupInProgress -> BackupInProgressDialog.create(context)
|
||||||
is BackupDialog.UnfinishedBackupFound -> UnfinishedBackupFoundDialog.create(context)
|
is BackupDialog.UnfinishedBackupFound -> UnfinishedBackupFoundDialog.create(
|
||||||
is BackupDialog.ConfirmDiscardingBackup -> ConfirmDiscardingBackupDialog.create(context)
|
context = context,
|
||||||
|
scanResponse = state.dialog.scanResponse,
|
||||||
|
)
|
||||||
|
is BackupDialog.ConfirmDiscardingBackup -> ConfirmDiscardingBackupDialog.create(
|
||||||
|
context = context,
|
||||||
|
unfinishedBackupScanResponse = state.dialog.scanResponse,
|
||||||
|
)
|
||||||
is BackupDialog.ResetBackupCard -> ResetBackupCardDialog.create(
|
is BackupDialog.ResetBackupCard -> ResetBackupCardDialog.create(
|
||||||
context = context,
|
context = context,
|
||||||
cardId = state.dialog.cardId,
|
cardId = state.dialog.cardId,
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package com.tangem.tap.features.onboarding.products.wallet.redux
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import com.tangem.common.CompletionResult
|
import com.tangem.common.CompletionResult
|
||||||
import com.tangem.common.card.Card
|
import com.tangem.common.card.Card
|
||||||
|
import com.tangem.domain.models.scan.ScanResponse
|
||||||
import com.tangem.domain.wallets.models.UserWalletId
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
import com.tangem.feature.onboarding.data.model.CreateWalletResponse
|
import com.tangem.feature.onboarding.data.model.CreateWalletResponse
|
||||||
import com.tangem.feature.onboarding.presentation.wallet2.analytics.SeedPhraseSource
|
import com.tangem.feature.onboarding.presentation.wallet2.analytics.SeedPhraseSource
|
||||||
|
|
@ -98,7 +99,10 @@ sealed class BackupAction : Action {
|
||||||
|
|
||||||
data object DiscardBackup : BackupAction()
|
data object DiscardBackup : BackupAction()
|
||||||
data object DiscardSavedBackup : BackupAction()
|
data object DiscardSavedBackup : BackupAction()
|
||||||
data object ResumeFoundUnfinishedBackup : BackupAction()
|
|
||||||
|
data class ResumeFoundUnfinishedBackup(
|
||||||
|
val unfinishedBackupScanResponse: ScanResponse?,
|
||||||
|
) : BackupAction()
|
||||||
|
|
||||||
data class ResetBackupCard(val cardId: String) : BackupAction()
|
data class ResetBackupCard(val cardId: String) : BackupAction()
|
||||||
}
|
}
|
||||||
|
|
@ -581,21 +581,50 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
|
||||||
store.state.globalState.onboardingState.onboardingManager?.finishActivation(it)
|
store.state.globalState.onboardingState.onboardingManager?.finishActivation(it)
|
||||||
}
|
}
|
||||||
backupService.discardSavedBackup()
|
backupService.discardSavedBackup()
|
||||||
|
|
||||||
|
val isOnboardingV2Enabled = store.inject(DaggerGraphState::onboardingV2FeatureToggles)
|
||||||
|
.isOnboardingV2Enabled
|
||||||
|
|
||||||
|
if (isOnboardingV2Enabled) {
|
||||||
|
val onboardingRepository = store.inject(DaggerGraphState::onboardingRepository)
|
||||||
|
onboardingRepository.clearUnfinishedFinalizeOnboarding()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is BackupAction.CheckForUnfinishedBackup -> {
|
is BackupAction.CheckForUnfinishedBackup -> {
|
||||||
if (backupService.hasIncompletedBackup) {
|
val isOnboardingV2Enabled = store.inject(DaggerGraphState::onboardingV2FeatureToggles).isOnboardingV2Enabled
|
||||||
store.dispatch(GlobalAction.ShowDialog(BackupDialog.UnfinishedBackupFound))
|
|
||||||
|
if (isOnboardingV2Enabled) {
|
||||||
|
val onboardingRepository = store.inject(DaggerGraphState::onboardingRepository)
|
||||||
|
|
||||||
|
mainScope.launch {
|
||||||
|
val onboardingScanResponse = onboardingRepository.getUnfinishedFinalizeOnboarding() ?: return@launch
|
||||||
|
store.dispatch(GlobalAction.ShowDialog(BackupDialog.UnfinishedBackupFound(onboardingScanResponse)))
|
||||||
|
}
|
||||||
|
} else if (backupService.hasIncompletedBackup) {
|
||||||
|
store.dispatch(GlobalAction.ShowDialog(BackupDialog.UnfinishedBackupFound()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is BackupAction.ResumeFoundUnfinishedBackup -> {
|
is BackupAction.ResumeFoundUnfinishedBackup -> {
|
||||||
store.dispatch(
|
if (action.unfinishedBackupScanResponse != null) {
|
||||||
GlobalAction.Onboarding.StartForUnfinishedBackup(
|
// onboarding V2
|
||||||
addedBackupCardsCount = backupService.addedBackupCardsCount,
|
store.dispatchNavigationAction {
|
||||||
),
|
push(
|
||||||
)
|
AppRoute.Onboarding(
|
||||||
|
scanResponse = action.unfinishedBackupScanResponse,
|
||||||
|
startFromBackup = false,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
store.dispatch(
|
||||||
|
GlobalAction.Onboarding.StartForUnfinishedBackup(
|
||||||
|
addedBackupCardsCount = backupService.addedBackupCardsCount,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
store.dispatchNavigationAction { push(AppRoute.OnboardingWallet()) }
|
store.dispatchNavigationAction { push(AppRoute.OnboardingWallet()) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
is BackupAction.SkipBackup -> {
|
is BackupAction.SkipBackup -> {
|
||||||
Analytics.send(Onboarding.Backup.Skipped())
|
Analytics.send(Onboarding.Backup.Skipped())
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package com.tangem.tap.features.onboarding.products.wallet.redux
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import com.tangem.common.card.Card
|
import com.tangem.common.card.Card
|
||||||
|
import com.tangem.domain.models.scan.ScanResponse
|
||||||
import com.tangem.domain.redux.StateDialog
|
import com.tangem.domain.redux.StateDialog
|
||||||
import com.tangem.domain.wallets.models.UserWalletId
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
import org.rekotlin.StateType
|
import org.rekotlin.StateType
|
||||||
|
|
@ -99,22 +100,29 @@ enum class AccessCodeError {
|
||||||
typealias CardId = String
|
typealias CardId = String
|
||||||
|
|
||||||
sealed class BackupStep {
|
sealed class BackupStep {
|
||||||
object InitBackup : BackupStep()
|
data object InitBackup : BackupStep()
|
||||||
object ScanOriginCard : BackupStep()
|
data object ScanOriginCard : BackupStep()
|
||||||
object AddBackupCards : BackupStep()
|
data object AddBackupCards : BackupStep()
|
||||||
object SetAccessCode : BackupStep()
|
data object SetAccessCode : BackupStep()
|
||||||
object EnterAccessCode : BackupStep()
|
data object EnterAccessCode : BackupStep()
|
||||||
object ReenterAccessCode : BackupStep()
|
data object ReenterAccessCode : BackupStep()
|
||||||
object WritePrimaryCard : BackupStep()
|
data object WritePrimaryCard : BackupStep()
|
||||||
data class WriteBackupCard(val cardNumber: Int) : BackupStep()
|
data class WriteBackupCard(val cardNumber: Int) : BackupStep()
|
||||||
object Finished : BackupStep()
|
data object Finished : BackupStep()
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class BackupDialog : StateDialog {
|
sealed class BackupDialog : StateDialog {
|
||||||
object AttestationFailed : BackupDialog()
|
data object AttestationFailed : BackupDialog()
|
||||||
object AddMoreBackupCards : BackupDialog()
|
data object AddMoreBackupCards : BackupDialog()
|
||||||
object BackupInProgress : BackupDialog()
|
data object BackupInProgress : BackupDialog()
|
||||||
object UnfinishedBackupFound : BackupDialog()
|
|
||||||
object ConfirmDiscardingBackup : BackupDialog()
|
data class UnfinishedBackupFound(
|
||||||
|
val scanResponse: ScanResponse? = null,
|
||||||
|
) : BackupDialog()
|
||||||
|
|
||||||
|
data class ConfirmDiscardingBackup(
|
||||||
|
val scanResponse: ScanResponse? = null,
|
||||||
|
) : BackupDialog()
|
||||||
|
|
||||||
data class ResetBackupCard(val cardId: String) : BackupDialog()
|
data class ResetBackupCard(val cardId: String) : BackupDialog()
|
||||||
}
|
}
|
||||||
|
|
@ -3,18 +3,19 @@ package com.tangem.tap.features.onboarding.products.wallet.ui.dialogs
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import com.tangem.domain.models.scan.ScanResponse
|
||||||
import com.tangem.tap.common.redux.global.GlobalAction
|
import com.tangem.tap.common.redux.global.GlobalAction
|
||||||
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction
|
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction
|
||||||
import com.tangem.tap.store
|
import com.tangem.tap.store
|
||||||
import com.tangem.wallet.R
|
import com.tangem.wallet.R
|
||||||
|
|
||||||
object ConfirmDiscardingBackupDialog {
|
object ConfirmDiscardingBackupDialog {
|
||||||
fun create(context: Context): AlertDialog {
|
fun create(context: Context, unfinishedBackupScanResponse: ScanResponse? = null): AlertDialog {
|
||||||
return MaterialAlertDialogBuilder(context, R.style.CustomMaterialDialog).apply {
|
return MaterialAlertDialogBuilder(context, R.style.CustomMaterialDialog).apply {
|
||||||
setTitle(R.string.welcome_interrupted_backup_discard_title)
|
setTitle(R.string.welcome_interrupted_backup_discard_title)
|
||||||
setMessage(R.string.welcome_interrupted_backup_discard_message)
|
setMessage(R.string.welcome_interrupted_backup_discard_message)
|
||||||
setPositiveButton(R.string.welcome_interrupted_backup_discard_resume) { _, _ ->
|
setPositiveButton(R.string.welcome_interrupted_backup_discard_resume) { _, _ ->
|
||||||
store.dispatch(BackupAction.ResumeFoundUnfinishedBackup)
|
store.dispatch(BackupAction.ResumeFoundUnfinishedBackup(unfinishedBackupScanResponse))
|
||||||
}
|
}
|
||||||
setNegativeButton(R.string.welcome_interrupted_backup_discard_discard) { _, _ ->
|
setNegativeButton(R.string.welcome_interrupted_backup_discard_discard) { _, _ ->
|
||||||
store.dispatch(BackupAction.DiscardSavedBackup)
|
store.dispatch(BackupAction.DiscardSavedBackup)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package com.tangem.tap.features.onboarding.products.wallet.ui.dialogs
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import com.tangem.domain.models.scan.ScanResponse
|
||||||
import com.tangem.tap.common.redux.global.GlobalAction
|
import com.tangem.tap.common.redux.global.GlobalAction
|
||||||
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction
|
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction
|
||||||
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupDialog
|
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupDialog
|
||||||
|
|
@ -10,17 +11,17 @@ import com.tangem.tap.store
|
||||||
import com.tangem.wallet.R
|
import com.tangem.wallet.R
|
||||||
|
|
||||||
object UnfinishedBackupFoundDialog {
|
object UnfinishedBackupFoundDialog {
|
||||||
fun create(context: Context): AlertDialog {
|
fun create(context: Context, scanResponse: ScanResponse? = null): AlertDialog {
|
||||||
return MaterialAlertDialogBuilder(context, R.style.CustomMaterialDialog).apply {
|
return MaterialAlertDialogBuilder(context, R.style.CustomMaterialDialog).apply {
|
||||||
setTitle(R.string.common_warning)
|
setTitle(R.string.common_warning)
|
||||||
setMessage(R.string.welcome_interrupted_backup_alert_message)
|
setMessage(R.string.welcome_interrupted_backup_alert_message)
|
||||||
setPositiveButton(R.string.welcome_interrupted_backup_alert_resume) { _, _ ->
|
setPositiveButton(R.string.welcome_interrupted_backup_alert_resume) { _, _ ->
|
||||||
store.dispatch(GlobalAction.HideDialog)
|
store.dispatch(GlobalAction.HideDialog)
|
||||||
store.dispatch(BackupAction.ResumeFoundUnfinishedBackup)
|
store.dispatch(BackupAction.ResumeFoundUnfinishedBackup(scanResponse))
|
||||||
}
|
}
|
||||||
setNegativeButton(R.string.welcome_interrupted_backup_alert_discard) { _, _ ->
|
setNegativeButton(R.string.welcome_interrupted_backup_alert_discard) { _, _ ->
|
||||||
store.dispatch(GlobalAction.HideDialog)
|
store.dispatch(GlobalAction.HideDialog)
|
||||||
store.dispatch(GlobalAction.ShowDialog(BackupDialog.ConfirmDiscardingBackup))
|
store.dispatch(GlobalAction.ShowDialog(BackupDialog.ConfirmDiscardingBackup(scanResponse)))
|
||||||
}
|
}
|
||||||
setCancelable(false)
|
setCancelable(false)
|
||||||
}.create()
|
}.create()
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import com.tangem.domain.feedback.GetCardInfoUseCase
|
||||||
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||||
import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase
|
import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase
|
||||||
import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase
|
import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase
|
||||||
|
import com.tangem.domain.onboarding.repository.OnboardingRepository
|
||||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||||
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
|
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
|
||||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||||
|
|
@ -72,5 +73,6 @@ data class DaggerGraphState(
|
||||||
val onrampFeatureToggles: OnrampFeatureToggles? = null,
|
val onrampFeatureToggles: OnrampFeatureToggles? = null,
|
||||||
val environmentConfigStorage: EnvironmentConfigStorage? = null,
|
val environmentConfigStorage: EnvironmentConfigStorage? = null,
|
||||||
val onboardingV2FeatureToggles: OnboardingV2FeatureToggles? = null,
|
val onboardingV2FeatureToggles: OnboardingV2FeatureToggles? = null,
|
||||||
|
val onboardingRepository: OnboardingRepository? = null,
|
||||||
val excludedBlockchains: ExcludedBlockchains? = null,
|
val excludedBlockchains: ExcludedBlockchains? = null,
|
||||||
) : StateType
|
) : StateType
|
||||||
|
|
@ -28,6 +28,7 @@ dependencies {
|
||||||
implementation(projects.domain.txhistory.models)
|
implementation(projects.domain.txhistory.models)
|
||||||
implementation(projects.domain.staking.models)
|
implementation(projects.domain.staking.models)
|
||||||
implementation(projects.domain.onramp.models)
|
implementation(projects.domain.onramp.models)
|
||||||
|
implementation(projects.domain.models)
|
||||||
|
|
||||||
/** Tangem libraries */
|
/** Tangem libraries */
|
||||||
implementation(deps.tangem.blockchain)
|
implementation(deps.tangem.blockchain)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import com.tangem.datasource.api.common.adapter.DateTimeAdapter
|
||||||
import com.tangem.datasource.api.common.adapter.LocalDateAdapter
|
import com.tangem.datasource.api.common.adapter.LocalDateAdapter
|
||||||
import com.tangem.datasource.api.common.adapter.addStakeKitEnumFallbackAdapters
|
import com.tangem.datasource.api.common.adapter.addStakeKitEnumFallbackAdapters
|
||||||
import com.tangem.datasource.local.config.providers.models.ProviderModel
|
import com.tangem.datasource.local.config.providers.models.ProviderModel
|
||||||
|
import com.tangem.domain.models.scan.serialization.*
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
|
|
@ -49,7 +50,16 @@ class MoshiModule {
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideSdkMoshiConverter(): MoshiJsonConverter {
|
fun provideSdkMoshiConverter(): MoshiJsonConverter {
|
||||||
return MoshiJsonConverter(
|
return MoshiJsonConverter(
|
||||||
adapters = listOf(BigDecimalAdapter()) + MoshiJsonConverter.getTangemSdkAdapters(),
|
adapters = MoshiJsonConverter.getTangemSdkAdapters() +
|
||||||
|
listOf(
|
||||||
|
BigDecimalAdapter(),
|
||||||
|
WalletDerivedKeysMapAdapter(),
|
||||||
|
ScanResponseDerivedKeysMapAdapter(),
|
||||||
|
ByteArrayKeyAdapter(),
|
||||||
|
ExtendedPublicKeysMapAdapter(),
|
||||||
|
CardBackupStatusAdapter(),
|
||||||
|
DerivationPathAdapterWithMigration(),
|
||||||
|
),
|
||||||
typedAdapters = MoshiJsonConverter.getTangemSdkTypedAdapters(),
|
typedAdapters = MoshiJsonConverter.getTangemSdkTypedAdapters(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,8 @@ object PreferencesKeys {
|
||||||
|
|
||||||
val ONRAMP_TRANSACTIONS_STATUSES_KEY by lazy { stringPreferencesKey(name = "onrampTransactionsStatuses") }
|
val ONRAMP_TRANSACTIONS_STATUSES_KEY by lazy { stringPreferencesKey(name = "onrampTransactionsStatuses") }
|
||||||
|
|
||||||
|
val ONBOARDING_FINALIZE_SCAN_RESPONSE_KEY by lazy { stringPreferencesKey(name = "onboardingFinalizeScanResponse") }
|
||||||
|
|
||||||
val IS_GOOGLE_SERVICES_AVAILABLE_KEY by lazy { booleanPreferencesKey(name = "isGoogleServicesAvailable") }
|
val IS_GOOGLE_SERVICES_AVAILABLE_KEY by lazy { booleanPreferencesKey(name = "isGoogleServicesAvailable") }
|
||||||
|
|
||||||
val IS_GOOGLE_PAY_AVAILABLE_KEY by lazy { booleanPreferencesKey(name = "isGooglePayAvailable") }
|
val IS_GOOGLE_PAY_AVAILABLE_KEY by lazy { booleanPreferencesKey(name = "isGooglePayAvailable") }
|
||||||
|
|
|
||||||
|
|
@ -26,5 +26,6 @@ dependencies {
|
||||||
|
|
||||||
// region Domain modules
|
// region Domain modules
|
||||||
implementation(projects.domain.onboarding)
|
implementation(projects.domain.onboarding)
|
||||||
|
implementation(projects.domain.models)
|
||||||
// endregion
|
// endregion
|
||||||
}
|
}
|
||||||
|
|
@ -2,9 +2,8 @@ package com.tangem.data.onboarding
|
||||||
|
|
||||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||||
import com.tangem.datasource.local.preferences.utils.get
|
import com.tangem.datasource.local.preferences.utils.*
|
||||||
import com.tangem.datasource.local.preferences.utils.getSyncOrDefault
|
import com.tangem.domain.models.scan.ScanResponse
|
||||||
import com.tangem.datasource.local.preferences.utils.store
|
|
||||||
import com.tangem.domain.onboarding.repository.OnboardingRepository
|
import com.tangem.domain.onboarding.repository.OnboardingRepository
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
|
@ -30,4 +29,23 @@ internal class DefaultOnboardingRepository(
|
||||||
override suspend fun saveTwinsOnboardingShown() {
|
override suspend fun saveTwinsOnboardingShown() {
|
||||||
appPreferencesStore.store(key = PreferencesKeys.WAS_TWINS_ONBOARDING_SHOWN, value = true)
|
appPreferencesStore.store(key = PreferencesKeys.WAS_TWINS_ONBOARDING_SHOWN, value = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getUnfinishedFinalizeOnboarding(): ScanResponse? {
|
||||||
|
return appPreferencesStore
|
||||||
|
.getObjectSyncOrNull<ScanResponse>(PreferencesKeys.ONBOARDING_FINALIZE_SCAN_RESPONSE_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun saveUnfinishedFinalizeOnboarding(scanResponse: ScanResponse) {
|
||||||
|
appPreferencesStore
|
||||||
|
.storeObject<ScanResponse>(
|
||||||
|
PreferencesKeys.ONBOARDING_FINALIZE_SCAN_RESPONSE_KEY,
|
||||||
|
scanResponse,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun clearUnfinishedFinalizeOnboarding() {
|
||||||
|
appPreferencesStore.editData { mutablePreferences ->
|
||||||
|
mutablePreferences.remove(PreferencesKeys.ONBOARDING_FINALIZE_SCAN_RESPONSE_KEY)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -6,4 +6,6 @@ plugins {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(deps.arrow.core)
|
implementation(deps.arrow.core)
|
||||||
implementation(deps.kotlin.coroutines)
|
implementation(deps.kotlin.coroutines)
|
||||||
|
|
||||||
|
implementation(projects.domain.models)
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.tangem.domain.onboarding.repository
|
package com.tangem.domain.onboarding.repository
|
||||||
|
|
||||||
|
import com.tangem.domain.models.scan.ScanResponse
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -15,4 +16,10 @@ interface OnboardingRepository {
|
||||||
suspend fun wasTwinsOnboardingShownSync(): Boolean
|
suspend fun wasTwinsOnboardingShownSync(): Boolean
|
||||||
|
|
||||||
suspend fun saveTwinsOnboardingShown()
|
suspend fun saveTwinsOnboardingShown()
|
||||||
|
|
||||||
|
suspend fun getUnfinishedFinalizeOnboarding(): ScanResponse?
|
||||||
|
|
||||||
|
suspend fun saveUnfinishedFinalizeOnboarding(scanResponse: ScanResponse)
|
||||||
|
|
||||||
|
suspend fun clearUnfinishedFinalizeOnboarding()
|
||||||
}
|
}
|
||||||
|
|
@ -25,6 +25,7 @@ dependencies {
|
||||||
implementation(projects.core.res)
|
implementation(projects.core.res)
|
||||||
implementation(projects.core.decompose)
|
implementation(projects.core.decompose)
|
||||||
implementation(projects.core.navigation)
|
implementation(projects.core.navigation)
|
||||||
|
implementation(projects.core.datasource)
|
||||||
|
|
||||||
/** Common */
|
/** Common */
|
||||||
implementation(projects.common.ui)
|
implementation(projects.common.ui)
|
||||||
|
|
@ -39,6 +40,7 @@ dependencies {
|
||||||
implementation(projects.domain.wallets.models)
|
implementation(projects.domain.wallets.models)
|
||||||
implementation(projects.domain.legacy)
|
implementation(projects.domain.legacy)
|
||||||
implementation(projects.domain.settings)
|
implementation(projects.domain.settings)
|
||||||
|
implementation(projects.domain.onboarding)
|
||||||
|
|
||||||
/** Tangem libraries */
|
/** Tangem libraries */
|
||||||
implementation(projects.libs.tangemSdkApi)
|
implementation(projects.libs.tangemSdkApi)
|
||||||
|
|
@ -62,6 +64,7 @@ dependencies {
|
||||||
implementation(deps.lottie.compose)
|
implementation(deps.lottie.compose)
|
||||||
implementation(deps.decompose.ext.compose)
|
implementation(deps.decompose.ext.compose)
|
||||||
implementation(deps.androidx.activity.compose)
|
implementation(deps.androidx.activity.compose)
|
||||||
|
implementation(deps.androidx.datastore)
|
||||||
|
|
||||||
/** Other libraries */
|
/** Other libraries */
|
||||||
implementation(deps.kotlin.immutable.collections)
|
implementation(deps.kotlin.immutable.collections)
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,6 @@ internal class OnboardingEntryModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
return when (scanResponse.productType) {
|
return when (scanResponse.productType) {
|
||||||
ProductType.Note -> TODO()
|
|
||||||
ProductType.Twins -> TODO()
|
|
||||||
ProductType.Wallet -> OnboardingRoute.MultiWallet(
|
ProductType.Wallet -> OnboardingRoute.MultiWallet(
|
||||||
scanResponse = scanResponse,
|
scanResponse = scanResponse,
|
||||||
withSeedPhraseFlow = false,
|
withSeedPhraseFlow = false,
|
||||||
|
|
@ -71,8 +69,7 @@ internal class OnboardingEntryModel @Inject constructor(
|
||||||
onDone = ::onMultiWalletOnboardingDone,
|
onDone = ::onMultiWalletOnboardingDone,
|
||||||
mode = multiWalletNavigationMode,
|
mode = multiWalletNavigationMode,
|
||||||
)
|
)
|
||||||
ProductType.Start2Coin -> TODO()
|
else -> error("Unsupported")
|
||||||
ProductType.Visa -> TODO()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,6 @@ internal class MultiWalletAccessCodeModel @Inject constructor(
|
||||||
if (checkAccessCode()) {
|
if (checkAccessCode()) {
|
||||||
backupServiceHolder.backupService.get()?.setAccessCode(_uiState.value.accessCodeFirst.text)
|
backupServiceHolder.backupService.get()?.setAccessCode(_uiState.value.accessCodeFirst.text)
|
||||||
|
|
||||||
// TODO delete access code from state
|
|
||||||
// make on done
|
// make on done
|
||||||
params.multiWalletState.update {
|
params.multiWalletState.update {
|
||||||
it.copy(accessCode = OnboardingMultiWalletState.AccessCode(uiState.value.accessCodeFirst.text))
|
it.copy(accessCode = OnboardingMultiWalletState.AccessCode(uiState.value.accessCodeFirst.text))
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.model
|
package com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.model
|
||||||
|
|
||||||
// TODO maybe delete some fields in the future
|
|
||||||
data class BackupState(
|
data class BackupState(
|
||||||
val backupCardsNumber: Int = 0,
|
val numberOfBackupCards: Int = 0,
|
||||||
)
|
)
|
||||||
|
|
@ -15,9 +15,9 @@ import com.tangem.features.onboarding.v2.multiwallet.impl.analytics.OnboardingEv
|
||||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildParams
|
import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildParams
|
||||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.MultiWalletBackupComponent
|
import com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.MultiWalletBackupComponent
|
||||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.ui.backupCardAttestationFailedDialog
|
import com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.ui.backupCardAttestationFailedDialog
|
||||||
|
import com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.ui.onlyOneBackupDeviceDialog
|
||||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.ui.resetBackupCardDialog
|
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.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.BackupServiceHolder
|
||||||
import com.tangem.sdk.api.TangemSdkManager
|
import com.tangem.sdk.api.TangemSdkManager
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
|
@ -70,30 +70,19 @@ class MultiWalletBackupModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getInitState(): MultiWalletBackupUM {
|
private fun getInitState(): MultiWalletBackupUM {
|
||||||
return when (backupService.currentState) {
|
return MultiWalletBackupUM(
|
||||||
BackupService.State.Preparing -> {
|
title = resourceReference(R.string.onboarding_title_no_backup_cards),
|
||||||
MultiWalletBackupUM(
|
bodyText = resourceReference(R.string.onboarding_subtitle_no_backup_cards),
|
||||||
title = resourceReference(R.string.onboarding_title_no_backup_cards),
|
finalizeButtonEnabled = false,
|
||||||
bodyText = resourceReference(R.string.onboarding_subtitle_no_backup_cards),
|
addBackupButtonEnabled = true,
|
||||||
finalizeButtonEnabled = false,
|
addBackupButtonLoading = false,
|
||||||
addBackupButtonEnabled = true,
|
onAddBackupClick = ::startBackupWallet,
|
||||||
addBackupButtonLoading = false,
|
onFinalizeButtonClick = ::onFinalizeClick,
|
||||||
onAddBackupClick = ::startBackupWallet,
|
)
|
||||||
onFinalizeButtonClick = ::onFinalizeClick,
|
|
||||||
onSkipButtonClick = {
|
|
||||||
// eventFlow.tryEmit(Unit)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
// TODO
|
|
||||||
is BackupService.State.FinalizingBackupCard -> TODO()
|
|
||||||
BackupService.State.FinalizingPrimaryCard -> TODO()
|
|
||||||
BackupService.State.Finished -> TODO()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startBackupWallet() {
|
private fun startBackupWallet() {
|
||||||
if (state.value.backupCardsNumber == 0) {
|
if (state.value.numberOfBackupCards == 0) {
|
||||||
backupService.discardSavedBackup()
|
backupService.discardSavedBackup()
|
||||||
}
|
}
|
||||||
val primaryCard = scanResponse.primaryCard
|
val primaryCard = scanResponse.primaryCard
|
||||||
|
|
@ -144,19 +133,35 @@ class MultiWalletBackupModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onFinalizeClick() {
|
private fun onFinalizeClick(fromDialog: Boolean = false) {
|
||||||
when (state.value.backupCardsNumber) {
|
when (state.value.numberOfBackupCards) {
|
||||||
1 -> {
|
1 -> {
|
||||||
// TODO show dialog
|
|
||||||
params.multiWalletState.update { it.copy(isThreeCards = false) }
|
params.multiWalletState.update { it.copy(isThreeCards = false) }
|
||||||
|
|
||||||
|
if (fromDialog.not()) {
|
||||||
|
showOnlyOneBackupWarningDialog()
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
2 -> {
|
2 -> {
|
||||||
params.multiWalletState.update { it.copy(isThreeCards = true) }
|
params.multiWalletState.update { it.copy(isThreeCards = true) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
modelScope.launch { eventFlow.emit(MultiWalletBackupComponent.Event.Done) }
|
modelScope.launch { eventFlow.emit(MultiWalletBackupComponent.Event.Done) }
|
||||||
|
|
||||||
analyticsHandler.send(OnboardingEvent.Backup.Finished(cardsCount = state.value.backupCardsNumber + 1))
|
analyticsHandler.send(OnboardingEvent.Backup.Finished(cardsCount = state.value.numberOfBackupCards + 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showOnlyOneBackupWarningDialog() {
|
||||||
|
_uiState.update {
|
||||||
|
it.copy(
|
||||||
|
dialog = onlyOneBackupDeviceDialog(
|
||||||
|
onDismiss = { _uiState.update { it.copy(dialog = null) } },
|
||||||
|
onConfirm = { onFinalizeClick(fromDialog = true) },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addBackupCardWithService() {
|
private fun addBackupCardWithService() {
|
||||||
|
|
@ -176,7 +181,7 @@ class MultiWalletBackupModel @Inject constructor(
|
||||||
is CompletionResult.Success -> {
|
is CompletionResult.Success -> {
|
||||||
state.update {
|
state.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
backupCardsNumber = it.backupCardsNumber + 1,
|
numberOfBackupCards = it.numberOfBackupCards + 1,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,12 +191,12 @@ class MultiWalletBackupModel @Inject constructor(
|
||||||
)
|
)
|
||||||
params.backups.update {
|
params.backups.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
card2 = if (state.value.backupCardsNumber == 1) backupCardInfo else it.card2,
|
card2 = if (state.value.numberOfBackupCards == 1) backupCardInfo else it.card2,
|
||||||
card3 = if (state.value.backupCardsNumber == 2) backupCardInfo else it.card3,
|
card3 = if (state.value.numberOfBackupCards == 2) backupCardInfo else it.card3,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
setNumberOfBackupCards(state.value.backupCardsNumber)
|
setNumberOfBackupCards(state.value.numberOfBackupCards)
|
||||||
}
|
}
|
||||||
is CompletionResult.Failure -> {
|
is CompletionResult.Failure -> {
|
||||||
when (val error = result.error) {
|
when (val error = result.error) {
|
||||||
|
|
|
||||||
|
|
@ -28,4 +28,18 @@ internal fun backupCardAttestationFailedDialog(onDismiss: () -> Unit) = MultiWal
|
||||||
onDismiss = onDismiss,
|
onDismiss = onDismiss,
|
||||||
onConfirm = onDismiss,
|
onConfirm = onDismiss,
|
||||||
onCancel = null,
|
onCancel = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun onlyOneBackupDeviceDialog(onDismiss: () -> Unit, onConfirm: () -> Unit) = MultiWalletBackupUMDialog(
|
||||||
|
title = resourceReference(R.string.common_warning),
|
||||||
|
bodyText = resourceReference(R.string.onboarding_alert_message_not_max_backup_cards_added),
|
||||||
|
confirmText = resourceReference(R.string.common_continue),
|
||||||
|
cancelText = resourceReference(R.string.common_cancel),
|
||||||
|
warningCancelColor = false,
|
||||||
|
onDismiss = onDismiss,
|
||||||
|
onConfirm = {
|
||||||
|
onDismiss()
|
||||||
|
onConfirm()
|
||||||
|
},
|
||||||
|
onCancel = onDismiss,
|
||||||
)
|
)
|
||||||
|
|
@ -13,7 +13,6 @@ data class MultiWalletBackupUM(
|
||||||
val finalizeButtonEnabled: Boolean = false,
|
val finalizeButtonEnabled: Boolean = false,
|
||||||
val onAddBackupClick: () -> Unit = {},
|
val onAddBackupClick: () -> Unit = {},
|
||||||
val onFinalizeButtonClick: () -> Unit = {},
|
val onFinalizeButtonClick: () -> Unit = {},
|
||||||
val onSkipButtonClick: () -> Unit = {},
|
|
||||||
val dialog: MultiWalletBackupUMDialog? = null,
|
val dialog: MultiWalletBackupUMDialog? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,6 @@ internal class Wallet1ChooseOptionModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onSkipClick() {
|
fun onSkipClick() {
|
||||||
// TODO show confirmation dialog
|
|
||||||
|
|
||||||
if (skipClicked) return
|
if (skipClicked) return
|
||||||
skipClicked = true
|
skipClicked = true
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||||
import com.tangem.domain.models.scan.CardDTO
|
import com.tangem.domain.models.scan.CardDTO
|
||||||
import com.tangem.domain.models.scan.ScanResponse
|
import com.tangem.domain.models.scan.ScanResponse
|
||||||
import com.tangem.domain.models.scan.isRing
|
import com.tangem.domain.models.scan.isRing
|
||||||
|
import com.tangem.domain.onboarding.repository.OnboardingRepository
|
||||||
import com.tangem.domain.wallets.builder.UserWalletBuilder
|
import com.tangem.domain.wallets.builder.UserWalletBuilder
|
||||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||||
import com.tangem.domain.wallets.models.UserWallet
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
|
|
@ -22,6 +23,7 @@ import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChild
|
||||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.finalize.MultiWalletFinalizeComponent
|
import com.tangem.features.onboarding.v2.multiwallet.impl.child.finalize.MultiWalletFinalizeComponent
|
||||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.finalize.ui.state.MultiWalletFinalizeUM
|
import com.tangem.features.onboarding.v2.multiwallet.impl.child.finalize.ui.state.MultiWalletFinalizeUM
|
||||||
import com.tangem.features.onboarding.v2.multiwallet.impl.common.ui.resetCardDialog
|
import com.tangem.features.onboarding.v2.multiwallet.impl.common.ui.resetCardDialog
|
||||||
|
import com.tangem.features.onboarding.v2.multiwallet.impl.model.OnboardingMultiWalletState.FinalizeStage.*
|
||||||
import com.tangem.operations.backup.BackupService
|
import com.tangem.operations.backup.BackupService
|
||||||
import com.tangem.sdk.api.BackupServiceHolder
|
import com.tangem.sdk.api.BackupServiceHolder
|
||||||
import com.tangem.sdk.api.TangemSdkManager
|
import com.tangem.sdk.api.TangemSdkManager
|
||||||
|
|
@ -46,23 +48,66 @@ internal class MultiWalletFinalizeModel @Inject constructor(
|
||||||
private val generateWalletNameUseCase: GenerateWalletNameUseCase,
|
private val generateWalletNameUseCase: GenerateWalletNameUseCase,
|
||||||
private val userWalletsListManager: UserWalletsListManager,
|
private val userWalletsListManager: UserWalletsListManager,
|
||||||
private val cardRepository: CardRepository,
|
private val cardRepository: CardRepository,
|
||||||
|
private val onboardingRepository: OnboardingRepository,
|
||||||
) : Model() {
|
) : Model() {
|
||||||
|
|
||||||
private val params = paramsContainer.require<MultiWalletChildParams>()
|
private val params = paramsContainer.require<MultiWalletChildParams>()
|
||||||
private val multiWalletState get() = params.multiWalletState
|
private val multiWalletState get() = params.multiWalletState
|
||||||
private val _uiState = MutableStateFlow(
|
private val _uiState = MutableStateFlow(getInitialState())
|
||||||
MultiWalletFinalizeUM(
|
|
||||||
isRing = backupServiceHolder.backupService.get()?.primaryCardBatchId?.let { isRing(it) } ?: false,
|
|
||||||
onScanClick = ::onLinkClick,
|
|
||||||
cardNumber = backupServiceHolder.backupService.get()?.primaryCardId?.lastMasked().orEmpty(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
private val backupCardIds = backupServiceHolder.backupService.get()?.backupCardIds.orEmpty()
|
|
||||||
private var walletHasBackupError = false
|
|
||||||
|
|
||||||
|
private val backupCardIds = backupServiceHolder.backupService.get()?.backupCardIds.orEmpty()
|
||||||
|
|
||||||
|
private var walletHasBackupError = false
|
||||||
val uiState = _uiState.asStateFlow()
|
val uiState = _uiState.asStateFlow()
|
||||||
|
|
||||||
val onEvent = MutableSharedFlow<MultiWalletFinalizeComponent.Event>()
|
val onEvent = MutableSharedFlow<MultiWalletFinalizeComponent.Event>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
// save scan response to preferences to be able
|
||||||
|
// to continue finalize process after app restart
|
||||||
|
modelScope.launch {
|
||||||
|
onboardingRepository.saveUnfinishedFinalizeOnboarding(
|
||||||
|
scanResponse = multiWalletState.value.currentScanResponse,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getInitialState(): MultiWalletFinalizeUM {
|
||||||
|
val backupService = backupServiceHolder.backupService.get() ?: return MultiWalletFinalizeUM()
|
||||||
|
val initialStep = getInitialStep()
|
||||||
|
|
||||||
|
val batchId = when (initialStep) {
|
||||||
|
MultiWalletFinalizeUM.Step.Primary -> backupService.primaryCardBatchId
|
||||||
|
MultiWalletFinalizeUM.Step.BackupDevice1 -> backupService.backupCardsBatchIds.getOrNull(0)
|
||||||
|
MultiWalletFinalizeUM.Step.BackupDevice2 -> backupService.backupCardsBatchIds.getOrNull(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
val cardId = when (initialStep) {
|
||||||
|
MultiWalletFinalizeUM.Step.Primary -> backupService.primaryCardId
|
||||||
|
MultiWalletFinalizeUM.Step.BackupDevice1 -> backupService.backupCardIds.getOrNull(0)
|
||||||
|
MultiWalletFinalizeUM.Step.BackupDevice2 -> backupService.backupCardIds.getOrNull(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return MultiWalletFinalizeUM(
|
||||||
|
isRing = batchId?.let { isRing(it) } ?: false,
|
||||||
|
onScanClick = ::onLinkClick,
|
||||||
|
scanPrimary = initialStep == MultiWalletFinalizeUM.Step.Primary,
|
||||||
|
cardNumber = cardId?.lastMasked().orEmpty(),
|
||||||
|
step = getInitialStep(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getInitialStep(): MultiWalletFinalizeUM.Step {
|
||||||
|
val startFromFinalize =
|
||||||
|
params.multiWalletState.value.startFromFinalize ?: return MultiWalletFinalizeUM.Step.Primary
|
||||||
|
|
||||||
|
return when (startFromFinalize) {
|
||||||
|
ScanPrimaryCard -> MultiWalletFinalizeUM.Step.Primary
|
||||||
|
ScanBackupFirstCard -> MultiWalletFinalizeUM.Step.BackupDevice1
|
||||||
|
ScanBackupSecondCard -> MultiWalletFinalizeUM.Step.BackupDevice2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun onLinkClick() {
|
private fun onLinkClick() {
|
||||||
when (_uiState.value.step) {
|
when (_uiState.value.step) {
|
||||||
MultiWalletFinalizeUM.Step.Primary -> {
|
MultiWalletFinalizeUM.Step.Primary -> {
|
||||||
|
|
@ -79,8 +124,6 @@ internal class MultiWalletFinalizeModel @Inject constructor(
|
||||||
|
|
||||||
private fun writePrimaryCard() {
|
private fun writePrimaryCard() {
|
||||||
val backupService = backupServiceHolder.backupService.get() ?: return
|
val backupService = backupServiceHolder.backupService.get() ?: return
|
||||||
// TODO store.dispatchOnMain(BackupAction.SetHasRing(hasRing = isRing))
|
|
||||||
|
|
||||||
val primaryCardBatchId = backupService.primaryCardBatchId ?: return
|
val primaryCardBatchId = backupService.primaryCardBatchId ?: return
|
||||||
val isRing = isRing(primaryCardBatchId)
|
val isRing = isRing(primaryCardBatchId)
|
||||||
val iconScanRes = if (isRing) R.drawable.img_hand_scan_ring else null
|
val iconScanRes = if (isRing) R.drawable.img_hand_scan_ring else null
|
||||||
|
|
@ -107,9 +150,6 @@ internal class MultiWalletFinalizeModel @Inject constructor(
|
||||||
|
|
||||||
private fun writeBackupCard(cardIndex: Int) {
|
private fun writeBackupCard(cardIndex: Int) {
|
||||||
val backupService = backupServiceHolder.backupService.get() ?: return
|
val backupService = backupServiceHolder.backupService.get() ?: return
|
||||||
|
|
||||||
// TODO store.dispatchOnMain(BackupAction.SetHasRing(hasRing = isRing))
|
|
||||||
|
|
||||||
val backupCardBatchId = backupService.backupCardsBatchIds.getOrNull(cardIndex) ?: return
|
val backupCardBatchId = backupService.backupCardsBatchIds.getOrNull(cardIndex) ?: return
|
||||||
val isRing = isRing(backupCardBatchId)
|
val isRing = isRing(backupCardBatchId)
|
||||||
val iconScanRes = if (isRing) R.drawable.img_hand_scan_ring else null
|
val iconScanRes = if (isRing) R.drawable.img_hand_scan_ring else null
|
||||||
|
|
@ -151,8 +191,6 @@ internal class MultiWalletFinalizeModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun finishBackup() {
|
private fun finishBackup() {
|
||||||
// TODO Analytics.send(Onboarding.Backup.Finished(backupState.backupCardsNumber))
|
|
||||||
|
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
val scanResponse = params.multiWalletState.value.currentScanResponse
|
val scanResponse = params.multiWalletState.value.currentScanResponse
|
||||||
val userWallet = createUserWallet(scanResponse)
|
val userWallet = createUserWallet(scanResponse)
|
||||||
|
|
@ -183,8 +221,12 @@ internal class MultiWalletFinalizeModel @Inject constructor(
|
||||||
it.copy(resultUserWallet = userWallet)
|
it.copy(resultUserWallet = userWallet)
|
||||||
}
|
}
|
||||||
|
|
||||||
cardRepository.finishCardActivation(scanResponse.card.cardId)
|
// user wallet is fully created and saved, remove scan response from preferences
|
||||||
|
// to prevent showing finalize screen dialog on next app start
|
||||||
|
onboardingRepository.clearUnfinishedFinalizeOnboarding()
|
||||||
|
|
||||||
|
cardRepository.finishCardActivation(scanResponse.card.cardId)
|
||||||
|
backupServiceHolder.backupService.get()?.discardSavedBackup()
|
||||||
onEvent.emit(MultiWalletFinalizeComponent.Event.ThreeBackupCardsAdded)
|
onEvent.emit(MultiWalletFinalizeComponent.Event.ThreeBackupCardsAdded)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -201,7 +243,7 @@ internal class MultiWalletFinalizeModel @Inject constructor(
|
||||||
|
|
||||||
@Suppress("MagicNumber")
|
@Suppress("MagicNumber")
|
||||||
private fun ScanResponse.updateScanResponseAfterBackup(): ScanResponse {
|
private fun ScanResponse.updateScanResponseAfterBackup(): ScanResponse {
|
||||||
val cardsCount = if (params.multiWalletState.value.isThreeCards) 3 else 2
|
val cardsCount = if (params.multiWalletState.value.isThreeCards) 2 else 1
|
||||||
|
|
||||||
val card = card.copy(
|
val card = card.copy(
|
||||||
backupStatus = CardDTO.BackupStatus.Active(cardCount = cardsCount),
|
backupStatus = CardDTO.BackupStatus.Active(cardCount = cardsCount),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package com.tangem.features.onboarding.v2.multiwallet.impl.child.finalize.ui
|
package com.tangem.features.onboarding.v2.multiwallet.impl.child.finalize.ui
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
|
|
||||||
|
|
@ -135,8 +135,6 @@ internal class MultiWalletSeedPhraseModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun openGeneratedSeedPhrase() {
|
private fun openGeneratedSeedPhrase() {
|
||||||
// TODO analytics SeedPhraseEvents.ButtonGenerateSeedPhrase
|
|
||||||
|
|
||||||
val words12 = state.value.generatedWords12 ?: return
|
val words12 = state.value.generatedWords12 ?: return
|
||||||
val words24 = state.value.generatedWords24 ?: return
|
val words24 = state.value.generatedWords24 ?: return
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,18 @@ import com.tangem.core.decompose.di.ComponentScoped
|
||||||
import com.tangem.core.decompose.model.Model
|
import com.tangem.core.decompose.model.Model
|
||||||
import com.tangem.core.decompose.model.ParamsContainer
|
import com.tangem.core.decompose.model.ParamsContainer
|
||||||
import com.tangem.core.decompose.navigation.Router
|
import com.tangem.core.decompose.navigation.Router
|
||||||
|
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||||
|
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||||
import com.tangem.domain.models.scan.CardDTO
|
import com.tangem.domain.models.scan.CardDTO
|
||||||
import com.tangem.domain.wallets.usecase.GetCardImageUseCase
|
import com.tangem.domain.wallets.usecase.GetCardImageUseCase
|
||||||
import com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletComponent
|
import com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletComponent
|
||||||
import com.tangem.features.onboarding.v2.multiwallet.impl.analytics.OnboardingEvent
|
import com.tangem.features.onboarding.v2.multiwallet.impl.analytics.OnboardingEvent
|
||||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildParams
|
import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildParams
|
||||||
import com.tangem.features.onboarding.v2.multiwallet.impl.common.ui.interruptBackupDialog
|
import com.tangem.features.onboarding.v2.multiwallet.impl.common.ui.interruptBackupDialog
|
||||||
|
import com.tangem.features.onboarding.v2.multiwallet.impl.model.OnboardingMultiWalletState.FinalizeStage
|
||||||
import com.tangem.features.onboarding.v2.multiwallet.impl.ui.state.OnboardingMultiWalletUM
|
import com.tangem.features.onboarding.v2.multiwallet.impl.ui.state.OnboardingMultiWalletUM
|
||||||
|
import com.tangem.operations.backup.BackupService
|
||||||
|
import com.tangem.sdk.api.BackupServiceHolder
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
@ -23,6 +28,8 @@ internal class OnboardingMultiWalletModel @Inject constructor(
|
||||||
override val dispatchers: CoroutineDispatcherProvider,
|
override val dispatchers: CoroutineDispatcherProvider,
|
||||||
private val router: Router,
|
private val router: Router,
|
||||||
private val analyticsHandler: AnalyticsEventHandler,
|
private val analyticsHandler: AnalyticsEventHandler,
|
||||||
|
private val backupServiceHolder: BackupServiceHolder,
|
||||||
|
private val appPreferencesStore: AppPreferencesStore,
|
||||||
) : Model() {
|
) : Model() {
|
||||||
private val params = paramsContainer.require<OnboardingMultiWalletComponent.Params>()
|
private val params = paramsContainer.require<OnboardingMultiWalletComponent.Params>()
|
||||||
private val getCardImageUseCase = GetCardImageUseCase()
|
private val getCardImageUseCase = GetCardImageUseCase()
|
||||||
|
|
@ -35,6 +42,7 @@ internal class OnboardingMultiWalletModel @Inject constructor(
|
||||||
accessCode = null,
|
accessCode = null,
|
||||||
isThreeCards = true,
|
isThreeCards = true,
|
||||||
resultUserWallet = null,
|
resultUserWallet = null,
|
||||||
|
startFromFinalize = getInitialStartFromFinalize(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
val backups = MutableStateFlow(MultiWalletChildParams.Backup())
|
val backups = MutableStateFlow(MultiWalletChildParams.Backup())
|
||||||
|
|
@ -48,10 +56,11 @@ internal class OnboardingMultiWalletModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onBack() {
|
fun onBack() {
|
||||||
_uiState.update {
|
_uiState.update { st ->
|
||||||
it.copy(
|
st.copy(
|
||||||
dialog = interruptBackupDialog(
|
dialog = interruptBackupDialog(
|
||||||
onConfirm = {
|
onConfirm = {
|
||||||
|
removeFinalizeScanResponseState()
|
||||||
router.pop()
|
router.pop()
|
||||||
},
|
},
|
||||||
dismiss = { _uiState.update { it.copy(dialog = null) } },
|
dismiss = { _uiState.update { it.copy(dialog = null) } },
|
||||||
|
|
@ -60,20 +69,30 @@ internal class OnboardingMultiWalletModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun removeFinalizeScanResponseState() {
|
||||||
|
// discarding onboarding means we have to remove scan response from preferences
|
||||||
|
// to prevent showing finalize screen dialog on next app start
|
||||||
|
modelScope.launch {
|
||||||
|
appPreferencesStore.editData { mutablePreferences ->
|
||||||
|
mutablePreferences.remove(PreferencesKeys.ONBOARDING_FINALIZE_SCAN_RESPONSE_KEY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun subscribeToBackups() {
|
private fun subscribeToBackups() {
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
backups.collectLatest { bst ->
|
backups.collectLatest { backup ->
|
||||||
when {
|
when {
|
||||||
_uiState.value.artwork2Url == null && bst.card2 != null -> {
|
_uiState.value.artwork2Url == null && backup.card2 != null -> {
|
||||||
val artwork =
|
val artwork =
|
||||||
getCardImageUseCase.invoke(bst.card2.cardId, bst.card2.cardPublicKey)
|
getCardImageUseCase.invoke(backup.card2.cardId, backup.card2.cardPublicKey)
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(artwork2Url = artwork)
|
it.copy(artwork2Url = artwork)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_uiState.value.artwork3Url == null && bst.card3 != null -> {
|
_uiState.value.artwork3Url == null && backup.card3 != null -> {
|
||||||
val artwork =
|
val artwork =
|
||||||
getCardImageUseCase.invoke(bst.card3.cardId, bst.card3.cardPublicKey)
|
getCardImageUseCase.invoke(backup.card3.cardId, backup.card3.cardPublicKey)
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(artwork3Url = artwork)
|
it.copy(artwork3Url = artwork)
|
||||||
}
|
}
|
||||||
|
|
@ -85,9 +104,10 @@ internal class OnboardingMultiWalletModel @Inject constructor(
|
||||||
|
|
||||||
private fun getInitialStep(): OnboardingMultiWalletState.Step {
|
private fun getInitialStep(): OnboardingMultiWalletState.Step {
|
||||||
val card = params.scanResponse.card
|
val card = params.scanResponse.card
|
||||||
|
val backupService = backupServiceHolder.backupService.get()!!
|
||||||
|
|
||||||
// todo check local storage for scan response
|
|
||||||
return when {
|
return when {
|
||||||
|
backupService.hasIncompletedBackup -> OnboardingMultiWalletState.Step.Finalize
|
||||||
card.wallets.isNotEmpty() && card.backupStatus == CardDTO.BackupStatus.NoBackup ->
|
card.wallets.isNotEmpty() && card.backupStatus == CardDTO.BackupStatus.NoBackup ->
|
||||||
OnboardingMultiWalletState.Step.AddBackupDevice
|
OnboardingMultiWalletState.Step.AddBackupDevice
|
||||||
card.wallets.isNotEmpty() && card.backupStatus?.isActive == true ->
|
card.wallets.isNotEmpty() && card.backupStatus?.isActive == true ->
|
||||||
|
|
@ -97,6 +117,19 @@ internal class OnboardingMultiWalletModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getInitialStartFromFinalize(): FinalizeStage? {
|
||||||
|
val backupService = backupServiceHolder.backupService.get() ?: return null
|
||||||
|
return when (val state = backupService.currentState) {
|
||||||
|
BackupService.State.FinalizingPrimaryCard -> FinalizeStage.ScanPrimaryCard
|
||||||
|
is BackupService.State.FinalizingBackupCard -> if (state.index == 1) {
|
||||||
|
FinalizeStage.ScanBackupFirstCard
|
||||||
|
} else {
|
||||||
|
FinalizeStage.ScanBackupSecondCard
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun initScreenTitle() {
|
private fun initScreenTitle() {
|
||||||
val title = screenTitleByStep(getInitialStep())
|
val title = screenTitleByStep(getInitialStep())
|
||||||
params.titleProvider.changeTitle(title)
|
params.titleProvider.changeTitle(title)
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,13 @@ data class OnboardingMultiWalletState(
|
||||||
val accessCode: AccessCode?,
|
val accessCode: AccessCode?,
|
||||||
val isThreeCards: Boolean,
|
val isThreeCards: Boolean,
|
||||||
val currentScanResponse: ScanResponse,
|
val currentScanResponse: ScanResponse,
|
||||||
val resultUserWallet: UserWallet?, // TODO make callback instead
|
val startFromFinalize: FinalizeStage?,
|
||||||
|
val resultUserWallet: UserWallet?,
|
||||||
) {
|
) {
|
||||||
|
enum class FinalizeStage {
|
||||||
|
ScanPrimaryCard, ScanBackupFirstCard, ScanBackupSecondCard
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wallet1
|
* Wallet1
|
||||||
* CreateWallet -> ChooseBackupOption -> AddBackupDevice -> Finalize -> [Done]
|
* CreateWallet -> ChooseBackupOption -> AddBackupDevice -> Finalize -> [Done]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue