diff --git a/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt b/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt index 22e43ff3d2..2e99af7238 100644 --- a/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt +++ b/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt @@ -24,6 +24,7 @@ import com.tangem.domain.feedback.GetCardInfoUseCase import com.tangem.domain.feedback.SendFeedbackEmailUseCase import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase 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.usercountry.GetUserCountryUseCase import com.tangem.domain.walletmanager.WalletManagersFacade @@ -119,6 +120,8 @@ interface ApplicationEntryPoint { fun getOnboardingV2FeatureToggles(): OnboardingV2FeatureToggles + fun getOnboardingRepository(): OnboardingRepository + fun getCoroutineDispatcherProvider(): CoroutineDispatcherProvider fun getExcludedBlockchains(): ExcludedBlockchains diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index 25ec1c6a48..6d42707997 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -586,7 +586,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac } } - // TODO add onboarding v2 handling store.dispatch(BackupAction.CheckForUnfinishedBackup) } diff --git a/app/src/main/java/com/tangem/tap/TangemApplication.kt b/app/src/main/java/com/tangem/tap/TangemApplication.kt index 0cb1183a0c..a118c8e072 100644 --- a/app/src/main/java/com/tangem/tap/TangemApplication.kt +++ b/app/src/main/java/com/tangem/tap/TangemApplication.kt @@ -38,6 +38,7 @@ import com.tangem.domain.feedback.GetCardInfoUseCase import com.tangem.domain.feedback.SendFeedbackEmailUseCase import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase 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.usercountry.GetUserCountryUseCase import com.tangem.domain.walletmanager.WalletManagersFacade @@ -188,6 +189,9 @@ abstract class TangemApplication : Application(), ImageLoaderFactory { private val onboardingV2FeatureToggles: OnboardingV2FeatureToggles get() = entryPoint.getOnboardingV2FeatureToggles() + private val onboardingRepository: OnboardingRepository + get() = entryPoint.getOnboardingRepository() + private val dispatchers: CoroutineDispatcherProvider get() = entryPoint.getCoroutineDispatcherProvider() @@ -273,6 +277,7 @@ abstract class TangemApplication : Application(), ImageLoaderFactory { onrampFeatureToggles = onrampFeatureToggles, environmentConfigStorage = environmentConfigStorage, onboardingV2FeatureToggles = onboardingV2FeatureToggles, + onboardingRepository = onboardingRepository, excludedBlockchains = excludedBlockchains, ), ), diff --git a/app/src/main/java/com/tangem/tap/common/DialogManager.kt b/app/src/main/java/com/tangem/tap/common/DialogManager.kt index c48a2988f8..0827efeed7 100644 --- a/app/src/main/java/com/tangem/tap/common/DialogManager.kt +++ b/app/src/main/java/com/tangem/tap/common/DialogManager.kt @@ -148,8 +148,14 @@ class DialogManager : StoreSubscriber { is BackupDialog.AttestationFailed -> AttestationFailedDialog.create(context) is BackupDialog.AddMoreBackupCards -> AddMoreBackupCardsDialog.create(context) is BackupDialog.BackupInProgress -> BackupInProgressDialog.create(context) - is BackupDialog.UnfinishedBackupFound -> UnfinishedBackupFoundDialog.create(context) - is BackupDialog.ConfirmDiscardingBackup -> ConfirmDiscardingBackupDialog.create(context) + is BackupDialog.UnfinishedBackupFound -> UnfinishedBackupFoundDialog.create( + context = context, + scanResponse = state.dialog.scanResponse, + ) + is BackupDialog.ConfirmDiscardingBackup -> ConfirmDiscardingBackupDialog.create( + context = context, + unfinishedBackupScanResponse = state.dialog.scanResponse, + ) is BackupDialog.ResetBackupCard -> ResetBackupCardDialog.create( context = context, cardId = state.dialog.cardId, diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt index 0f29d53f73..a4be295931 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt @@ -3,6 +3,7 @@ package com.tangem.tap.features.onboarding.products.wallet.redux import android.net.Uri import com.tangem.common.CompletionResult import com.tangem.common.card.Card +import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.onboarding.data.model.CreateWalletResponse import com.tangem.feature.onboarding.presentation.wallet2.analytics.SeedPhraseSource @@ -98,7 +99,10 @@ sealed class BackupAction : Action { data object DiscardBackup : BackupAction() data object DiscardSavedBackup : BackupAction() - data object ResumeFoundUnfinishedBackup : BackupAction() + + data class ResumeFoundUnfinishedBackup( + val unfinishedBackupScanResponse: ScanResponse?, + ) : BackupAction() data class ResetBackupCard(val cardId: String) : BackupAction() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt index 1c1ea6340e..0cb19b30a1 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt @@ -581,21 +581,50 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction) store.state.globalState.onboardingState.onboardingManager?.finishActivation(it) } backupService.discardSavedBackup() + + val isOnboardingV2Enabled = store.inject(DaggerGraphState::onboardingV2FeatureToggles) + .isOnboardingV2Enabled + + if (isOnboardingV2Enabled) { + val onboardingRepository = store.inject(DaggerGraphState::onboardingRepository) + onboardingRepository.clearUnfinishedFinalizeOnboarding() + } } } is BackupAction.CheckForUnfinishedBackup -> { - if (backupService.hasIncompletedBackup) { - store.dispatch(GlobalAction.ShowDialog(BackupDialog.UnfinishedBackupFound)) + val isOnboardingV2Enabled = store.inject(DaggerGraphState::onboardingV2FeatureToggles).isOnboardingV2Enabled + + 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 -> { - store.dispatch( - GlobalAction.Onboarding.StartForUnfinishedBackup( - addedBackupCardsCount = backupService.addedBackupCardsCount, - ), - ) + if (action.unfinishedBackupScanResponse != null) { + // onboarding V2 + 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 -> { Analytics.send(Onboarding.Backup.Skipped()) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt index af1af41924..8d8f92f601 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt @@ -3,6 +3,7 @@ package com.tangem.tap.features.onboarding.products.wallet.redux import android.graphics.Bitmap import android.net.Uri import com.tangem.common.card.Card +import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.redux.StateDialog import com.tangem.domain.wallets.models.UserWalletId import org.rekotlin.StateType @@ -99,22 +100,29 @@ enum class AccessCodeError { typealias CardId = String sealed class BackupStep { - object InitBackup : BackupStep() - object ScanOriginCard : BackupStep() - object AddBackupCards : BackupStep() - object SetAccessCode : BackupStep() - object EnterAccessCode : BackupStep() - object ReenterAccessCode : BackupStep() - object WritePrimaryCard : 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() - object Finished : BackupStep() + data object Finished : BackupStep() } sealed class BackupDialog : StateDialog { - object AttestationFailed : BackupDialog() - object AddMoreBackupCards : BackupDialog() - object BackupInProgress : BackupDialog() - object UnfinishedBackupFound : BackupDialog() - object ConfirmDiscardingBackup : BackupDialog() + data object AttestationFailed : BackupDialog() + data object AddMoreBackupCards : BackupDialog() + data object BackupInProgress : BackupDialog() + + data class UnfinishedBackupFound( + val scanResponse: ScanResponse? = null, + ) : BackupDialog() + + data class ConfirmDiscardingBackup( + val scanResponse: ScanResponse? = null, + ) : BackupDialog() + data class ResetBackupCard(val cardId: String) : BackupDialog() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/ConfirmDiscardingBackupDialog.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/ConfirmDiscardingBackupDialog.kt index de833bc7f7..9f5257bd04 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/ConfirmDiscardingBackupDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/ConfirmDiscardingBackupDialog.kt @@ -3,18 +3,19 @@ package com.tangem.tap.features.onboarding.products.wallet.ui.dialogs import android.content.Context import androidx.appcompat.app.AlertDialog 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.features.onboarding.products.wallet.redux.BackupAction import com.tangem.tap.store import com.tangem.wallet.R object ConfirmDiscardingBackupDialog { - fun create(context: Context): AlertDialog { + fun create(context: Context, unfinishedBackupScanResponse: ScanResponse? = null): AlertDialog { return MaterialAlertDialogBuilder(context, R.style.CustomMaterialDialog).apply { setTitle(R.string.welcome_interrupted_backup_discard_title) setMessage(R.string.welcome_interrupted_backup_discard_message) 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) { _, _ -> store.dispatch(BackupAction.DiscardSavedBackup) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/UnfinishedBackupFoundDialog.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/UnfinishedBackupFoundDialog.kt index c7c4c30450..8ee50fc80d 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/UnfinishedBackupFoundDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/UnfinishedBackupFoundDialog.kt @@ -3,6 +3,7 @@ package com.tangem.tap.features.onboarding.products.wallet.ui.dialogs import android.content.Context import androidx.appcompat.app.AlertDialog 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.features.onboarding.products.wallet.redux.BackupAction import com.tangem.tap.features.onboarding.products.wallet.redux.BackupDialog @@ -10,17 +11,17 @@ import com.tangem.tap.store import com.tangem.wallet.R object UnfinishedBackupFoundDialog { - fun create(context: Context): AlertDialog { + fun create(context: Context, scanResponse: ScanResponse? = null): AlertDialog { return MaterialAlertDialogBuilder(context, R.style.CustomMaterialDialog).apply { setTitle(R.string.common_warning) setMessage(R.string.welcome_interrupted_backup_alert_message) setPositiveButton(R.string.welcome_interrupted_backup_alert_resume) { _, _ -> store.dispatch(GlobalAction.HideDialog) - store.dispatch(BackupAction.ResumeFoundUnfinishedBackup) + store.dispatch(BackupAction.ResumeFoundUnfinishedBackup(scanResponse)) } setNegativeButton(R.string.welcome_interrupted_backup_alert_discard) { _, _ -> store.dispatch(GlobalAction.HideDialog) - store.dispatch(GlobalAction.ShowDialog(BackupDialog.ConfirmDiscardingBackup)) + store.dispatch(GlobalAction.ShowDialog(BackupDialog.ConfirmDiscardingBackup(scanResponse))) } setCancelable(false) }.create() diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt index 6827cc546c..2fda147000 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt @@ -20,6 +20,7 @@ import com.tangem.domain.feedback.GetCardInfoUseCase import com.tangem.domain.feedback.SendFeedbackEmailUseCase import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase 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.usercountry.GetUserCountryUseCase import com.tangem.domain.walletmanager.WalletManagersFacade @@ -72,5 +73,6 @@ data class DaggerGraphState( val onrampFeatureToggles: OnrampFeatureToggles? = null, val environmentConfigStorage: EnvironmentConfigStorage? = null, val onboardingV2FeatureToggles: OnboardingV2FeatureToggles? = null, + val onboardingRepository: OnboardingRepository? = null, val excludedBlockchains: ExcludedBlockchains? = null, ) : StateType \ No newline at end of file diff --git a/core/datasource/build.gradle.kts b/core/datasource/build.gradle.kts index 78f5b0e498..3570cea973 100644 --- a/core/datasource/build.gradle.kts +++ b/core/datasource/build.gradle.kts @@ -28,6 +28,7 @@ dependencies { implementation(projects.domain.txhistory.models) implementation(projects.domain.staking.models) implementation(projects.domain.onramp.models) + implementation(projects.domain.models) /** Tangem libraries */ implementation(deps.tangem.blockchain) diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/MoshiModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/MoshiModule.kt index 1e4d5836d3..0a3b4fef73 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/MoshiModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/MoshiModule.kt @@ -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.addStakeKitEnumFallbackAdapters import com.tangem.datasource.local.config.providers.models.ProviderModel +import com.tangem.domain.models.scan.serialization.* import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -49,7 +50,16 @@ class MoshiModule { @Singleton fun provideSdkMoshiConverter(): MoshiJsonConverter { return MoshiJsonConverter( - adapters = listOf(BigDecimalAdapter()) + MoshiJsonConverter.getTangemSdkAdapters(), + adapters = MoshiJsonConverter.getTangemSdkAdapters() + + listOf( + BigDecimalAdapter(), + WalletDerivedKeysMapAdapter(), + ScanResponseDerivedKeysMapAdapter(), + ByteArrayKeyAdapter(), + ExtendedPublicKeysMapAdapter(), + CardBackupStatusAdapter(), + DerivationPathAdapterWithMigration(), + ), typedAdapters = MoshiJsonConverter.getTangemSdkTypedAdapters(), ) } diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt index 488c466107..f2ab9fb04c 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt @@ -107,6 +107,8 @@ object PreferencesKeys { 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_PAY_AVAILABLE_KEY by lazy { booleanPreferencesKey(name = "isGooglePayAvailable") } diff --git a/data/onboarding/build.gradle.kts b/data/onboarding/build.gradle.kts index 91e15d90c9..2f080f54bd 100644 --- a/data/onboarding/build.gradle.kts +++ b/data/onboarding/build.gradle.kts @@ -26,5 +26,6 @@ dependencies { // region Domain modules implementation(projects.domain.onboarding) + implementation(projects.domain.models) // endregion } \ No newline at end of file diff --git a/data/onboarding/src/main/kotlin/com/tangem/data/onboarding/DefaultOnboardingRepository.kt b/data/onboarding/src/main/kotlin/com/tangem/data/onboarding/DefaultOnboardingRepository.kt index 683ce6d376..f57267d002 100644 --- a/data/onboarding/src/main/kotlin/com/tangem/data/onboarding/DefaultOnboardingRepository.kt +++ b/data/onboarding/src/main/kotlin/com/tangem/data/onboarding/DefaultOnboardingRepository.kt @@ -2,9 +2,8 @@ package com.tangem.data.onboarding import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.PreferencesKeys -import com.tangem.datasource.local.preferences.utils.get -import com.tangem.datasource.local.preferences.utils.getSyncOrDefault -import com.tangem.datasource.local.preferences.utils.store +import com.tangem.datasource.local.preferences.utils.* +import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.onboarding.repository.OnboardingRepository import kotlinx.coroutines.flow.Flow @@ -30,4 +29,23 @@ internal class DefaultOnboardingRepository( override suspend fun saveTwinsOnboardingShown() { appPreferencesStore.store(key = PreferencesKeys.WAS_TWINS_ONBOARDING_SHOWN, value = true) } + + override suspend fun getUnfinishedFinalizeOnboarding(): ScanResponse? { + return appPreferencesStore + .getObjectSyncOrNull(PreferencesKeys.ONBOARDING_FINALIZE_SCAN_RESPONSE_KEY) + } + + override suspend fun saveUnfinishedFinalizeOnboarding(scanResponse: ScanResponse) { + appPreferencesStore + .storeObject( + PreferencesKeys.ONBOARDING_FINALIZE_SCAN_RESPONSE_KEY, + scanResponse, + ) + } + + override suspend fun clearUnfinishedFinalizeOnboarding() { + appPreferencesStore.editData { mutablePreferences -> + mutablePreferences.remove(PreferencesKeys.ONBOARDING_FINALIZE_SCAN_RESPONSE_KEY) + } + } } \ No newline at end of file diff --git a/domain/onboarding/build.gradle.kts b/domain/onboarding/build.gradle.kts index 831b4eb4e4..404c6a7ab2 100644 --- a/domain/onboarding/build.gradle.kts +++ b/domain/onboarding/build.gradle.kts @@ -6,4 +6,6 @@ plugins { dependencies { implementation(deps.arrow.core) implementation(deps.kotlin.coroutines) + + implementation(projects.domain.models) } \ No newline at end of file diff --git a/domain/onboarding/src/main/kotlin/com/tangem/domain/onboarding/repository/OnboardingRepository.kt b/domain/onboarding/src/main/kotlin/com/tangem/domain/onboarding/repository/OnboardingRepository.kt index cda4f3b19a..795247335f 100644 --- a/domain/onboarding/src/main/kotlin/com/tangem/domain/onboarding/repository/OnboardingRepository.kt +++ b/domain/onboarding/src/main/kotlin/com/tangem/domain/onboarding/repository/OnboardingRepository.kt @@ -1,5 +1,6 @@ package com.tangem.domain.onboarding.repository +import com.tangem.domain.models.scan.ScanResponse import kotlinx.coroutines.flow.Flow /** @@ -15,4 +16,10 @@ interface OnboardingRepository { suspend fun wasTwinsOnboardingShownSync(): Boolean suspend fun saveTwinsOnboardingShown() + + suspend fun getUnfinishedFinalizeOnboarding(): ScanResponse? + + suspend fun saveUnfinishedFinalizeOnboarding(scanResponse: ScanResponse) + + suspend fun clearUnfinishedFinalizeOnboarding() } \ No newline at end of file diff --git a/features/onboarding-v2/impl/build.gradle.kts b/features/onboarding-v2/impl/build.gradle.kts index c603952480..6a64d3f440 100644 --- a/features/onboarding-v2/impl/build.gradle.kts +++ b/features/onboarding-v2/impl/build.gradle.kts @@ -25,6 +25,7 @@ dependencies { implementation(projects.core.res) implementation(projects.core.decompose) implementation(projects.core.navigation) + implementation(projects.core.datasource) /** Common */ implementation(projects.common.ui) @@ -39,6 +40,7 @@ dependencies { implementation(projects.domain.wallets.models) implementation(projects.domain.legacy) implementation(projects.domain.settings) + implementation(projects.domain.onboarding) /** Tangem libraries */ implementation(projects.libs.tangemSdkApi) @@ -62,6 +64,7 @@ dependencies { implementation(deps.lottie.compose) implementation(deps.decompose.ext.compose) implementation(deps.androidx.activity.compose) + implementation(deps.androidx.datastore) /** Other libraries */ implementation(deps.kotlin.immutable.collections) diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/OnboardingEntryModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/OnboardingEntryModel.kt index 54dd730ffd..5bf0212a80 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/OnboardingEntryModel.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/OnboardingEntryModel.kt @@ -53,8 +53,6 @@ internal class OnboardingEntryModel @Inject constructor( } return when (scanResponse.productType) { - ProductType.Note -> TODO() - ProductType.Twins -> TODO() ProductType.Wallet -> OnboardingRoute.MultiWallet( scanResponse = scanResponse, withSeedPhraseFlow = false, @@ -71,8 +69,7 @@ internal class OnboardingEntryModel @Inject constructor( onDone = ::onMultiWalletOnboardingDone, mode = multiWalletNavigationMode, ) - ProductType.Start2Coin -> TODO() - ProductType.Visa -> TODO() + else -> error("Unsupported") } } diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/accesscode/model/MultiWalletAccessCodeModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/accesscode/model/MultiWalletAccessCodeModel.kt index 755d6c3b3a..2f2a18e497 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/accesscode/model/MultiWalletAccessCodeModel.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/accesscode/model/MultiWalletAccessCodeModel.kt @@ -123,7 +123,6 @@ internal class MultiWalletAccessCodeModel @Inject constructor( if (checkAccessCode()) { backupServiceHolder.backupService.get()?.setAccessCode(_uiState.value.accessCodeFirst.text) - // TODO delete access code from state // make on done params.multiWalletState.update { it.copy(accessCode = OnboardingMultiWalletState.AccessCode(uiState.value.accessCodeFirst.text)) diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/backup/model/BackupState.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/backup/model/BackupState.kt index a87d14ae12..87ad111121 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/backup/model/BackupState.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/backup/model/BackupState.kt @@ -1,6 +1,5 @@ package com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.model -// TODO maybe delete some fields in the future data class BackupState( - val backupCardsNumber: Int = 0, + val numberOfBackupCards: Int = 0, ) \ No newline at end of file diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/backup/model/MultiWalletBackupModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/backup/model/MultiWalletBackupModel.kt index 89a5685f63..ecf5661097 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/backup/model/MultiWalletBackupModel.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/backup/model/MultiWalletBackupModel.kt @@ -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.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.onlyOneBackupDeviceDialog 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 @@ -70,30 +70,19 @@ class MultiWalletBackupModel @Inject constructor( } private fun getInitState(): MultiWalletBackupUM { - return when (backupService.currentState) { - BackupService.State.Preparing -> { - MultiWalletBackupUM( - title = resourceReference(R.string.onboarding_title_no_backup_cards), - bodyText = resourceReference(R.string.onboarding_subtitle_no_backup_cards), - finalizeButtonEnabled = false, - addBackupButtonEnabled = true, - addBackupButtonLoading = false, - onAddBackupClick = ::startBackupWallet, - onFinalizeButtonClick = ::onFinalizeClick, - onSkipButtonClick = { - // eventFlow.tryEmit(Unit) - }, - ) - } - // TODO - is BackupService.State.FinalizingBackupCard -> TODO() - BackupService.State.FinalizingPrimaryCard -> TODO() - BackupService.State.Finished -> TODO() - } + return MultiWalletBackupUM( + title = resourceReference(R.string.onboarding_title_no_backup_cards), + bodyText = resourceReference(R.string.onboarding_subtitle_no_backup_cards), + finalizeButtonEnabled = false, + addBackupButtonEnabled = true, + addBackupButtonLoading = false, + onAddBackupClick = ::startBackupWallet, + onFinalizeButtonClick = ::onFinalizeClick, + ) } private fun startBackupWallet() { - if (state.value.backupCardsNumber == 0) { + if (state.value.numberOfBackupCards == 0) { backupService.discardSavedBackup() } val primaryCard = scanResponse.primaryCard @@ -144,19 +133,35 @@ class MultiWalletBackupModel @Inject constructor( } } - private fun onFinalizeClick() { - when (state.value.backupCardsNumber) { + private fun onFinalizeClick(fromDialog: Boolean = false) { + when (state.value.numberOfBackupCards) { 1 -> { - // TODO show dialog params.multiWalletState.update { it.copy(isThreeCards = false) } + + if (fromDialog.not()) { + showOnlyOneBackupWarningDialog() + return + } } 2 -> { params.multiWalletState.update { it.copy(isThreeCards = true) } } } + 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() { @@ -176,7 +181,7 @@ class MultiWalletBackupModel @Inject constructor( is CompletionResult.Success -> { state.update { it.copy( - backupCardsNumber = it.backupCardsNumber + 1, + numberOfBackupCards = it.numberOfBackupCards + 1, ) } @@ -186,12 +191,12 @@ class MultiWalletBackupModel @Inject constructor( ) params.backups.update { it.copy( - card2 = if (state.value.backupCardsNumber == 1) backupCardInfo else it.card2, - card3 = if (state.value.backupCardsNumber == 2) backupCardInfo else it.card3, + card2 = if (state.value.numberOfBackupCards == 1) backupCardInfo else it.card2, + card3 = if (state.value.numberOfBackupCards == 2) backupCardInfo else it.card3, ) } - setNumberOfBackupCards(state.value.backupCardsNumber) + setNumberOfBackupCards(state.value.numberOfBackupCards) } is CompletionResult.Failure -> { when (val error = result.error) { diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/backup/ui/Dialogs.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/backup/ui/Dialogs.kt index 15d4a0b457..1f88ecbb2c 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/backup/ui/Dialogs.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/backup/ui/Dialogs.kt @@ -28,4 +28,18 @@ internal fun backupCardAttestationFailedDialog(onDismiss: () -> Unit) = MultiWal onDismiss = onDismiss, onConfirm = onDismiss, 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, ) \ No newline at end of file diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/backup/ui/state/MultiWalletBackupUM.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/backup/ui/state/MultiWalletBackupUM.kt index 96cc144862..86a75510c9 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/backup/ui/state/MultiWalletBackupUM.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/backup/ui/state/MultiWalletBackupUM.kt @@ -13,7 +13,6 @@ data class MultiWalletBackupUM( val finalizeButtonEnabled: Boolean = false, val onAddBackupClick: () -> Unit = {}, val onFinalizeButtonClick: () -> Unit = {}, - val onSkipButtonClick: () -> Unit = {}, val dialog: MultiWalletBackupUMDialog? = null, ) diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/chooseoption/model/Wallet1ChooseOptionModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/chooseoption/model/Wallet1ChooseOptionModel.kt index 4fe5566d5e..0577ed1513 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/chooseoption/model/Wallet1ChooseOptionModel.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/chooseoption/model/Wallet1ChooseOptionModel.kt @@ -37,8 +37,6 @@ internal class Wallet1ChooseOptionModel @Inject constructor( } fun onSkipClick() { - // TODO show confirmation dialog - if (skipClicked) return skipClicked = true diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/finalize/model/MultiWalletFinalizeModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/finalize/model/MultiWalletFinalizeModel.kt index 1214bf588d..805acccfca 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/finalize/model/MultiWalletFinalizeModel.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/finalize/model/MultiWalletFinalizeModel.kt @@ -12,6 +12,7 @@ import com.tangem.domain.feedback.models.FeedbackEmailType import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ScanResponse 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.legacy.UserWalletsListManager 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.ui.state.MultiWalletFinalizeUM 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.sdk.api.BackupServiceHolder import com.tangem.sdk.api.TangemSdkManager @@ -46,23 +48,66 @@ internal class MultiWalletFinalizeModel @Inject constructor( private val generateWalletNameUseCase: GenerateWalletNameUseCase, private val userWalletsListManager: UserWalletsListManager, private val cardRepository: CardRepository, + private val onboardingRepository: OnboardingRepository, ) : Model() { private val params = paramsContainer.require() private val multiWalletState get() = params.multiWalletState - private val _uiState = MutableStateFlow( - 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 _uiState = MutableStateFlow(getInitialState()) + private val backupCardIds = backupServiceHolder.backupService.get()?.backupCardIds.orEmpty() + + private var walletHasBackupError = false val uiState = _uiState.asStateFlow() + val onEvent = MutableSharedFlow() + 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() { when (_uiState.value.step) { MultiWalletFinalizeUM.Step.Primary -> { @@ -79,8 +124,6 @@ internal class MultiWalletFinalizeModel @Inject constructor( private fun writePrimaryCard() { val backupService = backupServiceHolder.backupService.get() ?: return - // TODO store.dispatchOnMain(BackupAction.SetHasRing(hasRing = isRing)) - val primaryCardBatchId = backupService.primaryCardBatchId ?: return val isRing = isRing(primaryCardBatchId) 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) { val backupService = backupServiceHolder.backupService.get() ?: return - - // TODO store.dispatchOnMain(BackupAction.SetHasRing(hasRing = isRing)) - val backupCardBatchId = backupService.backupCardsBatchIds.getOrNull(cardIndex) ?: return val isRing = isRing(backupCardBatchId) val iconScanRes = if (isRing) R.drawable.img_hand_scan_ring else null @@ -151,8 +191,6 @@ internal class MultiWalletFinalizeModel @Inject constructor( } private fun finishBackup() { - // TODO Analytics.send(Onboarding.Backup.Finished(backupState.backupCardsNumber)) - modelScope.launch { val scanResponse = params.multiWalletState.value.currentScanResponse val userWallet = createUserWallet(scanResponse) @@ -183,8 +221,12 @@ internal class MultiWalletFinalizeModel @Inject constructor( 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) } } @@ -201,7 +243,7 @@ internal class MultiWalletFinalizeModel @Inject constructor( @Suppress("MagicNumber") 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( backupStatus = CardDTO.BackupStatus.Active(cardCount = cardsCount), diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/finalize/ui/MultiWalletFinalize.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/finalize/ui/MultiWalletFinalize.kt index 6d90886eae..9bbfe88b5e 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/finalize/ui/MultiWalletFinalize.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/finalize/ui/MultiWalletFinalize.kt @@ -1,7 +1,7 @@ package com.tangem.features.onboarding.v2.multiwallet.impl.child.finalize.ui import androidx.compose.foundation.layout.* -import androidx.compose.material.Text +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/seedphrase/model/MultiWalletSeedPhraseModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/seedphrase/model/MultiWalletSeedPhraseModel.kt index d636e7e2ca..ad00ff8019 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/seedphrase/model/MultiWalletSeedPhraseModel.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/seedphrase/model/MultiWalletSeedPhraseModel.kt @@ -135,8 +135,6 @@ internal class MultiWalletSeedPhraseModel @Inject constructor( } private fun openGeneratedSeedPhrase() { - // TODO analytics SeedPhraseEvents.ButtonGenerateSeedPhrase - val words12 = state.value.generatedWords12 ?: return val words24 = state.value.generatedWords24 ?: return diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/model/OnboardingMultiWalletModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/model/OnboardingMultiWalletModel.kt index 2ebc2f3867..0b74c9f64f 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/model/OnboardingMultiWalletModel.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/model/OnboardingMultiWalletModel.kt @@ -5,13 +5,18 @@ 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.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.wallets.usecase.GetCardImageUseCase 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.child.MultiWalletChildParams 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.operations.backup.BackupService +import com.tangem.sdk.api.BackupServiceHolder import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch @@ -23,6 +28,8 @@ internal class OnboardingMultiWalletModel @Inject constructor( override val dispatchers: CoroutineDispatcherProvider, private val router: Router, private val analyticsHandler: AnalyticsEventHandler, + private val backupServiceHolder: BackupServiceHolder, + private val appPreferencesStore: AppPreferencesStore, ) : Model() { private val params = paramsContainer.require() private val getCardImageUseCase = GetCardImageUseCase() @@ -35,6 +42,7 @@ internal class OnboardingMultiWalletModel @Inject constructor( accessCode = null, isThreeCards = true, resultUserWallet = null, + startFromFinalize = getInitialStartFromFinalize(), ), ) val backups = MutableStateFlow(MultiWalletChildParams.Backup()) @@ -48,10 +56,11 @@ internal class OnboardingMultiWalletModel @Inject constructor( } fun onBack() { - _uiState.update { - it.copy( + _uiState.update { st -> + st.copy( dialog = interruptBackupDialog( onConfirm = { + removeFinalizeScanResponseState() router.pop() }, 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() { modelScope.launch { - backups.collectLatest { bst -> + backups.collectLatest { backup -> when { - _uiState.value.artwork2Url == null && bst.card2 != null -> { + _uiState.value.artwork2Url == null && backup.card2 != null -> { val artwork = - getCardImageUseCase.invoke(bst.card2.cardId, bst.card2.cardPublicKey) + getCardImageUseCase.invoke(backup.card2.cardId, backup.card2.cardPublicKey) _uiState.update { it.copy(artwork2Url = artwork) } } - _uiState.value.artwork3Url == null && bst.card3 != null -> { + _uiState.value.artwork3Url == null && backup.card3 != null -> { val artwork = - getCardImageUseCase.invoke(bst.card3.cardId, bst.card3.cardPublicKey) + getCardImageUseCase.invoke(backup.card3.cardId, backup.card3.cardPublicKey) _uiState.update { it.copy(artwork3Url = artwork) } @@ -85,9 +104,10 @@ internal class OnboardingMultiWalletModel @Inject constructor( private fun getInitialStep(): OnboardingMultiWalletState.Step { val card = params.scanResponse.card + val backupService = backupServiceHolder.backupService.get()!! - // todo check local storage for scan response return when { + backupService.hasIncompletedBackup -> OnboardingMultiWalletState.Step.Finalize card.wallets.isNotEmpty() && card.backupStatus == CardDTO.BackupStatus.NoBackup -> OnboardingMultiWalletState.Step.AddBackupDevice 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() { val title = screenTitleByStep(getInitialStep()) params.titleProvider.changeTitle(title) diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/model/OnboardingMultiWalletState.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/model/OnboardingMultiWalletState.kt index 22298a74b2..294dd6049d 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/model/OnboardingMultiWalletState.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/model/OnboardingMultiWalletState.kt @@ -8,8 +8,13 @@ data class OnboardingMultiWalletState( val accessCode: AccessCode?, val isThreeCards: Boolean, val currentScanResponse: ScanResponse, - val resultUserWallet: UserWallet?, // TODO make callback instead + val startFromFinalize: FinalizeStage?, + val resultUserWallet: UserWallet?, ) { + enum class FinalizeStage { + ScanPrimaryCard, ScanBackupFirstCard, ScanBackupSecondCard + } + /** * Wallet1 * CreateWallet -> ChooseBackupOption -> AddBackupDevice -> Finalize -> [Done]