Updated on 2026-08-14
This commit is contained in:
commit
c82fadbf63
17 changed files with 321 additions and 88 deletions
|
|
@ -114,6 +114,9 @@ object TangemSdkErrorMapper {
|
|||
is TangemSdkError.BackupFailedIncompatibleBatch -> TangemSdkError.BackupFailedIncompatibleBatch()
|
||||
is TangemSdkError.BiometricsUnavailable -> error
|
||||
is TangemSdkError.BiometricsAuthenticationFailed -> error
|
||||
is TangemSdkError.BiometricsAuthenticationLockout -> error
|
||||
is TangemSdkError.BiometricsAuthenticationPermanentLockout -> error
|
||||
is TangemSdkError.UserCanceledBiometricsAuthentication -> error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -13,28 +13,4 @@ sealed class UserWalletListError(code: Int) : TangemError(code) {
|
|||
override var customMessage: String = "This wallet has already been saved, you can add another one"
|
||||
override val messageResId: Int = R.string.user_wallet_list_error_wallet_already_saved
|
||||
}
|
||||
|
||||
class SaveEncryptionKeysError(
|
||||
override val cause: Throwable,
|
||||
) : UserWalletListError(code = 60001) {
|
||||
override var customMessage: String = "Encryption keys could not be saved: ${cause.localizedMessage}"
|
||||
}
|
||||
|
||||
class ReceiveEncryptionKeysError(
|
||||
override val cause: Throwable,
|
||||
) : UserWalletListError(code = 60002) {
|
||||
override var customMessage: String = "Encryption keys could not be received: ${cause.localizedMessage}"
|
||||
}
|
||||
|
||||
class SaveSensitiveInformationError(
|
||||
override val cause: Throwable,
|
||||
) : UserWalletListError(code = 60003) {
|
||||
override var customMessage: String = "Sensitive information could not be saved: ${cause.localizedMessage}"
|
||||
}
|
||||
|
||||
class ReceiveSensitiveInformationError(
|
||||
override val cause: Throwable,
|
||||
) : UserWalletListError(code = 60004) {
|
||||
override var customMessage: String = "Sensitive information could not be received: ${cause.localizedMessage}"
|
||||
}
|
||||
}
|
||||
|
|
@ -10,10 +10,8 @@ import com.tangem.common.core.TangemSdkError
|
|||
import com.tangem.common.doOnFailure
|
||||
import com.tangem.common.fold
|
||||
import com.tangem.common.map
|
||||
import com.tangem.common.mapFailure
|
||||
import com.tangem.common.services.secure.SecureStorage
|
||||
import com.tangem.domain.common.util.UserWalletId
|
||||
import com.tangem.tap.domain.userWalletList.UserWalletListError
|
||||
import com.tangem.tap.domain.userWalletList.model.UserWalletEncryptionKey
|
||||
import com.tangem.tap.domain.userWalletList.repository.UserWalletsKeysRepository
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
|
@ -39,18 +37,12 @@ internal class BiometricUserWalletsKeysRepository(
|
|||
override suspend fun getAll(): CompletionResult<List<UserWalletEncryptionKey>> {
|
||||
return withContext(Dispatchers.IO) {
|
||||
getAllInternal()
|
||||
.mapFailure { error ->
|
||||
UserWalletListError.ReceiveEncryptionKeysError(error.cause ?: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun save(encryptionKey: UserWalletEncryptionKey): CompletionResult<Unit> {
|
||||
return withContext(Dispatchers.IO) {
|
||||
storeEncryptionKey(encryptionKey)
|
||||
.mapFailure { error ->
|
||||
UserWalletListError.SaveEncryptionKeysError(error.cause ?: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -91,7 +83,7 @@ internal class BiometricUserWalletsKeysRepository(
|
|||
getEncryptionKey(userWalletId)
|
||||
.doOnFailure { error ->
|
||||
// If the user cancels biometric authentication, cancel the request for all keys
|
||||
if (error is TangemSdkError.BiometricsAuthenticationFailed) {
|
||||
if (error is TangemSdkError.UserCanceledBiometricsAuthentication) {
|
||||
return CompletionResult.Failure(error)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,10 @@ import com.squareup.moshi.Moshi
|
|||
import com.squareup.moshi.Types
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.catching
|
||||
import com.tangem.common.mapFailure
|
||||
import com.tangem.common.services.secure.SecureStorage
|
||||
import com.tangem.domain.common.util.UserWalletId
|
||||
import com.tangem.tap.common.extensions.filterNotNull
|
||||
import com.tangem.tap.domain.model.UserWallet
|
||||
import com.tangem.tap.domain.userWalletList.UserWalletListError
|
||||
import com.tangem.tap.domain.userWalletList.model.UserWalletEncryptionKey
|
||||
import com.tangem.tap.domain.userWalletList.model.UserWalletSensitiveInformation
|
||||
import com.tangem.tap.domain.userWalletList.repository.UserWalletsSensitiveInformationRepository
|
||||
|
|
@ -48,9 +46,6 @@ internal class DefaultUserWalletsSensitiveInformationRepository(
|
|||
.apply { set(userWallet.walletId.stringValue, encryptedSensitiveInformation) }
|
||||
.let { saveInternal(it) }
|
||||
}
|
||||
.mapFailure { error ->
|
||||
UserWalletListError.SaveSensitiveInformationError(error.cause ?: error)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getAll(
|
||||
|
|
@ -72,9 +67,6 @@ internal class DefaultUserWalletsSensitiveInformationRepository(
|
|||
}
|
||||
.filterNotNull()
|
||||
}
|
||||
.mapFailure { error ->
|
||||
UserWalletListError.ReceiveSensitiveInformationError(error.cause ?: error)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun delete(userWalletsIds: List<UserWalletId>): CompletionResult<Unit> {
|
||||
|
|
|
|||
|
|
@ -75,8 +75,11 @@ data class WalletState(
|
|||
get() = walletsStores.mapNotNull { it.walletManager }
|
||||
|
||||
private val primaryWalletStore: WalletStore?
|
||||
get() = if (isMultiwalletAllowed || walletsStores.isEmpty() || walletsStores.size > 1) null
|
||||
else walletsStores[0]
|
||||
get() = if (isMultiwalletAllowed || walletsStores.isEmpty() || walletsStores.size > 1) {
|
||||
null
|
||||
} else {
|
||||
walletsStores[0]
|
||||
}
|
||||
|
||||
private val primaryWalletManager: WalletManager?
|
||||
get() = primaryWalletStore?.walletManager
|
||||
|
|
|
|||
|
|
@ -131,7 +131,8 @@ class OnWalletLoadedReducer {
|
|||
}
|
||||
val walletData = walletState.primaryWalletData?.copy(
|
||||
currencyData = BalanceWidgetData(
|
||||
balanceStatus, wallet.blockchain.fullName,
|
||||
status = balanceStatus,
|
||||
currency = wallet.blockchain.fullName,
|
||||
currencySymbol = wallet.blockchain.currency,
|
||||
blockchainAmount = amount,
|
||||
amount = amount,
|
||||
|
|
|
|||
|
|
@ -3,50 +3,13 @@ package com.tangem.tap.features.walletSelector.ui
|
|||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import com.tangem.tap.common.extensions.toFormattedFiatValue
|
||||
import com.tangem.tap.domain.model.TotalFiatBalance
|
||||
import com.tangem.tap.features.details.ui.cardsettings.TextReference
|
||||
import com.tangem.tap.features.walletSelector.redux.UserWalletModel
|
||||
import com.tangem.tap.features.walletSelector.redux.WalletSelectorState
|
||||
import com.tangem.tap.features.walletSelector.ui.model.MultiCurrencyUserWalletItem
|
||||
import com.tangem.tap.features.walletSelector.ui.model.SingleCurrencyUserWalletItem
|
||||
import com.tangem.tap.features.walletSelector.ui.model.UserWalletItem
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal fun WalletSelectorScreenState.updateWithNewState(
|
||||
newState: WalletSelectorState,
|
||||
): WalletSelectorScreenState {
|
||||
val walletsUi = newState.wallets.toUiModels(newState.fiatCurrency)
|
||||
val walletsIds = walletsUi.map { it.id }
|
||||
val multiCurrencyWallets = arrayListOf<MultiCurrencyUserWalletItem>()
|
||||
val singleCurrencyWallets = arrayListOf<SingleCurrencyUserWalletItem>()
|
||||
walletsUi.forEach { wallet ->
|
||||
when (wallet) {
|
||||
is MultiCurrencyUserWalletItem -> {
|
||||
multiCurrencyWallets.add(wallet)
|
||||
}
|
||||
is SingleCurrencyUserWalletItem -> {
|
||||
singleCurrencyWallets.add(wallet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.copy(
|
||||
multiCurrencyWallets = multiCurrencyWallets,
|
||||
singleCurrencyWallets = singleCurrencyWallets,
|
||||
selectedUserWalletId = newState.selectedWalletId,
|
||||
editingUserWalletsIds = editingUserWalletsIds.filter { it in walletsIds },
|
||||
isLocked = newState.isLocked,
|
||||
showUnlockProgress = newState.isUnlockInProgress,
|
||||
showAddCardProgress = newState.isCardSavingInProgress,
|
||||
error = newState.error
|
||||
?.takeUnless { it.silent }
|
||||
?.let { error ->
|
||||
error.messageResId?.let { TextReference.Res(it) }
|
||||
?: TextReference.Str(error.customMessage)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun List<UserWalletModel>.toUiModels(
|
||||
internal fun List<UserWalletModel>.toUiModels(
|
||||
appCurrency: FiatCurrency,
|
||||
): Sequence<UserWalletItem> {
|
||||
return this.asSequence().map { userWalletModel ->
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import com.tangem.core.ui.fragments.ComposeBottomSheetFragment
|
|||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.tap.common.analytics.events.MyWallets
|
||||
import com.tangem.tap.features.details.ui.cardsettings.resolveReference
|
||||
import com.tangem.tap.features.walletSelector.ui.components.BiometricsLockoutDialogContent
|
||||
import com.tangem.tap.features.walletSelector.ui.components.RemoveWalletDialogContent
|
||||
import com.tangem.tap.features.walletSelector.ui.components.RenameWalletDialogContent
|
||||
import com.tangem.tap.features.walletSelector.ui.components.WalletSelectorScreenContent
|
||||
|
|
@ -89,6 +90,7 @@ internal class WalletSelectorBottomSheetFragment : ComposeBottomSheetFragment<Wa
|
|||
when (dialog) {
|
||||
is DialogModel.RemoveWalletDialog -> RemoveWalletDialogContent(dialog)
|
||||
is DialogModel.RenameWalletDialog -> RenameWalletDialogContent(dialog)
|
||||
is DialogModel.BiometricsLockoutDialog -> BiometricsLockoutDialogContent(dialog)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,13 +2,18 @@ package com.tangem.tap.features.walletSelector.ui
|
|||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.domain.common.util.UserWalletId
|
||||
import com.tangem.tap.common.analytics.events.MyWallets
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.features.details.ui.cardsettings.TextReference
|
||||
import com.tangem.tap.features.walletSelector.redux.WalletSelectorAction
|
||||
import com.tangem.tap.features.walletSelector.redux.WalletSelectorState
|
||||
import com.tangem.tap.features.walletSelector.ui.model.DialogModel
|
||||
import com.tangem.tap.features.walletSelector.ui.model.MultiCurrencyUserWalletItem
|
||||
import com.tangem.tap.features.walletSelector.ui.model.SingleCurrencyUserWalletItem
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.userWalletsListManager
|
||||
import com.tangem.tap.walletStoresManager
|
||||
|
|
@ -136,9 +141,41 @@ internal class WalletSelectorViewModel : ViewModel(), StoreSubscriber<WalletSele
|
|||
store.dispatch(WalletSelectorAction.CloseError)
|
||||
}
|
||||
|
||||
// TODO: Refactor errors handling
|
||||
override fun newState(state: WalletSelectorState) {
|
||||
stateInternal.update { prevState ->
|
||||
prevState.updateWithNewState(state)
|
||||
val walletsUi = state.wallets.toUiModels(state.fiatCurrency)
|
||||
val walletsIds = walletsUi.map { it.id }
|
||||
val multiCurrencyWallets = arrayListOf<MultiCurrencyUserWalletItem>()
|
||||
val singleCurrencyWallets = arrayListOf<SingleCurrencyUserWalletItem>()
|
||||
walletsUi.forEach { wallet ->
|
||||
when (wallet) {
|
||||
is MultiCurrencyUserWalletItem -> {
|
||||
multiCurrencyWallets.add(wallet)
|
||||
}
|
||||
is SingleCurrencyUserWalletItem -> {
|
||||
singleCurrencyWallets.add(wallet)
|
||||
}
|
||||
}
|
||||
}
|
||||
val biometricsLockoutDialog = createBiometricsLockoutDialogIfNeeded(state.error, prevState.dialog)
|
||||
|
||||
prevState.copy(
|
||||
multiCurrencyWallets = multiCurrencyWallets,
|
||||
singleCurrencyWallets = singleCurrencyWallets,
|
||||
selectedUserWalletId = state.selectedWalletId,
|
||||
editingUserWalletsIds = prevState.editingUserWalletsIds.filter { it in walletsIds },
|
||||
isLocked = state.isLocked,
|
||||
showUnlockProgress = state.isUnlockInProgress,
|
||||
showAddCardProgress = state.isCardSavingInProgress,
|
||||
dialog = biometricsLockoutDialog,
|
||||
error = state.error
|
||||
?.takeIf { !it.silent && biometricsLockoutDialog == null }
|
||||
?.let { error ->
|
||||
error.messageResId?.let { TextReference.Res(it) }
|
||||
?: TextReference.Str(error.customMessage)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -146,6 +183,32 @@ internal class WalletSelectorViewModel : ViewModel(), StoreSubscriber<WalletSele
|
|||
store.unsubscribe(this)
|
||||
}
|
||||
|
||||
private fun createBiometricsLockoutDialogIfNeeded(
|
||||
error: TangemError?,
|
||||
currentDialogModel: DialogModel?,
|
||||
): DialogModel? {
|
||||
return when (error) {
|
||||
is TangemSdkError.BiometricsAuthenticationLockout -> DialogModel.BiometricsLockoutDialog(
|
||||
isPermanent = false,
|
||||
onDismiss = this::dismissDialog,
|
||||
)
|
||||
is TangemSdkError.BiometricsAuthenticationPermanentLockout -> DialogModel.BiometricsLockoutDialog(
|
||||
isPermanent = true,
|
||||
onDismiss = this::dismissDialog,
|
||||
)
|
||||
else -> currentDialogModel
|
||||
}
|
||||
}
|
||||
|
||||
private fun dismissDialog() {
|
||||
stateInternal.update { prevState ->
|
||||
prevState.copy(
|
||||
dialog = null,
|
||||
)
|
||||
}
|
||||
closeError()
|
||||
}
|
||||
|
||||
private fun editWallet(userWalletId: UserWalletId) {
|
||||
stateInternal.update { prevState ->
|
||||
prevState.copy(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
package com.tangem.tap.features.walletSelector.ui.components
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.BasicDialog
|
||||
import com.tangem.core.ui.components.DialogButton
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.tap.features.walletSelector.ui.model.DialogModel
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
internal fun BiometricsLockoutDialogContent(
|
||||
dialog: DialogModel.BiometricsLockoutDialog,
|
||||
) {
|
||||
BasicDialog(
|
||||
title = stringResource(id = R.string.biometric_lockout_warning_title),
|
||||
message = stringResource(
|
||||
id = if (dialog.isPermanent) {
|
||||
R.string.biometric_lockout_permanent_warning_description
|
||||
} else {
|
||||
R.string.biometric_lockout_warning_description
|
||||
},
|
||||
),
|
||||
onDismissDialog = dialog.onDismiss,
|
||||
confirmButton = DialogButton(
|
||||
title = stringResource(id = R.string.common_ok),
|
||||
onClick = dialog.onDismiss,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
private fun BiometricsLockoutDialogSample(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
BiometricsLockoutDialogContent(
|
||||
dialog = DialogModel.BiometricsLockoutDialog(
|
||||
isPermanent = false,
|
||||
onDismiss = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun BiometricsLockoutDialogPreview_Light() {
|
||||
TangemTheme {
|
||||
BiometricsLockoutDialogSample()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun BiometricsLockoutDialogPreview_Dark() {
|
||||
TangemTheme(isDark = true) {
|
||||
BiometricsLockoutDialogSample()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BiometricsLockoutDialog_Permanent_Sample(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
BiometricsLockoutDialogContent(
|
||||
dialog = DialogModel.BiometricsLockoutDialog(
|
||||
isPermanent = true,
|
||||
onDismiss = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun BiometricsLockoutDialog_Permanent_Preview_Light() {
|
||||
TangemTheme {
|
||||
BiometricsLockoutDialog_Permanent_Sample()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun BiometricsLockoutDialog_Permanent_Preview_Dark() {
|
||||
TangemTheme(isDark = true) {
|
||||
BiometricsLockoutDialog_Permanent_Sample()
|
||||
}
|
||||
}
|
||||
// endregion Preview
|
||||
|
|
@ -11,4 +11,9 @@ internal sealed interface DialogModel {
|
|||
val onConfirm: () -> Unit,
|
||||
val onDismiss: () -> Unit,
|
||||
) : DialogModel
|
||||
|
||||
data class BiometricsLockoutDialog(
|
||||
val isPermanent: Boolean,
|
||||
val onDismiss: () -> Unit,
|
||||
) : DialogModel
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ import com.tangem.core.ui.fragments.ComposeFragment
|
|||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.tap.common.analytics.events.SignIn
|
||||
import com.tangem.tap.features.details.ui.cardsettings.resolveReference
|
||||
import com.tangem.tap.features.welcome.ui.components.BiometricsLockoutDialog
|
||||
import com.tangem.tap.features.welcome.ui.components.WelcomeScreenContent
|
||||
import com.tangem.wallet.R
|
||||
|
||||
|
|
@ -45,6 +46,7 @@ internal class WelcomeFragment : ComposeFragment<WelcomeScreenState>() {
|
|||
override fun ScreenContent(state: WelcomeScreenState, modifier: Modifier) {
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
val errorMessage by rememberUpdatedState(newValue = state.error?.resolveReference())
|
||||
val biometricsLockoutDialog by rememberUpdatedState(newValue = state.biometricsLockoutDialog)
|
||||
|
||||
val backgroundColor = colorResource(id = R.color.background_primary)
|
||||
SystemBarsEffect {
|
||||
|
|
@ -75,6 +77,8 @@ internal class WelcomeFragment : ComposeFragment<WelcomeScreenState>() {
|
|||
)
|
||||
}
|
||||
|
||||
BiometricsLockoutDialog(biometricsLockoutDialog)
|
||||
|
||||
LaunchedEffect(key1 = errorMessage) {
|
||||
errorMessage?.let {
|
||||
snackbarHostState.showSnackbar(it)
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@ package com.tangem.tap.features.welcome.ui
|
|||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.tap.features.details.ui.cardsettings.TextReference
|
||||
import com.tangem.tap.features.welcome.ui.model.BiometricsLockoutDialog
|
||||
|
||||
@Immutable
|
||||
internal data class WelcomeScreenState(
|
||||
val showUnlockWithBiometricsProgress: Boolean = false,
|
||||
val showUnlockWithCardProgress: Boolean = false,
|
||||
val biometricsLockoutDialog: BiometricsLockoutDialog? = null,
|
||||
val error: TextReference? = null,
|
||||
)
|
||||
|
|
@ -1,12 +1,15 @@
|
|||
package com.tangem.tap.features.welcome.ui
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.tap.common.analytics.events.SignIn
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.features.details.ui.cardsettings.TextReference
|
||||
import com.tangem.tap.features.welcome.redux.WelcomeAction
|
||||
import com.tangem.tap.features.welcome.redux.WelcomeState
|
||||
import com.tangem.tap.features.welcome.ui.model.BiometricsLockoutDialog
|
||||
import com.tangem.tap.store
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
|
@ -36,16 +39,20 @@ internal class WelcomeViewModel : ViewModel(), StoreSubscriber<WelcomeState> {
|
|||
store.dispatch(WelcomeAction.CloseError)
|
||||
}
|
||||
|
||||
// TODO: Refactor errors handling
|
||||
override fun newState(state: WelcomeState) {
|
||||
val biometricsLockoutDialog = createBiometricLockoutDialogIfNeeded(state.error)
|
||||
|
||||
stateInternal.update { prevState ->
|
||||
prevState.copy(
|
||||
showUnlockWithBiometricsProgress = state.isUnlockWithBiometricsInProgress,
|
||||
showUnlockWithCardProgress = state.isUnlockWithCardInProgress,
|
||||
biometricsLockoutDialog = biometricsLockoutDialog,
|
||||
error = state.error
|
||||
?.takeUnless { it.silent }
|
||||
?.let { error ->
|
||||
error.messageResId?.let { TextReference.Res(it) }
|
||||
?: TextReference.Str(error.customMessage)
|
||||
?.takeIf { !it.silent && biometricsLockoutDialog == null }
|
||||
?.let { e ->
|
||||
e.messageResId?.let { TextReference.Res(it) }
|
||||
?: TextReference.Str(e.customMessage)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -55,6 +62,29 @@ internal class WelcomeViewModel : ViewModel(), StoreSubscriber<WelcomeState> {
|
|||
store.unsubscribe(this)
|
||||
}
|
||||
|
||||
private fun createBiometricLockoutDialogIfNeeded(error: TangemError?): BiometricsLockoutDialog? {
|
||||
return when (error) {
|
||||
is TangemSdkError.BiometricsAuthenticationLockout -> BiometricsLockoutDialog(
|
||||
isPermanent = false,
|
||||
onDismiss = this::dismissDialog,
|
||||
)
|
||||
is TangemSdkError.BiometricsAuthenticationPermanentLockout -> BiometricsLockoutDialog(
|
||||
isPermanent = true,
|
||||
onDismiss = this::dismissDialog,
|
||||
)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun dismissDialog() {
|
||||
stateInternal.update { prevState ->
|
||||
prevState.copy(
|
||||
biometricsLockoutDialog = null,
|
||||
)
|
||||
}
|
||||
closeError()
|
||||
}
|
||||
|
||||
private fun subscribeToStoreChanges() {
|
||||
store.subscribe(this) { appState ->
|
||||
appState.skip { old, new -> old.welcomeState == new.welcomeState }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
package com.tangem.tap.features.welcome.ui.components
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.BasicDialog
|
||||
import com.tangem.core.ui.components.DialogButton
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.tap.features.welcome.ui.model.BiometricsLockoutDialog
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
internal fun BiometricsLockoutDialog(
|
||||
dialog: BiometricsLockoutDialog?,
|
||||
) {
|
||||
if (dialog == null) return
|
||||
BasicDialog(
|
||||
title = stringResource(id = R.string.biometric_lockout_warning_title),
|
||||
message = stringResource(
|
||||
id = if (dialog.isPermanent) {
|
||||
R.string.biometric_lockout_permanent_warning_description
|
||||
} else {
|
||||
R.string.biometric_lockout_warning_description
|
||||
},
|
||||
),
|
||||
onDismissDialog = dialog.onDismiss,
|
||||
confirmButton = DialogButton(
|
||||
title = stringResource(id = R.string.common_ok),
|
||||
onClick = dialog.onDismiss,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
private fun BiometricsLockoutDialogSample(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
BiometricsLockoutDialog(
|
||||
dialog = BiometricsLockoutDialog(
|
||||
isPermanent = false,
|
||||
onDismiss = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun BiometricsLockoutDialogPreview_Light() {
|
||||
TangemTheme {
|
||||
BiometricsLockoutDialogSample()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun BiometricsLockoutDialogPreview_Dark() {
|
||||
TangemTheme(isDark = true) {
|
||||
BiometricsLockoutDialogSample()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BiometricsLockoutDialog_Permanent_Sample(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
BiometricsLockoutDialog(
|
||||
dialog = BiometricsLockoutDialog(
|
||||
isPermanent = true,
|
||||
onDismiss = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun BiometricsLockoutDialog_Permanent_Preview_Light() {
|
||||
TangemTheme {
|
||||
BiometricsLockoutDialog_Permanent_Sample()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun BiometricsLockoutDialog_Permanent_Preview_Dark() {
|
||||
TangemTheme(isDark = true) {
|
||||
BiometricsLockoutDialog_Permanent_Sample()
|
||||
}
|
||||
}
|
||||
// endregion Preview
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.tap.features.welcome.ui.model
|
||||
|
||||
internal data class BiometricsLockoutDialog(
|
||||
val isPermanent: Boolean,
|
||||
val onDismiss: () -> Unit,
|
||||
)
|
||||
|
|
@ -64,7 +64,7 @@ object Versions {
|
|||
const val tangemBlockchainSdk = "develop-155"
|
||||
// const val tangemBlockchainSdk = "0.0.1" // Keep it! - used for local builds
|
||||
|
||||
const val tangemCardSdk = "develop-190"
|
||||
const val tangemCardSdk = "develop-191"
|
||||
// const val tangemCardSgk = "0.0.1" // Keep it! - used for local builds
|
||||
// endregion Tangem
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue