From 54833d8fc6fa76c50b88126298377c6657b8e854 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 23 Jul 2024 17:53:30 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../domain/card/DefaultResetCardUseCase.kt | 40 ++++---- .../tangem/tap/domain/sdk/TangemSdkManager.kt | 4 +- .../sdk/impl/DefaultTangemSdkManager.kt | 5 +- .../domain/sdk/impl/MockTangemSdkManager.kt | 8 +- .../tasks/product/ResetBackupCardTask.kt | 8 +- .../product/ResetToFactorySettingsTask.kt | 15 +-- .../ui/cardsettings/CardSettingsViewModel.kt | 49 +++++----- .../details/ui/common/utils/ResetToFactory.kt | 7 +- .../ui/resetcard/ResetCardViewModel.kt | 96 +++++++++++-------- common/routing/build.gradle.kts | 1 + .../com/tangem/common/routing/AppRoute.kt | 14 ++- .../tangem/domain/card/ResetCardUseCase.kt | 18 ++-- domain/models/build.gradle.kts | 2 + .../com/tangem/domain/models/scan/CardDTO.kt | 10 ++ 14 files changed, 166 insertions(+), 111 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/card/DefaultResetCardUseCase.kt b/app/src/main/java/com/tangem/tap/domain/card/DefaultResetCardUseCase.kt index 966f9613e9..1e322aeaba 100644 --- a/app/src/main/java/com/tangem/tap/domain/card/DefaultResetCardUseCase.kt +++ b/app/src/main/java/com/tangem/tap/domain/card/DefaultResetCardUseCase.kt @@ -11,8 +11,8 @@ import com.tangem.common.core.TangemError import com.tangem.common.core.TangemSdkError import com.tangem.common.core.UserCodeRequestPolicy import com.tangem.domain.card.ResetCardUseCase +import com.tangem.domain.card.ResetCardUserCodeParams import com.tangem.domain.card.models.ResetCardError -import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.wallets.models.UserWalletId import com.tangem.tap.domain.sdk.TangemSdkManager @@ -20,24 +20,25 @@ internal class DefaultResetCardUseCase( private val tangemSdkManager: TangemSdkManager, ) : ResetCardUseCase { - override suspend fun invoke(card: CardDTO): Either = resourceScope { - either { - withUserCodeRequestPolicy(card) + override suspend fun invoke(cardId: String, params: ResetCardUserCodeParams): Either = + resourceScope { + either { + withUserCodeRequestPolicy(params) - tangemSdkManager.resetToFactorySettings( - cardId = card.cardId, - allowsRequestAccessCodeFromRepository = true, - ).bind(raise = this) + tangemSdkManager.resetToFactorySettings( + cardId = cardId, + allowsRequestAccessCodeFromRepository = true, + ).bind(raise = this) + } } - } override suspend fun invoke( cardNumber: Int, - card: CardDTO, + params: ResetCardUserCodeParams, userWalletId: UserWalletId, - ): Either = resourceScope { + ): Either = resourceScope { either { - withUserCodeRequestPolicy(card) + withUserCodeRequestPolicy(params) tangemSdkManager.resetBackupCard( cardNumber = cardNumber, @@ -46,11 +47,11 @@ internal class DefaultResetCardUseCase( } } - private suspend fun ResourceScope.withUserCodeRequestPolicy(card: CardDTO) { + private suspend fun ResourceScope.withUserCodeRequestPolicy(params: ResetCardUserCodeParams) { install( acquire = { val policyBeforeReset = tangemSdkManager.userCodeRequestPolicy - requestMandatoryAccessCodeEntry(card) + requestMandatoryAccessCodeEntry(params) policyBeforeReset }, @@ -60,10 +61,10 @@ internal class DefaultResetCardUseCase( ) } - private fun requestMandatoryAccessCodeEntry(card: CardDTO) { - val type = if (card.isAccessCodeSet) { + private fun requestMandatoryAccessCodeEntry(params: ResetCardUserCodeParams) { + val type = if (params.isAccessCodeSet) { UserCodeType.AccessCode - } else if (card.isPasscodeSet == true) { + } else if (params.isPasscodeSet == true) { UserCodeType.Passcode } else { null @@ -74,15 +75,14 @@ internal class DefaultResetCardUseCase( } } - private fun CompletionResult<*>.bind(raise: Raise) { + private fun CompletionResult.bind(raise: Raise): Boolean { return when (this) { is CompletionResult.Failure -> { val domainError = error.mapToDomainError() raise.raise(domainError) } - is CompletionResult.Success -> { /* no-op */ - } + is CompletionResult.Success -> data } } diff --git a/app/src/main/java/com/tangem/tap/domain/sdk/TangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/sdk/TangemSdkManager.kt index 0f5e408f5e..2dbfaec5b7 100644 --- a/app/src/main/java/com/tangem/tap/domain/sdk/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/sdk/TangemSdkManager.kt @@ -70,9 +70,9 @@ interface TangemSdkManager { suspend fun resetToFactorySettings( cardId: String, allowsRequestAccessCodeFromRepository: Boolean, - ): CompletionResult + ): CompletionResult - suspend fun resetBackupCard(cardNumber: Int, userWalletId: UserWalletId): CompletionResult + suspend fun resetBackupCard(cardNumber: Int, userWalletId: UserWalletId): CompletionResult suspend fun saveAccessCode(accessCode: String, cardsIds: Set): CompletionResult diff --git a/app/src/main/java/com/tangem/tap/domain/sdk/impl/DefaultTangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/sdk/impl/DefaultTangemSdkManager.kt index 00bacdbe26..835a2fee69 100644 --- a/app/src/main/java/com/tangem/tap/domain/sdk/impl/DefaultTangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/sdk/impl/DefaultTangemSdkManager.kt @@ -202,7 +202,7 @@ class DefaultTangemSdkManager( override suspend fun resetToFactorySettings( cardId: String, allowsRequestAccessCodeFromRepository: Boolean, - ): CompletionResult { + ): CompletionResult { return runTaskAsyncReturnOnMain( runnable = ResetToFactorySettingsTask( allowsRequestAccessCodeFromRepository = allowsRequestAccessCodeFromRepository, @@ -210,10 +210,9 @@ class DefaultTangemSdkManager( cardId = cardId, initialMessage = Message(resources.getString(R.string.card_settings_reset_card_to_factory)), ) - .map { CardDTO(it) } } - override suspend fun resetBackupCard(cardNumber: Int, userWalletId: UserWalletId): CompletionResult { + override suspend fun resetBackupCard(cardNumber: Int, userWalletId: UserWalletId): CompletionResult { return runTaskAsyncReturnOnMain( runnable = ResetBackupCardTask(userWalletId), initialMessage = Message( diff --git a/app/src/main/java/com/tangem/tap/domain/sdk/impl/MockTangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/sdk/impl/MockTangemSdkManager.kt index dcc850f798..0dffc198a7 100644 --- a/app/src/main/java/com/tangem/tap/domain/sdk/impl/MockTangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/sdk/impl/MockTangemSdkManager.kt @@ -87,12 +87,12 @@ class MockTangemSdkManager( override suspend fun resetToFactorySettings( cardId: String, allowsRequestAccessCodeFromRepository: Boolean, - ): CompletionResult { - return MockProvider.getCardDto() + ): CompletionResult { + return CompletionResult.Success(true) } - override suspend fun resetBackupCard(cardNumber: Int, userWalletId: UserWalletId): CompletionResult { - return CompletionResult.Success(Unit) + override suspend fun resetBackupCard(cardNumber: Int, userWalletId: UserWalletId): CompletionResult { + return CompletionResult.Success(true) } override suspend fun saveAccessCode(accessCode: String, cardsIds: Set): CompletionResult { diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/ResetBackupCardTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/ResetBackupCardTask.kt index 7de7ceb374..288ca22a9d 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/ResetBackupCardTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/ResetBackupCardTask.kt @@ -19,11 +19,11 @@ import com.tangem.tap.domain.tasks.UserWalletIdPreflightReadFilter */ internal class ResetBackupCardTask( private val userWalletId: UserWalletId, -) : CardSessionRunnable { +) : CardSessionRunnable { override val allowsRequestAccessCodeFromRepository: Boolean = false - override fun run(session: CardSession, callback: CompletionCallback) { + override fun run(session: CardSession, callback: CompletionCallback) { PreflightReadTask( readMode = PreflightReadMode.FullCardRead, filter = UserWalletIdPreflightReadFilter(expectedUserWalletId = userWalletId), @@ -35,10 +35,10 @@ internal class ResetBackupCardTask( } } - private fun resetCard(session: CardSession, callback: CompletionCallback) { + private fun resetCard(session: CardSession, callback: CompletionCallback) { ResetToFactorySettingsTask(allowsRequestAccessCodeFromRepository).run(session) { result -> when (result) { - is CompletionResult.Success -> callback(CompletionResult.Success(Unit)) + is CompletionResult.Success -> callback(CompletionResult.Success(result.data)) is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) } } diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/ResetToFactorySettingsTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/ResetToFactorySettingsTask.kt index f06f2f801a..51ff0b2d80 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/ResetToFactorySettingsTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/ResetToFactorySettingsTask.kt @@ -10,13 +10,15 @@ import com.tangem.operations.wallet.PurgeWalletCommand class ResetToFactorySettingsTask( override val allowsRequestAccessCodeFromRepository: Boolean, -) : CardSessionRunnable { +) : CardSessionRunnable { - override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { + private var isResetCompleted = false + + override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { deleteWallets(session, callback) } - private fun deleteWallets(session: CardSession, callback: (result: CompletionResult) -> Unit) { + private fun deleteWallets(session: CardSession, callback: (result: CompletionResult) -> Unit) { val wallet = session.environment.card?.wallets?.lastOrNull().guard { resetBackup(session, callback) return @@ -25,6 +27,7 @@ class ResetToFactorySettingsTask( PurgeWalletCommand(wallet.publicKey).run(session) { result -> when (result) { is CompletionResult.Success -> { + isResetCompleted = true deleteWallets(session, callback) } is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) @@ -32,18 +35,18 @@ class ResetToFactorySettingsTask( } } - private fun resetBackup(session: CardSession, callback: (result: CompletionResult) -> Unit) { + private fun resetBackup(session: CardSession, callback: (result: CompletionResult) -> Unit) { if (session.environment.card?.backupStatus == null || session.environment.card?.backupStatus == Card.BackupStatus.NoBackup ) { - callback(CompletionResult.Success(session.environment.card!!)) + callback(CompletionResult.Success(isResetCompleted)) return } ResetBackupCommand().run(session) { result -> when (result) { is CompletionResult.Success -> { - callback(CompletionResult.Success(session.environment.card!!)) + callback(CompletionResult.Success(true)) } is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsViewModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsViewModel.kt index 4715114d15..97d4a464d9 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsViewModel.kt @@ -4,7 +4,6 @@ import android.os.Bundle import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import arrow.core.getOrElse import com.tangem.common.CompletionResult import com.tangem.common.doOnSuccess import com.tangem.common.routing.AppRoute @@ -14,10 +13,9 @@ import com.tangem.domain.card.ScanCardProcessor import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.models.scan.CardDTO +import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.wallets.builder.UserWalletIdBuilder -import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.Settings import com.tangem.tap.common.extensions.dispatchDialogShow @@ -38,7 +36,6 @@ import javax.inject.Inject @HiltViewModel internal class CardSettingsViewModel @Inject constructor( private val scanCardProcessor: ScanCardProcessor, - private val getUserWalletUseCase: GetUserWalletUseCase, private val tangemSdkManager: TangemSdkManager, savedStateHandle: SavedStateHandle, ) : ViewModel() { @@ -47,6 +44,8 @@ internal class CardSettingsViewModel @Inject constructor( ?.unbundle(UserWalletId.serializer()) ?: error("User wallet ID is required for CardSettingsViewModel") + private val scannedScanResponse = MutableStateFlow(value = null) + val screenState: MutableStateFlow = MutableStateFlow(getInitialState()) private fun getInitialState() = CardSettingsScreenState( @@ -58,13 +57,11 @@ internal class CardSettingsViewModel @Inject constructor( private fun scanCard() = viewModelScope.launch { scanCardProcessor.scan(allowsRequestAccessCodeFromRepository = true) .doOnSuccess { scanResponse -> + scannedScanResponse.value = scanResponse + val scannedUserWalletId = UserWalletIdBuilder.scanResponse(scanResponse).build() - val isCorrectUserWalletScanned = scannedUserWalletId == userWalletId - - if (isCorrectUserWalletScanned) { - val userWallet = getUserWallet() - - updateCardDetails(userWallet) + if (userWalletId == scannedUserWalletId || scannedUserWalletId == null) { + updateCardDetails(scanResponse) } else { store.dispatchDialogShow( AppDialog.SimpleOkDialogRes( @@ -76,9 +73,9 @@ internal class CardSettingsViewModel @Inject constructor( } } - private fun updateCardDetails(userWallet: UserWallet) { - val card = userWallet.scanResponse.card - val cardTypesResolver = userWallet.scanResponse.cardTypesResolver + private fun updateCardDetails(scanResponse: ScanResponse) { + val card = scanResponse.card + val cardTypesResolver = scanResponse.cardTypesResolver val cardId = cardTypesResolver.getCardId() val currentSecurityOption = getCurrentSecurityOption(card) @@ -112,7 +109,7 @@ internal class CardSettingsViewModel @Inject constructor( if (isResetCardAllowed) { CardInfo.ResetToFactorySettings( - description = getResetToFactoryDescription(card, cardTypesResolver), + description = getResetToFactoryDescription(card.backupStatus, cardTypesResolver), ).let(::add) } } @@ -129,9 +126,21 @@ internal class CardSettingsViewModel @Inject constructor( changeAccessCode() } is CardInfo.ResetToFactorySettings -> { + val card = requireNotNull(scannedScanResponse.value) { + "Impossible to reset card if ScanResponse is null" + }.card + Analytics.send(Settings.CardSettings.ButtonFactoryReset()) store.dispatchNavigationAction { - push(route = AppRoute.ResetToFactory(userWalletId)) + push( + route = AppRoute.ResetToFactory( + userWalletId = userWalletId, + cardSpecificInfo = AppRoute.ResetToFactory.CardSpecificInfo( + cardId = card.cardId, + backupStatus = card.backupStatus, + ), + ), + ) } } is CardInfo.SecurityMode -> { @@ -150,9 +159,9 @@ internal class CardSettingsViewModel @Inject constructor( } private fun changeAccessCode() = viewModelScope.launch { - val card = getUserWallet().scanResponse.card + val scanResponse = requireNotNull(scannedScanResponse.value) { "Scan response is null" } - when (val result = tangemSdkManager.setAccessCode(card.cardId)) { + when (val result = tangemSdkManager.setAccessCode(scanResponse.card.cardId)) { is CompletionResult.Success -> Analytics.send(Settings.CardSettings.UserCodeChanged()) is CompletionResult.Failure -> { Timber.e("Failed to change access code: ${result.error}") @@ -160,12 +169,6 @@ internal class CardSettingsViewModel @Inject constructor( } } - private fun getUserWallet(): UserWallet { - return getUserWalletUseCase(userWalletId).getOrElse { - error("Failed to get user wallet $userWalletId: $it") - } - } - private fun isResetToFactoryAllowedByCard(card: CardDTO, cardTypesResolver: CardTypesResolver): Boolean { val hasPermanentWallet = card.wallets.any { it.settings.isPermanent } val isNotAllowed = hasPermanentWallet || cardTypesResolver.isStart2Coin() diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/common/utils/ResetToFactory.kt b/app/src/main/java/com/tangem/tap/features/details/ui/common/utils/ResetToFactory.kt index b576e8193e..5be7b9eb88 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/common/utils/ResetToFactory.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/common/utils/ResetToFactory.kt @@ -5,8 +5,11 @@ import com.tangem.domain.models.scan.CardDTO import com.tangem.tap.features.details.ui.cardsettings.TextReference import com.tangem.wallet.R -internal fun getResetToFactoryDescription(card: CardDTO, typesResolver: CardTypesResolver): TextReference { - return if (card.backupStatus?.isActive != true || typesResolver.isTangemTwins()) { +internal fun getResetToFactoryDescription( + backupStatus: CardDTO.BackupStatus?, + typesResolver: CardTypesResolver, +): TextReference { + return if (backupStatus?.isActive != true || typesResolver.isTangemTwins()) { TextReference.Res(R.string.reset_card_without_backup_to_factory_message) } else { TextReference.Res(R.string.reset_card_with_backup_to_factory_message) diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardViewModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardViewModel.kt index d578f0875f..2131cd42e2 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardViewModel.kt @@ -11,12 +11,11 @@ import com.tangem.common.routing.utils.popTo import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.domain.card.DeleteSavedAccessCodesUseCase import com.tangem.domain.card.ResetCardUseCase +import com.tangem.domain.card.ResetCardUserCodeParams import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.models.scan.CardDTO -import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.legacy.asLockable -import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.usecase.DeleteWalletUseCase import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase @@ -39,7 +38,7 @@ import javax.inject.Inject @Suppress("LongParameterList") @HiltViewModel internal class ResetCardViewModel @Inject constructor( - private val getUserWalletUseCase: GetUserWalletUseCase, + getUserWalletUseCase: GetUserWalletUseCase, private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, private val resetCardUseCase: ResetCardUseCase, private val deleteSavedAccessCodesUseCase: DeleteSavedAccessCodesUseCase, @@ -49,9 +48,26 @@ internal class ResetCardViewModel @Inject constructor( savedStateHandle: SavedStateHandle, ) : ViewModel() { - private val userWalletId = savedStateHandle.get(AppRoute.ResetToFactory.USER_WALLET_ID) + // region Card-set specific data. All cards from single set have the same userWalletId and cardTypesResolver + private val currentUserWalletId = savedStateHandle.get(AppRoute.ResetToFactory.USER_WALLET_ID) ?.unbundle(UserWalletId.serializer()) - ?: error("User wallet ID must be provided for ResetCardViewModel") + ?: error("UserWalletId must be provided for ResetCardViewModel") + + // Use only for card-specific data + private val userWallet = getUserWalletUseCase(userWalletId = currentUserWalletId) + .getOrElse { error("Failed to get user wallet: $it") } + + private val currentCardTypesResolver = userWallet.cardTypesResolver + private val currentUserCodeParams = ResetCardUserCodeParams( + isAccessCodeSet = userWallet.scanResponse.card.isAccessCodeSet, + isPasscodeSet = userWallet.scanResponse.card.isPasscodeSet, + ) + // endregion + + // region Data of card that was scanned on CardSettings + private val primaryCardId: String + private val primaryBackupStatus: CardDTO.BackupStatus? + // endregion // TODO: move logic to separate domain entity private var resetBackupCardCount = 0 @@ -60,29 +76,33 @@ internal class ResetCardViewModel @Inject constructor( value = getInitialState(), ) - private fun getInitialState(): ResetCardScreenState { - val userWallet = getUserWalletUseCase(userWalletId).getOrElse { - error("Failed to get user wallet $userWalletId: $it") - } - val card = userWallet.scanResponse.card - val cardTypesResolver = userWallet.scanResponse.cardTypesResolver - val descriptionText = getResetToFactoryDescription(card, cardTypesResolver) - val isTangemWallet = cardTypesResolver.isTangemWallet() || cardTypesResolver.isWallet2() - val showResetPasswordButton = isTangemWallet && card.backupStatus is CardDTO.BackupStatus.Active + init { + val cardSpecificInfo = savedStateHandle.get(AppRoute.ResetToFactory.CARD_SPECIFIC_DATA) + ?.unbundle(AppRoute.ResetToFactory.CardSpecificInfo.serializer()) + ?: error("CardSpecificData must be provided for ResetCardViewModel") + primaryCardId = cardSpecificInfo.cardId + primaryBackupStatus = cardSpecificInfo.backupStatus + } + + private fun getInitialState(): ResetCardScreenState { + val shouldShowResetPasswordButton = shouldShowResetPasswordButton() val warningsToShow = buildList { add(ResetCardScreenState.WarningsToReset.LOST_WALLET_ACCESS) - if (showResetPasswordButton) { + if (shouldShowResetPasswordButton) { add(ResetCardScreenState.WarningsToReset.LOST_PASSWORD_RESTORE) } } return ResetCardScreenState( resetButtonEnabled = false, - descriptionText = descriptionText, + descriptionText = getResetToFactoryDescription( + backupStatus = primaryBackupStatus, + typesResolver = currentCardTypesResolver, + ), warningsToShow = warningsToShow, - showResetPasswordButton = showResetPasswordButton, + showResetPasswordButton = shouldShowResetPasswordButton, acceptCondition1Checked = false, acceptCondition2Checked = false, onAcceptCondition1ToggleClick = ::toggleFirstCondition, @@ -92,6 +112,12 @@ internal class ResetCardViewModel @Inject constructor( ) } + private fun shouldShowResetPasswordButton(): Boolean { + val isTangemWallet = currentCardTypesResolver.isTangemWallet() || currentCardTypesResolver.isWallet2() + + return isTangemWallet && primaryBackupStatus is CardDTO.BackupStatus.Active + } + private fun toggleFirstCondition(isAccepted: Boolean) { screenState.update { prevState -> val resetButtonEnabled = if (prevState.showResetPasswordButton) { @@ -154,12 +180,9 @@ internal class ResetCardViewModel @Inject constructor( private fun makeFullReset() { viewModelScope.launch { - val userWallet = getUserWallet() - val scanResponse = userWallet.scanResponse - - resetCardUseCase(card = scanResponse.card).onRight { - deleteSavedAccessCodesUseCase(scanResponse.card.cardId) - val hasUserWallets = deleteWalletUseCase(userWalletId).getOrElse { + resetCardUseCase(cardId = primaryCardId, params = currentUserCodeParams).onRight { + deleteSavedAccessCodesUseCase(cardId = primaryCardId) + val hasUserWallets = deleteWalletUseCase(userWalletId = currentUserWalletId).getOrElse { Timber.e("Unable to delete user wallet: $it") return@launch } @@ -183,14 +206,15 @@ internal class ResetCardViewModel @Inject constructor( dismissDialog() viewModelScope.launch { - val userWallet = getUserWallet() resetCardUseCase( cardNumber = resetBackupCardCount + 1, - card = userWallet.scanResponse.card, - userWalletId = userWalletId, + params = currentUserCodeParams, + userWalletId = currentUserWalletId, ) - .onRight { - resetBackupCardCount++ + .onRight { isResetCompleted -> + if (isResetCompleted) { + resetBackupCardCount++ + } delay(DELAY_SDK_DIALOG_CLOSE) @@ -213,7 +237,7 @@ internal class ResetCardViewModel @Inject constructor( } private fun checkRemainingBackupCards() { - val backupCardsCount = getUserWallet().scanResponse.getBackupCardsCount() + val backupCardsCount = getBackupCardsCount() when { backupCardsCount > resetBackupCardCount -> showDialog(ResetCardDialog.ContinueResetDialog) @@ -227,12 +251,6 @@ internal class ResetCardViewModel @Inject constructor( } } - private fun getUserWallet(): UserWallet { - return getUserWalletUseCase(userWalletId).getOrElse { - error("Failed to get user wallet $userWalletId: $it") - } - } - private fun dismissAndFinishFullReset() { dismissDialog() @@ -249,7 +267,7 @@ internal class ResetCardViewModel @Inject constructor( if (isLocked && userWalletsListManager.hasUserWallets) { store.dispatchNavigationAction { popTo() } } else { - store.dispatchNavigationAction { popTo() } + store.dispatchNavigationAction { replaceAll(AppRoute.Home) } } } } @@ -262,10 +280,10 @@ internal class ResetCardViewModel @Inject constructor( screenState.update { it.copy(dialog = null) } } - private fun ScanResponse.getBackupCardsCount(): Int { - if (!cardTypesResolver.isMultiwalletAllowed()) return 0 + private fun getBackupCardsCount(): Int { + if (!currentCardTypesResolver.isMultiwalletAllowed()) return 0 - return when (val status = card.backupStatus) { + return when (val status = primaryBackupStatus) { is CardDTO.BackupStatus.Active -> status.cardCount is CardDTO.BackupStatus.CardLinked, is CardDTO.BackupStatus.NoBackup, diff --git a/common/routing/build.gradle.kts b/common/routing/build.gradle.kts index ae4552a1f0..a492ede7aa 100644 --- a/common/routing/build.gradle.kts +++ b/common/routing/build.gradle.kts @@ -15,6 +15,7 @@ dependencies { /* Domain */ implementation(projects.domain.qrScanning.models) + implementation(projects.domain.models) implementation(projects.domain.tokens.models) implementation(projects.domain.wallets.models) implementation(projects.domain.staking.models) 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 bdd1ea3030..8f9796de2b 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 @@ -5,6 +5,7 @@ import com.tangem.common.routing.bundle.RouteBundleParams import com.tangem.common.routing.bundle.bundle import com.tangem.common.routing.entity.SerializableIntent import com.tangem.core.decompose.navigation.Route +import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.qrscanning.models.SourceType import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.tokens.model.CryptoCurrency @@ -143,15 +144,26 @@ sealed class AppRoute(val path: String) : Route { @Serializable data object AppSettings : AppRoute(path = "/app_settings") + /** + * Reset to factory route + * + * @property userWalletId user wallet id + * @property cardSpecificInfo info about card that was scanned on CardSettings + */ @Serializable data class ResetToFactory( val userWalletId: UserWalletId, - ) : AppRoute(path = "/reset_to_factory/${userWalletId.stringValue}"), RouteBundleParams { + val cardSpecificInfo: CardSpecificInfo, + ) : AppRoute(path = "/reset_to_factory/${userWalletId.stringValue}/$cardSpecificInfo"), RouteBundleParams { override fun getBundle(): Bundle = bundle(serializer()) + @Serializable + data class CardSpecificInfo(val cardId: String, val backupStatus: CardDTO.BackupStatus?) + companion object { const val USER_WALLET_ID = "userWalletId" + const val CARD_SPECIFIC_DATA = "cardSpecificInfo" } } diff --git a/domain/card/src/main/kotlin/com/tangem/domain/card/ResetCardUseCase.kt b/domain/card/src/main/kotlin/com/tangem/domain/card/ResetCardUseCase.kt index 7bdd4f2293..7a71009602 100644 --- a/domain/card/src/main/kotlin/com/tangem/domain/card/ResetCardUseCase.kt +++ b/domain/card/src/main/kotlin/com/tangem/domain/card/ResetCardUseCase.kt @@ -2,7 +2,6 @@ package com.tangem.domain.card import arrow.core.Either import com.tangem.domain.card.models.ResetCardError -import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.wallets.models.UserWalletId /** @@ -12,13 +11,18 @@ import com.tangem.domain.wallets.models.UserWalletId */ interface ResetCardUseCase { - /** Reset card [card] to factory settings */ - suspend operator fun invoke(card: CardDTO): Either + /** Reset card [cardId] to factory settings */ + suspend operator fun invoke(cardId: String, params: ResetCardUserCodeParams): Either - /** Reset backup card [cardNumber] with expected [UserWalletId] using [card] of reset card */ + /** Reset backup card [cardNumber] with expected [UserWalletId] using [params] of reset card */ suspend operator fun invoke( cardNumber: Int, - card: CardDTO, + params: ResetCardUserCodeParams, userWalletId: UserWalletId, - ): Either -} \ No newline at end of file + ): Either +} + +data class ResetCardUserCodeParams( + val isAccessCodeSet: Boolean, + val isPasscodeSet: Boolean?, +) \ No newline at end of file diff --git a/domain/models/build.gradle.kts b/domain/models/build.gradle.kts index fbccb4120d..bf25c0f8b6 100644 --- a/domain/models/build.gradle.kts +++ b/domain/models/build.gradle.kts @@ -1,9 +1,11 @@ plugins { alias(deps.plugins.kotlin.jvm) + alias(deps.plugins.kotlin.serialization) id("configuration") } dependencies { implementation(deps.tangem.card.core) implementation(deps.moshi.kotlin) + implementation(deps.kotlin.serialization) } \ No newline at end of file diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/scan/CardDTO.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/scan/CardDTO.kt index ff2b5be1d0..91ceeb0049 100644 --- a/domain/models/src/main/kotlin/com/tangem/domain/models/scan/CardDTO.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/scan/CardDTO.kt @@ -8,6 +8,8 @@ import com.tangem.common.card.EncryptionMode import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey import com.tangem.operations.attestation.Attestation +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable import java.util.Date import com.tangem.common.card.FirmwareVersion as SdkFirmwareVersion @@ -298,11 +300,19 @@ data class CardDTO( } } + @Serializable sealed class BackupStatus { + + @Serializable + @SerialName("card_linked") data class CardLinked(val cardCount: Int) : BackupStatus() + @Serializable + @SerialName("active") data class Active(val cardCount: Int) : BackupStatus() + @Serializable + @SerialName("no_backup") data object NoBackup : BackupStatus() val isActive: Boolean