From 3c3f35aeb60e30fcee8370ee3bd3f2b504974b99 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 11 Sep 2025 15:53:28 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../domain/scanCard/LegacyScanProcessor.kt | 2 +- .../tangem/tap/routing/utils/ChildFactory.kt | 17 +- .../com/tangem/common/routing/AppRoute.kt | 16 +- .../wallets/hot/DefaultHotWalletAccessor.kt | 13 +- .../domain/wallets/hot/HotWalletAccessor.kt | 3 + .../ClearHotWalletContextualUnlockUseCase.kt | 7 + features/hot-wallet/impl/build.gradle.kts | 2 + .../hotwallet/accesscode/AccessCodeModel.kt | 5 +- .../upgradewallet/UpgradeWalletModel.kt | 111 ++++++++++- .../upgradewallet/entity/UpgradeWalletUM.kt | 1 + .../viewphrase/model/ViewPhraseModel.kt | 6 +- .../v2/entry/OnboardingEntryComponent.kt | 14 +- features/onboarding-v2/impl/build.gradle.kts | 1 + .../entry/impl/model/OnboardingEntryModel.kt | 12 +- .../api/OnboardingMultiWalletComponent.kt | 10 +- .../DefaultOnboardingMultiWalletComponent.kt | 7 + .../model/MultiWalletFinalizeModel.kt | 10 + .../MultiWalletUpgradeWalletComponent.kt | 56 ++++++ .../model/MultiWalletUpgradeWalletModel.kt | 181 ++++++++++++++++++ .../ui/MultiWalletUpgradeWallet.kt | 98 ++++++++++ .../ui/state/MultiWalletUpgradeWalletUM.kt | 11 ++ .../v2/multiwallet/impl/di/ComponentModule.kt | 6 + .../impl/model/OnboardingMultiWalletModel.kt | 3 + .../impl/model/OnboardingMultiWalletState.kt | 9 +- .../v2/multiwallet/impl/model/Utils.kt | 1 + .../model/WalletSettingsModel.kt | 50 +++-- 26 files changed, 589 insertions(+), 63 deletions(-) create mode 100644 features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/upgradewallet/MultiWalletUpgradeWalletComponent.kt create mode 100644 features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/upgradewallet/model/MultiWalletUpgradeWalletModel.kt create mode 100644 features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/upgradewallet/ui/MultiWalletUpgradeWallet.kt create mode 100644 features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/upgradewallet/ui/state/MultiWalletUpgradeWalletUM.kt diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt index ea780d4fa5..5b44a91292 100644 --- a/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt +++ b/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt @@ -193,7 +193,7 @@ internal class LegacyScanProcessor @Inject constructor( } } - @Suppress("LongMethod", "MagicNumber") + @Suppress("LongMethod", "LongParameterList", "MagicNumber") private suspend inline fun onScanSuccess( scanResponse: ScanResponse, crossinline onProgressStateChange: suspend (showProgress: Boolean) -> Unit, diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt index 29a693971b..6035f506dc 100644 --- a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt +++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt @@ -237,14 +237,19 @@ internal class ChildFactory @Inject constructor( context = context, params = OnboardingEntryComponent.Params( scanResponse = route.scanResponse, - mode = when (route.mode) { - AppRoute.Onboarding.Mode.Onboarding -> OnboardingEntryComponent.Mode.Onboarding - AppRoute.Onboarding.Mode.AddBackupWallet1 -> OnboardingEntryComponent.Mode.AddBackupWallet1 - AppRoute.Onboarding.Mode.WelcomeOnlyTwin -> OnboardingEntryComponent.Mode.WelcomeOnlyTwin - AppRoute.Onboarding.Mode.RecreateWalletTwin -> + mode = when (val mode = route.mode) { + is AppRoute.Onboarding.Mode.Onboarding -> + OnboardingEntryComponent.Mode.Onboarding + is AppRoute.Onboarding.Mode.AddBackupWallet1 -> + OnboardingEntryComponent.Mode.AddBackupWallet1 + is AppRoute.Onboarding.Mode.WelcomeOnlyTwin -> + OnboardingEntryComponent.Mode.WelcomeOnlyTwin + is AppRoute.Onboarding.Mode.RecreateWalletTwin -> OnboardingEntryComponent.Mode.RecreateWalletTwin - AppRoute.Onboarding.Mode.ContinueFinalize -> + is AppRoute.Onboarding.Mode.ContinueFinalize -> OnboardingEntryComponent.Mode.ContinueFinalize + is AppRoute.Onboarding.Mode.UpgradeHotWallet -> + OnboardingEntryComponent.Mode.UpgradeHotWallet(mode.userWalletId) }, ), componentFactory = onboardingEntryComponentFactory, diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt index 96fcdbc5fd..3ca2c4c565 100644 --- a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt @@ -274,14 +274,16 @@ sealed class AppRoute(val path: String) : Route { data class Onboarding( val scanResponse: ScanResponse, val mode: Mode = Mode.Onboarding, - ) : AppRoute(path = "/onboarding_v2/${mode.name}") { + ) : AppRoute(path = "/onboarding_v2/$mode") { - enum class Mode { - Onboarding, // general Mode - AddBackupWallet1, // continue backup process for existing wallet 1 - WelcomeOnlyTwin, // show welcome screen and then navigate to wallet for twins - RecreateWalletTwin, // reset twins - ContinueFinalize, // continue finalize process (unfinished backup dialog) + @Serializable + sealed class Mode { + data object Onboarding : Mode() // general Mode + data object AddBackupWallet1 : Mode() // continue backup process for existing wallet 1 + data object WelcomeOnlyTwin : Mode() // show welcome screen and then navigate to wallet for twins + data object RecreateWalletTwin : Mode() // reset twins + data object ContinueFinalize : Mode() // continue finalize process (unfinished backup dialog) + data class UpgradeHotWallet(val userWalletId: UserWalletId) : Mode() // upgrade hot wallet } } diff --git a/data/wallets/src/main/java/com/tangem/data/wallets/hot/DefaultHotWalletAccessor.kt b/data/wallets/src/main/java/com/tangem/data/wallets/hot/DefaultHotWalletAccessor.kt index 33dc7635a7..fdf4a50d73 100644 --- a/data/wallets/src/main/java/com/tangem/data/wallets/hot/DefaultHotWalletAccessor.kt +++ b/data/wallets/src/main/java/com/tangem/data/wallets/hot/DefaultHotWalletAccessor.kt @@ -3,6 +3,7 @@ package com.tangem.data.wallets.hot import com.tangem.common.core.TangemSdkError import com.tangem.domain.core.wallets.UserWalletsListRepository import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.wallets.hot.HotWalletAccessor import com.tangem.domain.wallets.hot.HotWalletPasswordRequester import com.tangem.domain.wallets.repository.WalletsRepository @@ -56,12 +57,22 @@ class DefaultHotWalletAccessor @Inject constructor( contextualUnlockHotWallet[hotWalletId] override fun clearContextualUnlock(hotWalletId: HotWalletId) { - contextualUnlockHotWallet[hotWalletId] = null + contextualUnlockHotWallet.remove(hotWalletId) scope.launch { tangemHotSdk.clearUnlockContext(hotWalletId) } } + override fun clearContextualUnlock(userWalletId: UserWalletId) { + scope.launch { + val userWallet = userWalletsListRepository.userWalletsSync() + .find { it is UserWallet.Hot && it.walletId == userWalletId } + as? UserWallet.Hot + ?: return@launch + clearContextualUnlock(userWallet.hotWalletId) + } + } + override fun clearAllContextualUnlock() { val hotWalletsIds = contextualUnlockHotWallet.keys.toList() contextualUnlockHotWallet.clear() diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/hot/HotWalletAccessor.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/hot/HotWalletAccessor.kt index 41fc7f5fcb..ca71e8b7a6 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/hot/HotWalletAccessor.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/hot/HotWalletAccessor.kt @@ -1,5 +1,6 @@ package com.tangem.domain.wallets.hot +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.hot.sdk.model.* interface HotWalletAccessor { @@ -16,5 +17,7 @@ interface HotWalletAccessor { fun clearContextualUnlock(hotWalletId: HotWalletId) + fun clearContextualUnlock(userWalletId: UserWalletId) + fun clearAllContextualUnlock() } \ No newline at end of file diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/ClearHotWalletContextualUnlockUseCase.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/ClearHotWalletContextualUnlockUseCase.kt index be1a030837..caf1ea5457 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/ClearHotWalletContextualUnlockUseCase.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/ClearHotWalletContextualUnlockUseCase.kt @@ -2,6 +2,7 @@ package com.tangem.domain.wallets.usecase import arrow.core.Either +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.wallets.hot.HotWalletAccessor import com.tangem.hot.sdk.model.HotWalletId @@ -14,4 +15,10 @@ class ClearHotWalletContextualUnlockUseCase( hotWalletAccessor.clearContextualUnlock(hotWalletId) } } + + operator fun invoke(userWalletId: UserWalletId): Either { + return Either.catch { + hotWalletAccessor.clearContextualUnlock(userWalletId) + } + } } \ No newline at end of file diff --git a/features/hot-wallet/impl/build.gradle.kts b/features/hot-wallet/impl/build.gradle.kts index 2f25fc3478..481078432a 100644 --- a/features/hot-wallet/impl/build.gradle.kts +++ b/features/hot-wallet/impl/build.gradle.kts @@ -34,6 +34,8 @@ dependencies { implementation(projects.domain.wallets.models) implementation(projects.domain.models) implementation(projects.domain.settings) + implementation(projects.domain.feedback) + implementation(projects.domain.feedback.models) /** Common */ implementation(projects.common.ui) diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeModel.kt index e611297547..404b5091bb 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeModel.kt @@ -23,7 +23,6 @@ import com.tangem.features.hotwallet.accesscode.entity.AccessCodeUM import com.tangem.features.hotwallet.impl.R import com.tangem.hot.sdk.TangemHotSdk import com.tangem.hot.sdk.model.HotAuth -import com.tangem.hot.sdk.model.HotWalletId import com.tangem.hot.sdk.model.UnlockHotWallet import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.MutableStateFlow @@ -54,8 +53,6 @@ internal class AccessCodeModel @Inject constructor( private val params = paramsContainer.require() - private var hotWalletId: HotWalletId? = null - internal val uiState: StateFlow field = MutableStateFlow(getInitialState()) @@ -197,7 +194,7 @@ internal class AccessCodeModel @Inject constructor( } override fun onDestroy() { - hotWalletId?.let { clearHotWalletContextualUnlockUseCase.invoke(it) } + clearHotWalletContextualUnlockUseCase.invoke(params.userWalletId) super.onDestroy() } } \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/upgradewallet/UpgradeWalletModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/upgradewallet/UpgradeWalletModel.kt index bc03e54e43..00ca37071d 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/upgradewallet/UpgradeWalletModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/upgradewallet/UpgradeWalletModel.kt @@ -1,33 +1,71 @@ package com.tangem.features.hotwallet.upgradewallet +import com.tangem.common.core.TangemError +import com.tangem.common.core.TangemSdkError +import com.tangem.common.doOnFailure +import com.tangem.common.doOnSuccess +import com.tangem.common.routing.AppRoute import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model +import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.navigation.Router +import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.navigation.url.UrlOpener +import com.tangem.core.ui.R +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.toWrappedList +import com.tangem.core.ui.message.DialogMessage +import com.tangem.core.ui.message.EventMessageAction +import com.tangem.domain.card.repository.CardSdkConfigRepository +import com.tangem.domain.feedback.SendFeedbackEmailUseCase +import com.tangem.domain.feedback.models.FeedbackEmailType +import com.tangem.domain.models.scan.ScanResponse +import com.tangem.domain.settings.repositories.SettingsRepository +import com.tangem.domain.wallets.usecase.ClearHotWalletContextualUnlockUseCase import com.tangem.domain.wallets.usecase.GenerateBuyTangemCardLinkUseCase +import com.tangem.features.hotwallet.UpgradeWalletComponent import com.tangem.features.hotwallet.upgradewallet.entity.UpgradeWalletUM +import com.tangem.sdk.api.TangemSdkManager +import com.tangem.sdk.extensions.localizedDescriptionRes import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import com.tangem.utils.extensions.DELAY_SDK_DIALOG_CLOSE +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import javax.inject.Inject +@Suppress("LongParameterList") @ModelScoped internal class UpgradeWalletModel @Inject constructor( + paramsContainer: ParamsContainer, override val dispatchers: CoroutineDispatcherProvider, private val router: Router, private val generateBuyTangemCardLinkUseCase: GenerateBuyTangemCardLinkUseCase, private val urlOpener: UrlOpener, + private val settingsRepository: SettingsRepository, + private val cardSdkConfigRepository: CardSdkConfigRepository, + private val uiMessageSender: UiMessageSender, + private val clearHotWalletContextualUnlockUseCase: ClearHotWalletContextualUnlockUseCase, + private val tangemSdkManager: TangemSdkManager, + private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase, ) : Model() { + private val params = paramsContainer.require() - internal val uiState: StateFlow - field = MutableStateFlow( + private val _uiState = MutableStateFlow( UpgradeWalletUM( onBackClick = { router.pop() }, onBuyTangemWalletClick = ::onBuyTangemWalletClick, onScanDeviceClick = ::onScanDeviceClick, ), ) + internal val uiState: StateFlow = _uiState + + override fun onDestroy() { + clearHotWalletContextualUnlockUseCase.invoke(params.userWalletId) + super.onDestroy() + } private fun onBuyTangemWalletClick() { modelScope.launch { @@ -36,6 +74,73 @@ internal class UpgradeWalletModel @Inject constructor( } private fun onScanDeviceClick() { - // TODO [REDACTED_TASK_KEY] + scanCard() + } + + private fun scanCard() { + modelScope.launch { + setLoading(true) + + val shouldSaveAccessCodes = settingsRepository.shouldSaveAccessCodes() + cardSdkConfigRepository.setAccessCodeRequestPolicy( + isBiometricsRequestPolicy = shouldSaveAccessCodes, + ) + + tangemSdkManager + .scanProduct() + .doOnSuccess { + delay(DELAY_SDK_DIALOG_CLOSE) + tangemSdkManager.changeDisplayedCardIdNumbersCount(it) + navigateToUpgradeFlow(it) + } + .doOnFailure { + showCardVerificationFailedDialog(it) + } + } + } + + private fun setLoading(isLoading: Boolean) { + _uiState.update { it.copy(isLoading = isLoading) } + } + + private fun showCardVerificationFailedDialog(error: TangemError) { + if (error !is TangemSdkError.CardVerificationFailed) return + + // TODO [REDACTED_TASK_KEY] track error + + val resource = error.localizedDescriptionRes() + val resId = resource.resId ?: R.string.common_unknown_error + val resArgs = resource.args.map { it.value } + + uiMessageSender.send( + DialogMessage( + message = resourceReference(id = resId, resArgs.toWrappedList()), + title = resourceReference(id = R.string.security_alert_title), + isDismissable = false, + firstActionBuilder = { + EventMessageAction( + title = resourceReference(id = R.string.alert_button_request_support), + onClick = { + modelScope.launch { + sendFeedbackEmailUseCase(type = FeedbackEmailType.CardAttestationFailed) + } + }, + ) + }, + secondActionBuilder = { cancelAction(onClick = {}) }, + ), + ) + } + + private fun navigateToUpgradeFlow(scanResponse: ScanResponse) { + setLoading(false) + router.push( + AppRoute.Onboarding( + scanResponse = scanResponse, + mode = AppRoute.Onboarding.Mode.UpgradeHotWallet( + userWalletId = params.userWalletId, + ), + ), + ) } } \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/upgradewallet/entity/UpgradeWalletUM.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/upgradewallet/entity/UpgradeWalletUM.kt index 8f700a27b9..ff2ae172d9 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/upgradewallet/entity/UpgradeWalletUM.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/upgradewallet/entity/UpgradeWalletUM.kt @@ -4,4 +4,5 @@ internal data class UpgradeWalletUM( val onBackClick: () -> Unit, val onBuyTangemWalletClick: () -> Unit, val onScanDeviceClick: () -> Unit, + val isLoading: Boolean = false, ) \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/viewphrase/model/ViewPhraseModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/viewphrase/model/ViewPhraseModel.kt index f2d3adf53e..ede4241680 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/viewphrase/model/ViewPhraseModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/viewphrase/model/ViewPhraseModel.kt @@ -13,7 +13,6 @@ import com.tangem.domain.wallets.usecase.ExportSeedPhraseUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.features.hotwallet.ViewPhraseComponent import com.tangem.features.hotwallet.viewphrase.entity.ViewPhraseUM -import com.tangem.hot.sdk.model.HotWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.MutableStateFlow @@ -35,8 +34,6 @@ internal class ViewPhraseModel @Inject constructor( private val params = paramsContainer.require() - private var hotWalletId: HotWalletId? = null - internal val uiState: StateFlow field = MutableStateFlow( ViewPhraseUM( @@ -52,7 +49,6 @@ internal class ViewPhraseModel @Inject constructor( val userWallet = getUserWalletUseCase(params.userWalletId) .getOrElse { error("User wallet with id ${params.userWalletId} not found") } if (userWallet is UserWallet.Hot) { - hotWalletId = userWallet.hotWalletId modelScope.launch { val words = exportSeedPhraseUseCase.invoke(userWallet.hotWalletId) .getOrElse { error("Unable to export seed phrase for wallet with id ${params.userWalletId}") } @@ -70,7 +66,7 @@ internal class ViewPhraseModel @Inject constructor( } override fun onDestroy() { - hotWalletId?.let { clearHotWalletContextualUnlockUseCase.invoke(it) } + clearHotWalletContextualUnlockUseCase.invoke(params.userWalletId) super.onDestroy() } } \ No newline at end of file diff --git a/features/onboarding-v2/api/src/main/kotlin/com/tangem/features/onboarding/v2/entry/OnboardingEntryComponent.kt b/features/onboarding-v2/api/src/main/kotlin/com/tangem/features/onboarding/v2/entry/OnboardingEntryComponent.kt index b52fabf421..a21785815f 100644 --- a/features/onboarding-v2/api/src/main/kotlin/com/tangem/features/onboarding/v2/entry/OnboardingEntryComponent.kt +++ b/features/onboarding-v2/api/src/main/kotlin/com/tangem/features/onboarding/v2/entry/OnboardingEntryComponent.kt @@ -3,6 +3,7 @@ package com.tangem.features.onboarding.v2.entry import com.tangem.core.decompose.factory.ComponentFactory import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.domain.models.scan.ScanResponse +import com.tangem.domain.models.wallet.UserWalletId interface OnboardingEntryComponent : ComposableContentComponent { @@ -11,12 +12,13 @@ interface OnboardingEntryComponent : ComposableContentComponent { val mode: Mode, ) - enum class Mode { - Onboarding, - AddBackupWallet1, - WelcomeOnlyTwin, - RecreateWalletTwin, - ContinueFinalize, + sealed class Mode { + data object Onboarding : Mode() + data object AddBackupWallet1 : Mode() + data object WelcomeOnlyTwin : Mode() + data object RecreateWalletTwin : Mode() + data object ContinueFinalize : Mode() + data class UpgradeHotWallet(val userWalletId: UserWalletId) : Mode() } interface Factory : ComponentFactory diff --git a/features/onboarding-v2/impl/build.gradle.kts b/features/onboarding-v2/impl/build.gradle.kts index d6abeb7356..7febfb81ba 100644 --- a/features/onboarding-v2/impl/build.gradle.kts +++ b/features/onboarding-v2/impl/build.gradle.kts @@ -53,6 +53,7 @@ dependencies { implementation(projects.domain.transaction) /** Tangem libraries */ + implementation(tangemDeps.hot.core) implementation(projects.libs.tangemSdkApi) implementation(tangemDeps.card.core) implementation(tangemDeps.card.android) { 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 0ac07b8a3e..ff0ace4c4a 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 @@ -73,16 +73,20 @@ internal class OnboardingEntryModel @Inject constructor( uiMessageSender.send(CantLeaveBackupDialog) } + @Suppress("CyclomaticComplexMethod") private fun routeByProductType(scanResponse: ScanResponse): OnboardingRoute { return when (scanResponse.productType) { ProductType.Wallet, ProductType.Wallet2, ProductType.Ring, -> { - val multiWalletNavigationMode = when (params.mode) { - Mode.Onboarding -> OnboardingMultiWalletComponent.Mode.Onboarding - Mode.AddBackupWallet1 -> OnboardingMultiWalletComponent.Mode.AddBackup - Mode.ContinueFinalize -> OnboardingMultiWalletComponent.Mode.ContinueFinalize + val multiWalletNavigationMode = when (val mode = params.mode) { + is Mode.Onboarding -> OnboardingMultiWalletComponent.Mode.Onboarding + is Mode.AddBackupWallet1 -> OnboardingMultiWalletComponent.Mode.AddBackup + is Mode.ContinueFinalize -> OnboardingMultiWalletComponent.Mode.ContinueFinalize + is Mode.UpgradeHotWallet -> OnboardingMultiWalletComponent.Mode.UpgradeHotWallet( + userWalletId = mode.userWalletId, + ) else -> error("Incorrect onboarding type") } diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/api/OnboardingMultiWalletComponent.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/api/OnboardingMultiWalletComponent.kt index a6c7896af2..75a31024e6 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/api/OnboardingMultiWalletComponent.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/api/OnboardingMultiWalletComponent.kt @@ -5,6 +5,7 @@ import com.tangem.core.decompose.navigation.inner.InnerNavigationHolder import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.features.onboarding.v2.TitleProvider interface OnboardingMultiWalletComponent : ComposableContentComponent, InnerNavigationHolder { @@ -17,10 +18,11 @@ interface OnboardingMultiWalletComponent : ComposableContentComponent, InnerNavi val onDone: (UserWallet.Cold) -> Unit, ) - enum class Mode { - Onboarding, - AddBackup, - ContinueFinalize, + sealed class Mode { + data object Onboarding : Mode() + data object AddBackup : Mode() + data object ContinueFinalize : Mode() + data class UpgradeHotWallet(val userWalletId: UserWalletId) : Mode() } interface Factory : ComponentFactory diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/DefaultOnboardingMultiWalletComponent.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/DefaultOnboardingMultiWalletComponent.kt index 90696c234b..6a1d3cf78e 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/DefaultOnboardingMultiWalletComponent.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/DefaultOnboardingMultiWalletComponent.kt @@ -35,6 +35,7 @@ import com.tangem.features.onboarding.v2.multiwallet.impl.child.createwallet.Mul import com.tangem.features.onboarding.v2.multiwallet.impl.child.finalize.MultiWalletFinalizeComponent import com.tangem.features.onboarding.v2.multiwallet.impl.child.scanprimary.MultiWalletScanPrimaryComponent import com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.MultiWalletSeedPhraseComponent +import com.tangem.features.onboarding.v2.multiwallet.impl.child.upgradewallet.MultiWalletUpgradeWalletComponent import com.tangem.features.onboarding.v2.multiwallet.impl.model.OnboardingMultiWalletModel import com.tangem.features.onboarding.v2.multiwallet.impl.model.OnboardingMultiWalletState import com.tangem.features.onboarding.v2.multiwallet.impl.model.OnboardingMultiWalletState.Step.* @@ -57,6 +58,7 @@ internal class DefaultOnboardingMultiWalletComponent @AssistedInject constructor private val artworksState = instanceKeeper.getOrCreateSimple(key = "artworksState") { MutableStateFlow( when (model.state.value.currentStep) { + UpgradeWallet -> WalletArtworksState.Folded CreateWallet -> WalletArtworksState.Folded ChooseBackupOption -> WalletArtworksState.Fan SeedPhrase -> WalletArtworksState.Folded @@ -142,6 +144,11 @@ internal class DefaultOnboardingMultiWalletComponent @AssistedInject constructor childContext: AppComponentContext, ): ComposableContentComponent { return when (step) { + UpgradeWallet -> MultiWalletUpgradeWalletComponent( + context = childContext, + params = childParams, + onNextStep = ::handleNavigationEvent, + ) CreateWallet -> MultiWalletCreateWalletComponent( context = childContext, params = childParams, 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 e05be61546..65dd32233d 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 @@ -265,6 +265,16 @@ internal class MultiWalletFinalizeModel @Inject constructor( userWallet } + is OnboardingMultiWalletComponent.Mode.UpgradeHotWallet -> { + saveWalletUseCase( + userWallet = userWalletCreated.copy( + scanResponse = scanResponse.updateScanResponseAfterBackup(), + ), + canOverride = true, + ) + // TODO [REDACTED_TASK_KEY] remove hot wallet after upgrade + userWalletCreated + } }.requireColdWallet() if (hasRing) { diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/upgradewallet/MultiWalletUpgradeWalletComponent.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/upgradewallet/MultiWalletUpgradeWalletComponent.kt new file mode 100644 index 0000000000..222b173ffb --- /dev/null +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/upgradewallet/MultiWalletUpgradeWalletComponent.kt @@ -0,0 +1,56 @@ +package com.tangem.features.onboarding.v2.multiwallet.impl.child.upgradewallet + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.arkivanov.essenty.lifecycle.doOnStart +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.features.onboarding.v2.impl.R +import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildParams +import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildComponent +import com.tangem.features.onboarding.v2.multiwallet.impl.child.upgradewallet.model.MultiWalletUpgradeWalletModel +import com.tangem.features.onboarding.v2.multiwallet.impl.child.upgradewallet.ui.MultiWalletUpgradeWallet +import com.tangem.features.onboarding.v2.multiwallet.impl.model.OnboardingMultiWalletState +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch + +internal class MultiWalletUpgradeWalletComponent( + context: AppComponentContext, + params: MultiWalletChildParams, + onNextStep: (OnboardingMultiWalletState.Step) -> Unit, +) : AppComponentContext by context, MultiWalletChildComponent { + + private val model: MultiWalletUpgradeWalletModel = getOrCreateModel(params) + + init { + lifecycle.doOnStart { + params.innerNavigation.update { + it.copy( + stackSize = 2, + stackMaxSize = 9, + ) + } + + params.parentParams.titleProvider.changeTitle( + text = resourceReference(R.string.common_tangem), + ) + } + + componentScope.launch { + model.onDone.collect(onNextStep) + } + } + + @Composable + override fun Content(modifier: Modifier) { + val state by model.uiState.collectAsStateWithLifecycle() + + MultiWalletUpgradeWallet( + modifier = modifier, + state = state, + ) + } +} \ No newline at end of file diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/upgradewallet/model/MultiWalletUpgradeWalletModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/upgradewallet/model/MultiWalletUpgradeWalletModel.kt new file mode 100644 index 0000000000..f6b6306cd6 --- /dev/null +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/upgradewallet/model/MultiWalletUpgradeWalletModel.kt @@ -0,0 +1,181 @@ +package com.tangem.features.onboarding.v2.multiwallet.impl.child.upgradewallet.model + +import androidx.compose.runtime.Stable +import arrow.core.getOrElse +import com.tangem.common.CompletionResult +import com.tangem.common.core.TangemSdkError +import com.tangem.core.decompose.di.ModelScoped +import com.tangem.core.decompose.model.Model +import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.domain.card.repository.CardRepository +import com.tangem.domain.feedback.GetWalletMetaInfoUseCase +import com.tangem.domain.feedback.SendFeedbackEmailUseCase +import com.tangem.domain.feedback.models.FeedbackEmailType +import com.tangem.domain.models.scan.ScanResponse +import com.tangem.domain.wallets.builder.ColdUserWalletBuilder +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.wallets.usecase.ExportSeedPhraseUseCase +import com.tangem.domain.wallets.usecase.GetUserWalletUseCase +import com.tangem.domain.wallets.usecase.SaveWalletUseCase +import com.tangem.features.onboarding.v2.impl.R +import com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletComponent +import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildParams +import com.tangem.features.onboarding.v2.multiwallet.impl.child.upgradewallet.ui.state.MultiWalletUpgradeWalletUM +import com.tangem.features.onboarding.v2.multiwallet.impl.common.ui.resetCardDialog +import com.tangem.features.onboarding.v2.multiwallet.impl.model.OnboardingMultiWalletState.Step +import com.tangem.sdk.api.TangemSdkManager +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import javax.inject.Inject + +@Suppress("LongParameterList") +@Stable +@ModelScoped +internal class MultiWalletUpgradeWalletModel @Inject constructor( + paramsContainer: ParamsContainer, + override val dispatchers: CoroutineDispatcherProvider, + private val tangemSdkManager: TangemSdkManager, + private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase, + private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase, + private val cardRepository: CardRepository, + private val coldUserWalletBuilderFactory: ColdUserWalletBuilder.Factory, + private val saveWalletUseCase: SaveWalletUseCase, + private val getUserWalletUseCase: GetUserWalletUseCase, + private val exportSeedPhraseUseCase: ExportSeedPhraseUseCase, +) : Model() { + + private val params = paramsContainer.require() + private val multiWalletState + get() = params.multiWalletState + + private val _uiState = MutableStateFlow( + MultiWalletUpgradeWalletUM( + title = resourceReference(R.string.hw_upgrade_start_title), + bodyText = resourceReference(R.string.hw_upgrade_start_description), + onStartUpgradeClick = { + // TODO [REDACTED_TASK_KEY] track button click + upgradeWallet(false) + }, + dialog = null, + ), + ) + + val uiState: StateFlow = _uiState + val onDone = MutableSharedFlow() + + init { + // TODO [REDACTED_TASK_KEY] track screen opened + } + + private fun upgradeWallet(shouldReset: Boolean) { + modelScope.launch { + val mode = params.parentParams.mode + require(mode is OnboardingMultiWalletComponent.Mode.UpgradeHotWallet) + + val userWallet = getUserWalletUseCase(mode.userWalletId) + .getOrElse { error("User wallet with id ${mode.userWalletId} not found") } + if (userWallet is UserWallet.Hot) { + val privateInfo = exportSeedPhraseUseCase + .invoke(userWallet.hotWalletId) + .getOrElse { error("Unable to export seed phrase for wallet with id ${mode.userWalletId}") } + + modelScope.launch { + val result = tangemSdkManager.importWallet( + scanResponse = multiWalletState.value.currentScanResponse, + shouldReset = shouldReset, + mnemonic = privateInfo.mnemonic.mnemonicComponents.joinToString(" "), + passphrase = privateInfo.passphrase?.concatToString(), + ) + + when (result) { + is CompletionResult.Success -> { + multiWalletState.update { + it.copy( + currentScanResponse = it.currentScanResponse.copy( + card = result.data.card, + derivedKeys = result.data.derivedKeys, + primaryCard = result.data.primaryCard, + ), + ) + } + + cardRepository.startCardActivation(cardId = result.data.card.cardId) + + // TODO [REDACTED_TASK_KEY] track wallet created + val cardDoesNotSupportBackup = result.data.card.settings.isBackupAllowed.not() + when { + cardDoesNotSupportBackup -> createWalletAndNavigateBackWithDone() + params.parentParams.withSeedPhraseFlow -> onDone.emit(Step.AddBackupDevice) + else -> { + onDone.emit(Step.ChooseBackupOption) + } + } + } + + is CompletionResult.Failure -> { + if (result.error is TangemSdkError.WalletAlreadyCreated) { + // show should reset dialog + handleActivationError() + } + } + } + } + } + } + } + + private fun createWalletAndNavigateBackWithDone() { + modelScope.launch { + val scanResponse = params.multiWalletState.value.currentScanResponse + + val userWallet = createUserWallet(scanResponse) + saveWalletUseCase(userWallet, canOverride = true) + .onRight { + cardRepository.finishCardActivation(scanResponse.card.cardId) + + // save user wallet for manage tokens screen + params.multiWalletState.update { + it.copy(resultUserWallet = userWallet) + } + + onDone.emit(Step.Done) + } + } + } + + private fun createUserWallet(scanResponse: ScanResponse): UserWallet.Cold { + return requireNotNull( + value = coldUserWalletBuilderFactory.create(scanResponse = scanResponse).build(), + lazyMessage = { "User wallet not created" }, + ) + } + + private fun handleActivationError() { + _uiState.update { state -> + state.copy( + dialog = resetCardDialog( + onConfirm = ::navigateToSupportScreen, + dismiss = { _uiState.update { it.copy(dialog = null) } }, + onDismissButtonClick = ::resetCard, + ), + ) + } + } + + private fun resetCard() { + upgradeWallet(true) + } + + fun navigateToSupportScreen() { + modelScope.launch { + val cardInfo = + getWalletMetaInfoUseCase(multiWalletState.value.currentScanResponse).getOrNull() ?: return@launch + sendFeedbackEmailUseCase(FeedbackEmailType.DirectUserRequest(cardInfo)) + } + } +} \ No newline at end of file diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/upgradewallet/ui/MultiWalletUpgradeWallet.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/upgradewallet/ui/MultiWalletUpgradeWallet.kt new file mode 100644 index 0000000000..28ffd8f2f8 --- /dev/null +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/upgradewallet/ui/MultiWalletUpgradeWallet.kt @@ -0,0 +1,98 @@ +package com.tangem.features.onboarding.v2.multiwallet.impl.child.upgradewallet.ui + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.tangem.core.ui.components.BasicDialog +import com.tangem.core.ui.components.DialogButtonUM +import com.tangem.core.ui.components.PrimaryButtonIconEnd +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.features.onboarding.v2.impl.R +import com.tangem.features.onboarding.v2.multiwallet.impl.child.upgradewallet.ui.state.MultiWalletUpgradeWalletUM + +@Composable +internal fun MultiWalletUpgradeWallet(state: MultiWalletUpgradeWalletUM, modifier: Modifier = Modifier) { + if (state.dialog != null) { + BasicDialog( + title = state.dialog.title.resolveReference(), + message = state.dialog.message.resolveReference(), + confirmButton = DialogButtonUM( + title = state.dialog.confirmButtonText.resolveReference(), + onClick = state.dialog.onConfirmClick, + ), + dismissButton = DialogButtonUM( + title = state.dialog.dismissButtonText.resolveReference(), + warning = state.dialog.dismissWarningColor, + onClick = state.dialog.onDismissButtonClick, + ), + onDismissDialog = state.dialog.onDismiss, + ) + } + + Column( + modifier = modifier + .fillMaxSize() + .navigationBarsPadding(), + verticalArrangement = Arrangement.Bottom, + ) { + Column( + Modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()) + .padding(start = 32.dp, end = 32.dp, bottom = 16.dp) + .weight(1f), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text( + text = state.title.resolveReference(), + style = TangemTheme.typography.h2, + color = TangemTheme.colors.text.primary1, + textAlign = TextAlign.Center, + modifier = Modifier.padding(top = 16.dp), + ) + + Text( + text = state.bodyText.resolveReference(), + style = TangemTheme.typography.body1, + color = TangemTheme.colors.text.secondary, + textAlign = TextAlign.Center, + modifier = Modifier.padding(top = 12.dp), + ) + } + + PrimaryButtonIconEnd( + modifier = Modifier + .padding(start = 16.dp, end = 16.dp, bottom = 16.dp) + .fillMaxWidth(), + iconResId = R.drawable.ic_tangem_24, + text = stringResourceSafe(R.string.hw_upgrade_start_action), + onClick = state.onStartUpgradeClick, + ) + } +} + +@Preview(showBackground = true) +@Composable +private fun Preview() { + TangemThemePreview { + MultiWalletUpgradeWallet( + state = MultiWalletUpgradeWalletUM( + title = stringReference("Title"), + bodyText = stringReference("Body body body"), + onStartUpgradeClick = {}, + dialog = null, + ), + ) + } +} \ No newline at end of file diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/upgradewallet/ui/state/MultiWalletUpgradeWalletUM.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/upgradewallet/ui/state/MultiWalletUpgradeWalletUM.kt new file mode 100644 index 0000000000..c61c12a810 --- /dev/null +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/upgradewallet/ui/state/MultiWalletUpgradeWalletUM.kt @@ -0,0 +1,11 @@ +package com.tangem.features.onboarding.v2.multiwallet.impl.child.upgradewallet.ui.state + +import com.tangem.core.ui.extensions.TextReference +import com.tangem.features.onboarding.v2.common.ui.OnboardingDialogUM + +internal data class MultiWalletUpgradeWalletUM( + val title: TextReference, + val bodyText: TextReference, + val onStartUpgradeClick: () -> Unit, + val dialog: OnboardingDialogUM?, +) \ No newline at end of file diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/di/ComponentModule.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/di/ComponentModule.kt index 45a7844f49..86d6bdf5b2 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/di/ComponentModule.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/di/ComponentModule.kt @@ -11,6 +11,7 @@ import com.tangem.features.onboarding.v2.multiwallet.impl.child.createwallet.mod import com.tangem.features.onboarding.v2.multiwallet.impl.child.finalize.model.MultiWalletFinalizeModel import com.tangem.features.onboarding.v2.multiwallet.impl.child.scanprimary.model.MultiWalletScanPrimaryModel import com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.model.MultiWalletSeedPhraseModel +import com.tangem.features.onboarding.v2.multiwallet.impl.child.upgradewallet.model.MultiWalletUpgradeWalletModel import com.tangem.features.onboarding.v2.multiwallet.impl.model.OnboardingMultiWalletModel import dagger.Binds import dagger.Module @@ -72,4 +73,9 @@ internal interface ModelModule { @IntoMap @ClassKey(MultiWalletScanPrimaryModel::class) fun provideModel8(model: MultiWalletScanPrimaryModel): Model + + @Binds + @IntoMap + @ClassKey(MultiWalletUpgradeWalletModel::class) + fun provideModel9(model: MultiWalletUpgradeWalletModel): Model } \ No newline at end of file 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 bf9980e12d..9895662a0a 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 @@ -117,6 +117,9 @@ internal class OnboardingMultiWalletModel @Inject constructor( val card = scanResponse.card return when { + params.mode is OnboardingMultiWalletComponent.Mode.UpgradeHotWallet -> { + OnboardingMultiWalletState.Step.UpgradeWallet + } params.mode == OnboardingMultiWalletComponent.Mode.ContinueFinalize -> OnboardingMultiWalletState.Step.Finalize // Add backup button 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 aeb5dc4011..d1fa192d94 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 @@ -32,7 +32,14 @@ data class OnboardingMultiWalletState( * -> AddBackupDevice -> Finalize -> [Done] */ enum class Step { - CreateWallet, ChooseBackupOption, SeedPhrase, ScanPrimary, AddBackupDevice, Finalize, Done + UpgradeWallet, + CreateWallet, + ChooseBackupOption, + SeedPhrase, + ScanPrimary, + AddBackupDevice, + Finalize, + Done, } @JvmInline diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/model/Utils.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/model/Utils.kt index 1f4d1ee3a6..9a7b2c1476 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/model/Utils.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/model/Utils.kt @@ -14,4 +14,5 @@ fun screenTitleByStep(step: OnboardingMultiWalletState.Step): TextReference = wh OnboardingMultiWalletState.Step.ChooseBackupOption -> resourceReference(R.string.onboarding_getting_started) OnboardingMultiWalletState.Step.Finalize -> resourceReference(R.string.onboarding_button_finalize_backup) OnboardingMultiWalletState.Step.Done -> resourceReference(R.string.common_done) + OnboardingMultiWalletState.Step.UpgradeWallet -> resourceReference(R.string.common_tangem) } \ No newline at end of file diff --git a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/model/WalletSettingsModel.kt b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/model/WalletSettingsModel.kt index 3257456f21..85b018759e 100644 --- a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/model/WalletSettingsModel.kt +++ b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/model/WalletSettingsModel.kt @@ -363,32 +363,16 @@ internal class WalletSettingsModel @Inject constructor( if (!state.value.isWalletBackedUp) { messageSender.send(makeBackupAtFirstAlertBS) } else { - val userWallet = getUserWalletUseCase(params.userWalletId) - .getOrElse { error("User wallet with id ${params.userWalletId} not found") } - if (userWallet is UserWallet.Hot) { - val hotWalletId = userWallet.hotWalletId - when (hotWalletId.authType) { - HotWalletId.AuthType.NoPassword -> { - router.push(AppRoute.UpdateAccessCode(params.userWalletId)) - } - HotWalletId.AuthType.Password, - HotWalletId.AuthType.Biometry, - -> modelScope.launch { - unlockHotWalletContextualUseCase.invoke(hotWalletId) - .onLeft { - Timber.e("Unable to unlock wallet with id ${params.userWalletId}") - } - .onRight { - router.push(AppRoute.UpdateAccessCode(params.userWalletId)) - } - } - } + unlockWalletIfNeedAndProceed { + router.push(AppRoute.UpdateAccessCode(params.userWalletId)) } } } private fun onUpgradeWalletClick() { - router.push(AppRoute.UpgradeWallet(params.userWalletId)) + unlockWalletIfNeedAndProceed { + router.push(AppRoute.UpgradeWallet(params.userWalletId)) + } } private fun onDismissUpgradeWalletClick() { @@ -396,4 +380,28 @@ internal class WalletSettingsModel @Inject constructor( dismissUpgradeWalletNotificationUseCase.invoke(params.userWalletId) } } + + private fun unlockWalletIfNeedAndProceed(action: () -> Unit) { + val userWallet = getUserWalletUseCase(params.userWalletId) + .getOrElse { error("User wallet with id ${params.userWalletId} not found") } + if (userWallet is UserWallet.Hot) { + val hotWalletId = userWallet.hotWalletId + when (hotWalletId.authType) { + HotWalletId.AuthType.NoPassword -> { + action() + } + HotWalletId.AuthType.Password, + HotWalletId.AuthType.Biometry, + -> modelScope.launch { + unlockHotWalletContextualUseCase.invoke(hotWalletId) + .onLeft { + Timber.e("Unable to unlock wallet with id ${params.userWalletId}") + } + .onRight { + action() + } + } + } + } + } } \ No newline at end of file