Updated on 2026-08-14

This commit is contained in:
Tangem 2025-11-17 10:43:29 +03:00
parent c5f7baa0ff
commit 6ad2d142a4
10 changed files with 55 additions and 25 deletions

View file

@ -29,7 +29,14 @@ internal object LegacyMiddleware {
val walletsRepository = store.inject(DaggerGraphState::walletsRepository)
selectedUserWallet()
.distinctUntilChanged()
.distinctUntilChanged { old, new ->
if (old is UserWallet.Cold && new is UserWallet.Cold) {
old.walletId == new.walletId &&
old.scanResponse == new.scanResponse
} else {
old.walletId == new.walletId
}
}
.onEach { selectedUserWallet ->
val initializedAppSettingsStateContent = initializeAppSettingsState(
shouldSaveUserWallets = walletsRepository.shouldSaveUserWalletsSync(),
@ -78,6 +85,7 @@ internal object LegacyMiddleware {
isHidingEnabled = store.inject(DaggerGraphState::balanceHidingRepository)
.getBalanceHidingSettings().isHidingEnabledInSettings,
needEnrollBiometrics = runCatching(tangemSdkManager::needEnrollBiometrics).getOrNull() == true,
hasSecuredWallets = store.inject(DaggerGraphState::userWalletsListRepository).hasSecuredWallets(),
)
}
}

View file

@ -483,4 +483,12 @@ internal object WalletsDomainModule {
dispatchers = dispatcherProvider,
)
}
@Provides
@Singleton
fun provideHasSecuredWalletsUseCase(
userWalletsListRepository: UserWalletsListRepository,
): HasSecuredWalletsUseCase {
return HasSecuredWalletsUseCase(userWalletsListRepository = userWalletsListRepository)
}
}

View file

@ -345,6 +345,12 @@ internal class DefaultUserWalletsListRepository(
userWalletEncryptionKeysRepository.clear()
}
override suspend fun hasSecuredWallets(): Boolean {
val userWallets = userWalletsSync()
val unsecuredWalletIds = userWalletEncryptionKeysRepository.getAllUnsecured().map { it.walletId }.toSet()
return userWallets.any { it.walletId !in unsecuredWalletIds }
}
private suspend fun requestPasswordRecursive(
hotWalletId: HotWalletId,
block: suspend (CharArray) -> UserWalletEncryptionKey?,

View file

@ -66,14 +66,8 @@ class DetailsMiddleware {
when (action.setting) {
AppSetting.SaveWallets -> toggleSaveWallets(state, enable = action.enable)
AppSetting.SaveAccessCode -> toggleSaveAccessCodes(state, enable = action.enable)
AppSetting.RequireAccessCode -> toggleRequireAccessCode(
state = state,
enable = action.enable,
)
AppSetting.BiometricAuthentication -> toggleBiometricsAuthentication(
state = state,
enable = action.enable,
)
AppSetting.RequireAccessCode -> toggleRequireAccessCode(enable = action.enable)
AppSetting.BiometricAuthentication -> toggleBiometricsAuthentication(enable = action.enable)
}
}
is DetailsAction.AppSettings.CheckBiometricsStatus -> {
@ -100,7 +94,7 @@ class DetailsMiddleware {
}
}
private fun toggleBiometricsAuthentication(state: DetailsState, enable: Boolean) {
private fun toggleBiometricsAuthentication(enable: Boolean) {
scope.launch {
val walletsRepository = store.inject(DaggerGraphState::walletsRepository)
@ -110,16 +104,12 @@ class DetailsMiddleware {
return@launch
}
toggleRequireAccessCode(
state = state,
enable = true,
)
if (enable) {
setBiometricLockForAllWallets()
} else {
// Remove all biometric-related data
removeAllBiometricData()
walletsRepository.setRequireAccessCode(value = true)
}
walletsRepository.setUseBiometricAuthentication(value = enable)
@ -127,7 +117,7 @@ class DetailsMiddleware {
}
}
private fun toggleRequireAccessCode(state: DetailsState, enable: Boolean) {
private fun toggleRequireAccessCode(enable: Boolean) {
scope.launch {
val walletsRepository = store.inject(DaggerGraphState::walletsRepository)
@ -138,11 +128,8 @@ class DetailsMiddleware {
}
if (enable) {
// Remove all biometric sign data
// Remove all saved access codes
removeAllBiometricSingData()
toggleSaveAccessCodes(state, enable = false)
} else {
toggleSaveAccessCodes(state, enable = true)
}
walletsRepository.setRequireAccessCode(value = enable)

View file

@ -111,6 +111,9 @@ private fun handlePrivacyAction(action: DetailsAction.AppSettings, state: Detail
isHidingEnabled = action.state.isHidingEnabled,
selectedAppCurrency = action.state.selectedAppCurrency,
selectedThemeMode = action.state.selectedThemeMode,
useBiometricAuthentication = action.state.useBiometricAuthentication,
requireAccessCode = action.state.requireAccessCode,
hasSecuredWallets = action.state.hasSecuredWallets,
),
)
is DetailsAction.AppSettings.EnrollBiometrics,

View file

@ -22,6 +22,7 @@ data class AppSettingsState(
val requireAccessCode: Boolean = false,
val useBiometricAuthentication: Boolean = false,
val needEnrollBiometrics: Boolean = false,
val hasSecuredWallets: Boolean = false,
val isHidingEnabled: Boolean = false,
val isInProgress: Boolean = false,
val selectedAppCurrency: AppCurrency = AppCurrency.Default,

View file

@ -10,6 +10,7 @@ import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
import com.tangem.domain.apptheme.model.AppThemeMode
import com.tangem.domain.apptheme.repository.AppThemeModeRepository
import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.settings.CanUseBiometryUseCase
import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.domain.wallets.repository.WalletsRepository
@ -48,6 +49,7 @@ internal class AppSettingsModel @Inject constructor(
private val appCurrencyRepository: AppCurrencyRepository,
private val walletsRepository: WalletsRepository,
private val canUseBiometryUseCase: CanUseBiometryUseCase,
private val userWalletsListRepository: UserWalletsListRepository,
private val balanceHidingRepository: BalanceHidingRepository,
private val analyticsEventHandler: AnalyticsEventHandler,
private val appThemeModeRepository: AppThemeModeRepository,
@ -114,7 +116,8 @@ internal class AppSettingsModel @Inject constructor(
)
if (hotWalletFeatureToggles.isHotWalletEnabled) {
val canUseBiometrics = !state.needEnrollBiometrics && !state.isInProgress
val canUseBiometrics =
!state.needEnrollBiometrics && !state.isInProgress && state.hasSecuredWallets
add(
itemsFactory.createUseBiometricsSwitch(
@ -126,7 +129,7 @@ internal class AppSettingsModel @Inject constructor(
add(
itemsFactory.createRequireAccessCodeSwitch(
isChecked = state.requireAccessCode,
isChecked = state.requireAccessCode || !state.useBiometricAuthentication,
isEnabled = canUseBiometrics && state.useBiometricAuthentication,
onCheckedChange = ::onRequireAccessCodeToggled,
),
@ -206,14 +209,12 @@ internal class AppSettingsModel @Inject constructor(
// analyticsEventHandler.send(Settings.AppSettings.BiometricAuthenticationChanged(param))
if (isChecked) {
onSettingsToggled(AppSetting.BiometricAuthentication, enable = true)
onSettingsToggled(AppSetting.RequireAccessCode, enable = true)
} else {
updateContentState {
copy(
dialog = dialogsFactory.createDisableBiometricAuthenticationAlert(
onDisable = {
onSettingsToggled(AppSetting.BiometricAuthentication, enable = false)
onSettingsToggled(AppSetting.RequireAccessCode, enable = true)
dismissDialog()
},
onDismiss = ::dismissDialog,
@ -323,6 +324,7 @@ internal class AppSettingsModel @Inject constructor(
isHidingEnabled = balanceHidingRepository.getBalanceHidingSettings().isHidingEnabledInSettings,
selectedAppCurrency = appCurrencyRepository.getSelectedAppCurrency().firstOrNull() ?: AppCurrency.Default,
selectedThemeMode = appThemeModeRepository.getAppThemeMode().firstOrNull() ?: AppThemeMode.DEFAULT,
hasSecuredWallets = userWalletsListRepository.hasSecuredWallets(),
)
store.dispatchWithMain(DetailsAction.AppSettings.Prepare(state))

View file

@ -123,6 +123,11 @@ interface UserWalletsListRepository {
*/
suspend fun clearPersistentData()
/**
* Checks if there are any secured wallets (wallets that are not locked with [LockMethod.NoLock]).
*/
suspend fun hasSecuredWallets(): Boolean
sealed class LockMethod {
data object Biometric : LockMethod()
class AccessCode(val accessCode: CharArray) : LockMethod()

View file

@ -0,0 +1,10 @@
package com.tangem.domain.wallets.usecase
import com.tangem.domain.common.wallets.UserWalletsListRepository
class HasSecuredWalletsUseCase(private val userWalletsListRepository: UserWalletsListRepository) {
suspend operator fun invoke(): Boolean {
return userWalletsListRepository.hasSecuredWallets()
}
}

View file

@ -81,7 +81,7 @@ internal class AccessCodeModel @Inject constructor(
accessCodeColor = when {
params.accessCodeToConfirm == null -> PinTextColor.Primary
value.length != uiState.value.accessCodeLength -> PinTextColor.Primary
value == params.accessCodeToConfirm -> PinTextColor.Success
value == params.accessCodeToConfirm -> PinTextColor.Primary
else -> PinTextColor.WrongCode
},
)