Updated on 2026-08-14
This commit is contained in:
parent
324ae269fe
commit
54833d8fc6
14 changed files with 166 additions and 111 deletions
|
|
@ -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<ScanResponse?>(value = null)
|
||||
|
||||
val screenState: MutableStateFlow<CardSettingsScreenState> = 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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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<Bundle>(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<Bundle>(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<Bundle>(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<AppRoute.Welcome>() }
|
||||
} else {
|
||||
store.dispatchNavigationAction { popTo<AppRoute.Home>() }
|
||||
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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue