From b848ba134f770ee5990bd81e1737d310b27747bd Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 2 Feb 2023 18:03:30 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../common/analytics/TangemSdkErrorMapper.kt | 3 + .../userWalletList/UserWalletListError.kt | 24 ----- .../BiometricUserWalletsKeysRepository.kt | 10 +- ...erWalletsSensitiveInformationRepository.kt | 8 -- .../tap/features/walletSelector/ui/Mapper.kt | 39 +------- .../ui/WalletSelectorBottomSheetFragment.kt | 2 + .../ui/WalletSelectorViewModel.kt | 65 ++++++++++++- .../BiometricsLockoutDialogContent.kt | 95 ++++++++++++++++++ .../walletSelector/ui/model/DialogModel.kt | 5 + .../features/welcome/ui/WelcomeFragment.kt | 4 + .../features/welcome/ui/WelcomeScreenState.kt | 2 + .../features/welcome/ui/WelcomeViewModel.kt | 38 +++++++- .../ui/components/BiometricsLockoutDialog.kt | 96 +++++++++++++++++++ .../ui/model/BiometricsLockoutDialog.kt | 6 ++ buildSrc/src/main/java/Versions.kt | 2 +- 15 files changed, 314 insertions(+), 85 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/BiometricsLockoutDialogContent.kt create mode 100644 app/src/main/java/com/tangem/tap/features/welcome/ui/components/BiometricsLockoutDialog.kt create mode 100644 app/src/main/java/com/tangem/tap/features/welcome/ui/model/BiometricsLockoutDialog.kt diff --git a/app/src/main/java/com/tangem/tap/common/analytics/TangemSdkErrorMapper.kt b/app/src/main/java/com/tangem/tap/common/analytics/TangemSdkErrorMapper.kt index 2f9552c1ae..b6fb925fd4 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/TangemSdkErrorMapper.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/TangemSdkErrorMapper.kt @@ -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 } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/UserWalletListError.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/UserWalletListError.kt index 6291f7ae4d..bf42b9c005 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/UserWalletListError.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/UserWalletListError.kt @@ -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}" - } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/BiometricUserWalletsKeysRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/BiometricUserWalletsKeysRepository.kt index 5f72d40ab7..f3f39a647f 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/BiometricUserWalletsKeysRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/BiometricUserWalletsKeysRepository.kt @@ -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> { return withContext(Dispatchers.IO) { getAllInternal() - .mapFailure { error -> - UserWalletListError.ReceiveEncryptionKeysError(error.cause ?: error) - } } } override suspend fun save(encryptionKey: UserWalletEncryptionKey): CompletionResult { 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) } } diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsSensitiveInformationRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsSensitiveInformationRepository.kt index 8d1fdddb9f..3cbf04ac4e 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsSensitiveInformationRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsSensitiveInformationRepository.kt @@ -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): CompletionResult { diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/Mapper.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/Mapper.kt index 5d18c5baa9..626857097f 100644 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/Mapper.kt +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/Mapper.kt @@ -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() - val singleCurrencyWallets = arrayListOf() - 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.toUiModels( +internal fun List.toUiModels( appCurrency: FiatCurrency, ): Sequence { return this.asSequence().map { userWalletModel -> diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorBottomSheetFragment.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorBottomSheetFragment.kt index 30eb4cb1fb..696367fcd0 100644 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorBottomSheetFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorBottomSheetFragment.kt @@ -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 RemoveWalletDialogContent(dialog) is DialogModel.RenameWalletDialog -> RenameWalletDialogContent(dialog) + is DialogModel.BiometricsLockoutDialog -> BiometricsLockoutDialogContent(dialog) } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorViewModel.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorViewModel.kt index 21313550f3..41e5ca44f0 100644 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorViewModel.kt @@ -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 - prevState.updateWithNewState(state) + val walletsUi = state.wallets.toUiModels(state.fiatCurrency) + val walletsIds = walletsUi.map { it.id } + val multiCurrencyWallets = arrayListOf() + val singleCurrencyWallets = arrayListOf() + 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 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( diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/BiometricsLockoutDialogContent.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/BiometricsLockoutDialogContent.kt new file mode 100644 index 0000000000..cb724fe222 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/BiometricsLockoutDialogContent.kt @@ -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 \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/DialogModel.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/DialogModel.kt index 1dcfb1a2a5..309df35752 100644 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/DialogModel.kt +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/DialogModel.kt @@ -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 } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeFragment.kt b/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeFragment.kt index 7cebf15891..0ae230fba6 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeFragment.kt @@ -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() { 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() { ) } + BiometricsLockoutDialog(biometricsLockoutDialog) + LaunchedEffect(key1 = errorMessage) { errorMessage?.let { snackbarHostState.showSnackbar(it) diff --git a/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeScreenState.kt b/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeScreenState.kt index d0d883148a..f9ea592c62 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeScreenState.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeScreenState.kt @@ -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, ) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeViewModel.kt b/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeViewModel.kt index b810503287..042034cfd8 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeViewModel.kt @@ -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 { 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 { 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 } diff --git a/app/src/main/java/com/tangem/tap/features/welcome/ui/components/BiometricsLockoutDialog.kt b/app/src/main/java/com/tangem/tap/features/welcome/ui/components/BiometricsLockoutDialog.kt new file mode 100644 index 0000000000..0a594b864d --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/welcome/ui/components/BiometricsLockoutDialog.kt @@ -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 \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/welcome/ui/model/BiometricsLockoutDialog.kt b/app/src/main/java/com/tangem/tap/features/welcome/ui/model/BiometricsLockoutDialog.kt new file mode 100644 index 0000000000..bb1e4bf3cd --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/welcome/ui/model/BiometricsLockoutDialog.kt @@ -0,0 +1,6 @@ +package com.tangem.tap.features.welcome.ui.model + +internal data class BiometricsLockoutDialog( + val isPermanent: Boolean, + val onDismiss: () -> Unit, +) \ No newline at end of file diff --git a/buildSrc/src/main/java/Versions.kt b/buildSrc/src/main/java/Versions.kt index 2c0370693c..095215a0a2 100644 --- a/buildSrc/src/main/java/Versions.kt +++ b/buildSrc/src/main/java/Versions.kt @@ -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