From 94a44864437c8400b83d969cd5f4dd02176d3140 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 5 Sep 2025 13:19:12 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/di/domain/SettingsDomainModule.kt | 10 +- .../di/UserWalletsListManagerModule.kt | 3 + .../DefaultUserWalletsListRepository.kt | 108 +++++++++------- .../utils/UserWalletEncyptionKeyCalculator.kt | 4 + .../converter/UserWalletItemUMConverter.kt | 8 +- .../local/preferences/PreferencesKeys.kt | 6 +- core/res/src/main/res/values-de/strings.xml | 4 +- core/res/src/main/res/values-es/strings.xml | 119 +++++++++--------- core/res/src/main/res/values-fr/strings.xml | 18 ++- core/res/src/main/res/values-ru/strings.xml | 13 +- .../src/main/res/values-uk-rUA/strings.xml | 12 +- core/res/src/main/res/values/strings.xml | 93 +++++++++++++- .../settings/DefaultSettingsRepository.kt | 8 +- ...eCase.kt => SetAskBiometryShownUseCase.kt} | 4 +- ...ase.kt => ShouldShowAskBiometryUseCase.kt} | 4 +- .../repositories/SettingsRepository.kt | 4 +- .../biometry/impl/model/AskBiometryModel.kt | 6 +- .../details/model/UserWalletListModel.kt | 2 +- .../hotwallet/accesscode/AccessCodeModel.kt | 61 +++++++++ .../entry/impl/model/OnboardingEntryModel.kt | 2 +- .../model/MultiWalletFinalizeModel.kt | 14 ++- .../wallet/utils/UserWalletsFetcher.kt | 2 +- .../wallet/child/wallet/model/WalletModel.kt | 19 ++- .../model/WalletsUpdateActionResolver.kt | 14 +-- .../MultiWalletWarningsSubscriber.kt | 6 + .../wallet/utils/DefaultUserWalletsFetcher.kt | 23 +++- .../connections/utils/WcUserWalletsFetcher.kt | 2 +- .../welcome/impl/model/WelcomeModel.kt | 2 +- 28 files changed, 393 insertions(+), 178 deletions(-) rename domain/settings/src/main/java/com/tangem/domain/settings/{SetSaveWalletScreenShownUseCase.kt => SetAskBiometryShownUseCase.kt} (71%) rename domain/settings/src/main/java/com/tangem/domain/settings/{ShouldShowSaveWalletScreenUseCase.kt => ShouldShowAskBiometryUseCase.kt} (57%) diff --git a/app/src/main/java/com/tangem/tap/di/domain/SettingsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/SettingsDomainModule.kt index df25dd1255..2cc898457b 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/SettingsDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/SettingsDomainModule.kt @@ -54,8 +54,8 @@ internal object SettingsDomainModule { @Singleton fun providesShouldShowSaveWalletScreenUseCase( settingsRepository: SettingsRepository, - ): ShouldShowSaveWalletScreenUseCase { - return ShouldShowSaveWalletScreenUseCase(settingsRepository = settingsRepository) + ): ShouldShowAskBiometryUseCase { + return ShouldShowAskBiometryUseCase(settingsRepository = settingsRepository) } @Provides @@ -138,10 +138,8 @@ internal object SettingsDomainModule { @Provides @Singleton - fun provideSetSaveWalletScreenShownUseCase( - settingsRepository: SettingsRepository, - ): SetSaveWalletScreenShownUseCase { - return SetSaveWalletScreenShownUseCase(settingsRepository = settingsRepository) + fun provideSetSaveWalletScreenShownUseCase(settingsRepository: SettingsRepository): SetAskBiometryShownUseCase { + return SetAskBiometryShownUseCase(settingsRepository = settingsRepository) } @Provides diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerModule.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerModule.kt index c521709443..f582dea772 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerModule.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerModule.kt @@ -8,6 +8,7 @@ import com.tangem.common.json.TangemSdkAdapter import com.tangem.common.services.secure.SecureStorage import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.datasource.local.preferences.AppPreferencesStore +import com.tangem.domain.card.ScanCardProcessor import com.tangem.domain.models.scan.serialization.* import com.tangem.domain.visa.model.VisaActivationRemoteState import com.tangem.domain.visa.model.VisaCardActivationStatus @@ -122,6 +123,7 @@ internal object UserWalletsListManagerModule { passwordRequester: HotWalletPasswordRequester, appPreferencesStore: AppPreferencesStore, hotWalletAccessCodeAttemptsRepository: HotWalletAccessCodeAttemptsRepository, + scanCardProcessor: ScanCardProcessor, ): UserWalletsListRepository { val moshi = buildMoshi() val secureStorage = buildSecureStorage(applicationContext = applicationContext) @@ -168,6 +170,7 @@ internal object UserWalletsListManagerModule { appPreferencesStore = appPreferencesStore, savePersistentInformation = ProviderSuspend { true }, // Always save persistent information for now hotWalletAccessCodeAttemptsRepository = hotWalletAccessCodeAttemptsRepository, + scanCardProcessor = scanCardProcessor, ) } diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DefaultUserWalletsListRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DefaultUserWalletsListRepository.kt index 87edec2202..c6204135b7 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DefaultUserWalletsListRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DefaultUserWalletsListRepository.kt @@ -11,6 +11,7 @@ import com.tangem.common.map import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.PreferencesKeys import com.tangem.datasource.local.preferences.utils.getSyncOrDefault +import com.tangem.domain.card.ScanCardProcessor import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.isLocked @@ -38,6 +39,9 @@ import com.tangem.utils.ProviderSuspend import com.tangem.utils.extensions.indexOfFirstOrNull import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update +import com.tangem.core.analytics.models.AnalyticsParam +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock @Suppress("LongParameterList", "LargeClass") internal class DefaultUserWalletsListRepository( @@ -50,36 +54,45 @@ internal class DefaultUserWalletsListRepository( private val savePersistentInformation: ProviderSuspend, private val appPreferencesStore: AppPreferencesStore, private val hotWalletAccessCodeAttemptsRepository: HotWalletAccessCodeAttemptsRepository, + private val scanCardProcessor: ScanCardProcessor, ) : UserWalletsListRepository { override val userWallets = MutableStateFlow?>(null) override val selectedUserWallet = MutableStateFlow(null) + private val mutex = Mutex() override suspend fun load() { - if (userWallets.value != null) return + mutex.withLock { + if (userWallets.value != null) return - if (savePersistentInformation().not()) { - // If we don't save persistent information, we don't need to load user wallets - // and we should clear any existing data - clearPersistentData() - updateWallets { emptyList() } - return - } - - val unsecuredEncryptionKeys = userWalletEncryptionKeysRepository.getAllUnsecured() - - publicInformationRepository.getAll() - .map { it.toUserWallets() } - .flatMap { wallets -> - sensitiveInformationRepository.getAll(unsecuredEncryptionKeys) - .map { wallets.updateWith(it) } - }.doOnSuccess { - userWallets.value = it + if (savePersistentInformation().not()) { + // If we don't save persistent information, we don't need to load user wallets + // and we should clear any existing data + clearPersistentData() + updateWallets { emptyList() } + return } - val selectedUserWalletId = selectedUserWalletRepository.get() - selectedUserWallet.value = userWallets.value?.firstOrNull { it.walletId == selectedUserWalletId } - ?: userWallets.value?.firstOrNull() + val unsecuredEncryptionKeys = userWalletEncryptionKeysRepository.getAllUnsecured() + + publicInformationRepository.getAll() + .map { it.toUserWallets() } + .flatMap { wallets -> + sensitiveInformationRepository.getAll(unsecuredEncryptionKeys) + .map { wallets.updateWith(it) } + } + .doOnSuccess { loadedWallets -> + userWallets.update { toUpdate -> + val selectedUserWalletId = selectedUserWalletRepository.get() + selectedUserWallet.value = loadedWallets.firstOrNull { it.walletId == selectedUserWalletId } + ?: loadedWallets.firstOrNull()?.also { + selectedUserWalletRepository.set(it.walletId) + } + + loadedWallets + } + } + } } override suspend fun userWalletsSync(): List { @@ -191,18 +204,17 @@ internal class DefaultUserWalletsListRepository( userWalletEncryptionKeysRepository.delete(userWalletIds) - val userWalletsBeforeDelete = userWallets.value ?: return@either - userWallets.update { currentWallets -> - currentWallets?.filterNot { it.walletId in userWalletIds } - } - - selectedUserWallet.update { currentSelected -> - if (currentSelected == null) return@update null - - userWallets.value?.findAvailableUserWallet( - userWalletsBeforeDelete.indexOfFirstOrNull { it.walletId == currentSelected.walletId } ?: 0, - ) + val updatedWallets = currentWallets?.filter { userWalletIds.contains(it.walletId).not() } + selectedUserWallet.update { currentSelected -> + if (currentSelected == null) return@update null + val newSelected = updatedWallets?.findAvailableUserWallet( + currentWallets.indexOfFirstOrNull { it.walletId == currentSelected.walletId } ?: 0, + ) + selectedUserWalletRepository.set(newSelected?.walletId) + newSelected + } + updatedWallets } } @@ -258,7 +270,7 @@ internal class DefaultUserWalletsListRepository( raise(UnlockWalletError.UnableToUnlock) } - tangemSdkManagerProvider().scanProduct() + scanCardProcessor.scan(analyticsSource = AnalyticsParam.ScreensSources.SignIn) .doOnSuccess { scanResponse -> val expectedId = UserWalletIdBuilder.scanResponse(scanResponse).build() @@ -266,9 +278,14 @@ internal class DefaultUserWalletsListRepository( raise(UnlockWalletError.ScannedCardWalletNotMatched) } - saveWithoutLock(userWallet.copy(scanResponse = scanResponse), canOverride = true) - .mapLeft { UnlockWalletError.UnableToUnlock } - .bind() + val encryptionKey = UserWalletEncryptionKey( + walletId = userWallet.walletId, + encryptionKey = scanResponse.encryptionKey ?: raise(UnlockWalletError.UnableToUnlock), + ) + + sensitiveInformationRepository.getAll(listOf(encryptionKey)) + .doOnSuccess { sensitiveInfo -> updateWallets { it?.updateWith(sensitiveInfo) } } + .doOnFailure { error -> raise(UnlockWalletError.UnableToUnlock) } } .doOnFailure { raise(UnlockWalletError.UserCancelled) @@ -278,7 +295,8 @@ internal class DefaultUserWalletsListRepository( } override suspend fun unlockAllWallets(): Either = either { - val userWalletIds = userWalletsSync().map { it.walletId }.toSet() + val userWallets = userWalletsSync() + val userWalletIds = userWallets.map { it.walletId }.toSet() val biometricKeys = runCatching { userWalletEncryptionKeysRepository.getAllBiometric() }.getOrElse { @@ -291,7 +309,7 @@ internal class DefaultUserWalletsListRepository( val unlockedWalletsIds = allKeys.map { it.walletId } val unlockedWallets = unlockedWalletsIds.mapNotNull { id -> - userWalletsSync().firstOrNull { it.walletId == id } + userWallets.firstOrNull { it.walletId == id } } // Remove all password attempts for unlocked hot wallets @@ -306,7 +324,7 @@ internal class DefaultUserWalletsListRepository( sensitiveInformationRepository.getAll(allKeys) .doOnSuccess { sensitiveInfo -> - updateWallets { it?.updateWith(sensitiveInfo) } + updateWallets { userWallets.updateWith(sensitiveInfo) } } .doOnFailure { raise(UnlockWalletError.UnableToUnlock) } } @@ -390,11 +408,13 @@ internal class DefaultUserWalletsListRepository( } private fun updateWallets(block: (List?) -> List?) { - userWallets.update(block) - - selectedUserWallet.update { currentSelected -> - if (currentSelected == null) return@update null - userWallets.value?.find { it.walletId == currentSelected.walletId } + userWallets.update { + val updated = block(it) + selectedUserWallet.update { currentSelected -> + if (currentSelected == null) return@update null + updated?.find { it.walletId == currentSelected.walletId } + } + updated } } diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/utils/UserWalletEncyptionKeyCalculator.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/utils/UserWalletEncyptionKeyCalculator.kt index 8b05c741e7..65b8e925a7 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/utils/UserWalletEncyptionKeyCalculator.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/utils/UserWalletEncyptionKeyCalculator.kt @@ -4,6 +4,7 @@ import com.tangem.common.extensions.calculateSha256 import com.tangem.domain.common.extensions.calculateHmacSha256 import com.tangem.domain.models.MobileWallet import com.tangem.domain.models.scan.CardDTO +import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.wallet.UserWallet internal val UserWallet.encryptionKey: ByteArray? @@ -19,6 +20,9 @@ private fun calculateEncryptionKey(publicKey: ByteArray): ByteArray { return message.calculateHmacSha256(keyHash) } +val ScanResponse.encryptionKey: ByteArray? + get() = findPublicKey(this.card.wallets)?.let { calculateEncryptionKey(it) } + private fun findPublicKey(wallets: List): ByteArray? { return wallets.firstOrNull()?.publicKey } diff --git a/common/ui/src/main/java/com/tangem/common/ui/userwallet/converter/UserWalletItemUMConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/userwallet/converter/UserWalletItemUMConverter.kt index aec881c569..a079675da1 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/userwallet/converter/UserWalletItemUMConverter.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/userwallet/converter/UserWalletItemUMConverter.kt @@ -34,7 +34,7 @@ class UserWalletItemUMConverter( private val appCurrency: AppCurrency? = null, private val balance: TotalFiatBalance? = null, private val isBalanceHidden: Boolean = false, - private val authMode: Boolean = false, + private val isAuthMode: Boolean = false, private val endIcon: UserWalletItemUM.EndIcon = UserWalletItemUM.EndIcon.None, artwork: UserWalletItemUM.ImageState? = null, ) : Converter { @@ -58,11 +58,11 @@ class UserWalletItemUMConverter( } private fun isEnabled(userWallet: UserWallet): Boolean { - return authMode || userWallet.isLocked.not() + return isAuthMode || userWallet.isLocked.not() } private fun getLabelOrNull(userWallet: UserWallet): LabelUM? { - return if (authMode.not() && userWallet is UserWallet.Hot && !userWallet.backedUp) { + return if (isAuthMode.not() && userWallet is UserWallet.Hot && !userWallet.backedUp) { LabelUM( text = resourceReference(R.string.hw_backup_no_backup), style = LabelStyle.WARNING, @@ -92,7 +92,7 @@ class UserWalletItemUMConverter( private fun getBalanceInfo(userWallet: UserWallet): UserWalletItemUM.Balance { return when { userWallet.isLocked -> UserWalletItemUM.Balance.Locked - authMode -> UserWalletItemUM.Balance.NotShowing + isAuthMode -> UserWalletItemUM.Balance.NotShowing isBalanceHidden -> UserWalletItemUM.Balance.Hidden balance == null -> UserWalletItemUM.Balance.Loading else -> { diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt index 8644fb72c8..63fbdfebf8 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt @@ -7,7 +7,7 @@ import com.tangem.datasource.local.preferences.PreferencesKeys.IS_TANGEM_TOS_ACC import com.tangem.datasource.local.preferences.PreferencesKeys.SAVE_USER_WALLETS_KEY import com.tangem.datasource.local.preferences.PreferencesKeys.SHOULD_OPEN_WELCOME_ON_RESUME_KEY import com.tangem.datasource.local.preferences.PreferencesKeys.SHOULD_SAVE_ACCESS_CODES_KEY -import com.tangem.datasource.local.preferences.PreferencesKeys.SHOULD_SHOW_SAVE_USER_WALLET_SCREEN_KEY +import com.tangem.datasource.local.preferences.PreferencesKeys.SHOULD_SHOW_ASK_BIOMETRY_KEY import com.tangem.datasource.local.preferences.PreferencesKeys.SHOW_RATING_DIALOG_AT_LAUNCH_COUNT_KEY import com.tangem.datasource.local.preferences.PreferencesKeys.USED_CARDS_INFO_KEY import com.tangem.datasource.local.preferences.PreferencesKeys.USER_WAS_INTERACT_WITH_RATING_KEY @@ -26,7 +26,7 @@ object PreferencesKeys { val SAVE_USER_WALLETS_KEY by lazy { booleanPreferencesKey(name = "saveUserWallets") } - val SHOULD_SHOW_SAVE_USER_WALLET_SCREEN_KEY by lazy { booleanPreferencesKey("saveUserWalletShown") } + val SHOULD_SHOW_ASK_BIOMETRY_KEY by lazy { booleanPreferencesKey("saveUserWalletShown") } val APP_LAUNCH_COUNT_KEY by lazy { intPreferencesKey(name = "launchCount") } @@ -183,7 +183,7 @@ object PreferencesKeys { internal fun getTapPrefKeysToMigrate(): Set { return setOf( SAVE_USER_WALLETS_KEY, - SHOULD_SHOW_SAVE_USER_WALLET_SCREEN_KEY, + SHOULD_SHOW_ASK_BIOMETRY_KEY, APP_LAUNCH_COUNT_KEY, SHOW_RATING_DIALOG_AT_LAUNCH_COUNT_KEY, FUNDS_FOUND_DATE_KEY, diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml index d0bb34dd95..b7cc837f73 100644 --- a/core/res/src/main/res/values-de/strings.xml +++ b/core/res/src/main/res/values-de/strings.xml @@ -452,7 +452,7 @@ Aktuelle Preise werden abgerufen ... Variabler Zinssatz Durch die Nutzung der Swap-Funktion erklärst du dich mit den folgenden Bedingungen des Anbieters einverstanden %s - Durch die Nutzung der Swap-Funktionalität erklärst du dich mit des Anbieters %1$s und %2$s einverstanden + Durch die Nutzung der Swap-Funktionalität erklärst du dich mit des Anbieters %1$s und %2$s einverstanden. Weitere Anbieter folgen in Kürze Anbieter Bester Preis @@ -1039,7 +1039,7 @@ Senden... Berühre an beliebiger Stelle für Änderungen Versende %s - Du sendest **%1$s** inklusive der Netzwerkgebühr %2$s + Du sendest **%1$s** inklusive der Netzwerkgebühr %2$s. Du sendest **%1$s** und %2$s Du sendest **%1$s** Die Netzwerkgebühr wird durch die Nutzung von %1$s Energieträgern gedeckt. diff --git a/core/res/src/main/res/values-es/strings.xml b/core/res/src/main/res/values-es/strings.xml index 1e084899ad..16cac3a4f8 100644 --- a/core/res/src/main/res/values-es/strings.xml +++ b/core/res/src/main/res/values-es/strings.xml @@ -26,7 +26,7 @@ Gestionar tokens Envíe solo %1$s (%2$s) desde redes como %3$s a esta dirección. Usar otros tokens y redes puede resultar en la pérdida de fondos. Por defecto - Legado + Legacy Cómo escanear Solicitar soporte Inténtelo de nuevo @@ -65,8 +65,8 @@ Guarde estas %s palabras en un lugar seguro, como un administrador de contraseñas, y nunca las comparta con nadie. No se puede restaurar Frase de recuperación - Escriba estas palabras %s en orden y guárdelas en un lugar privado y seguro - La responsabilidad total de la seguridad y la copia de seguridad de la billetera y la frase de recuperación recae en el usuario, no en Tangem. + Escriba estas %s palabras en orden y guárdelas en un lugar privado y seguro + La responsabilidad total sobre la seguridad y copia de seguridad de la billetera y su frase de recuperación recae en el usuario, no en Tangem. Frase de recuperación Para ocultar o mostrar tus saldos, simplemente gira la pantalla de tu dispositivo hacia abajo, o desactívalo en Ajustes No mostrar de nuevo @@ -118,7 +118,7 @@ Requisitos de transacción de Cardano Para realizar una %1$s transacción, deba depositar algo de ADA para cubrir la tarifa de red y el valor mínimo de ADA (se recomiendan 5 ADA) ADA insuficiente para la transferencia de tokens - El monto enviado y el cambio no pueden ser menores a 1 ADA + La cantidad enviada y el cambio no pueden ser menores a 1 ADA Deba mantener algo de ADA porque tiene algunos tokens en la blockchain de Cardano ADA insuficiente Aceptar @@ -147,9 +147,9 @@ Cancelar Cambie Elige una acción - Elegir red - Elige token - Elegir wallet + Elija red + Elija token + Elija wallet Reclamar Reclame recompensas Cerrar @@ -221,7 +221,7 @@ Anillo primario Frase de contraseña Pegar - Política de privacidad + Política de privacidad %1$s-%2$s %1$s — %2$s Leer más @@ -254,10 +254,10 @@ Enviar Con éxito Soporte - Redes compatibles + Redes soportadas Swap términos y condiciones - Condiciones de uso + Condiciones de uso Hoy %d token @@ -271,7 +271,7 @@ Hubo un error. Por favor inténtelo de nuevo. Inaccesible Termine el staking - Debido a limitaciónes sobre %1$s, solos %2$d UTXO pueden caber en una sola transacción. Esto significa que solo puedes enviar %3$s o menos. Debe reducir la cantidad. + Debido a limitaciones sobre %1$s, solo %2$d UTXO pueden caber en una sola transacción. Esto significa que solo puedes enviar %3$s o menos. Debe reducir la cantidad. Valor copiado semana con @@ -334,7 +334,8 @@ Recibir activos %s dirección Enviar activos a otras redes resultará en una pérdida permanente. - Envíe fondos utilizando solo + Red de %s + Envíe fondos utilizando solo la Hola equipo de soporte, he encontrado un error con el código: %s Error de WalletConnect Ha usado una tarjeta de otra billetera. Toque la tarjeta asociada con esta billetera @@ -377,7 +378,7 @@ Esperando depósito Esperando depósito Reembolso completado - A la espera del reembolso + Esperando reembolso Transfiriendo a su cuenta Enviando fondos... Fondos enviados @@ -389,12 +390,12 @@ Buscar tarifas actuales... Tasa flotante Al utilizar la función de intercambio, acepta el %s - Al utilizar la función de intercambio, aceptas las condiciones de %1$s y %2$s del proveedor + Al utilizar la función de intercambio, aceptas las condiciones de %1$s y %2$s del proveedor. Pronto habrá más proveedores disponibles Proveedor Mejor tarifa Lista de advertencias de la FCA - Proveedor en la lista de advertencia de la FCA + Proveedor en la lista de advertencias de la FCA Disponible hasta %s Disponible desde %s No disponible para este par @@ -407,7 +408,7 @@ Una vez oculto, el estado de la transacción no se puede volver a ver. En su lugar, basta con deslizar el dedo para descartarla. ¿Ocultar el estado de la transacción? Este token no es compatible. Por favor, elige un token diferente para intercambiar. - %s no es compatible + %s no está soportado Intercambiar con No se encontraron fichas. Por favor intenta con otra solicitud ID : %s @@ -436,8 +437,8 @@ Para continuar, de autorización a los smart contracts de %1s para usar su %2s Dar autorización Ilimitado - Agregar billetera existente - Crear nueva billetera + Agregar Billetera Existente + Crear Nueva Billetera Pedir Tangem Escanee a %s @@ -524,7 +525,7 @@ Top Ganadores Top Perdedores Tendencias - Staking es la forma más fácil de recibir recompensas por tus criptomonedas. %s + Staking es la forma más fácil de recibir recompensas por sus criptomonedas. %s Gana hasta %s APY Acerca de %s @@ -605,14 +606,14 @@ Colecciones de NFT Es posible que algunos datos no se carguen Problemas temporales de carga - Información de base + Información básica Cadena Dirección del contrato Cadena es la blockchain en la que está el NFT. La dirección del contrato es un identificador único para el contrato inteligente que gobierna los tokens en la blockchain Una etiqueta que describe lo raro que es el NFT. Cuanto menor sea el valor, más único será el NFT. La posición de un NFT en el ranking de rareza entre otros tokens. Cuanto más alto sea el ranking, más raro y valioso será el NFT. - La dirección del contrato es un identificador único para el contrato inteligente que gobierna los tokens en la blockchain + La dirección del token es un identificador único del token en la blockchain, que permite el seguimiento de las transacciones y la propiedad El ID del token es un identificador único asignado a cada token, que lo distingue de otros en la colección. El estándar de token define qué tipo de token es y cómo funciona con diferentes billeteras y plataformas Último precio de venta @@ -642,13 +643,13 @@ Pulse aquí para recibir el primer NFT Colecciones de NFT No se pueden cargar los datos - Para utilizar la red %1$s, debe pagar la reserva de cuenta (%2$s%3$s), que bloquea y oculta ese monto indefinidamente + Para utilizar la red %1$s, debe pagar la reserva de cuenta (%2$s%3$s), que bloqueará y ocultará esa cantidad indefinidamente La cuenta de destino no está activa. Envíe %s o más para activar la cuenta. Para crear una cuenta, envíe fondos a esta dirección - La cuenta de destino no tiene una Trustline para el activo que se envía. - Únete ahora - Comparte tu código y gana 5 USDT por venta. Tu amigo obtiene un 10% de descuento. - ¡Obtén RECOMPENSAS por cada amigo! + La cuenta de destino no tiene una Trustline (línea de confianza) para el activo que se envía. + Únase ahora + Comparta su código y gane 5 USDT por venta. Su amigo obtiene un 10% de descuento. + ¡Obtenga RECOMPENSAS por cada amigo! Deba configurar un único código de acceso para proteger todss sus dispositivos. Proteger Puede configurar un código de acceso individual en cada tarjeta más adelante @@ -775,11 +776,11 @@ Organizar tokens Desagrupar Más información - Puedes activar las Notificaciones para Tangem en Ajustes. + Puede activar las Notificaciones para Tangem en Ajustes. Activar más tarde Ajustes Activar notificaciones - Reciba alertas de transacciones entrantes en las redes compatibles + Reciba alertas de transacciones entrantes en las redes soportadas Notificaciones de transacciones Seleccione de la galería Ajustes @@ -869,11 +870,11 @@ Introduce la dirección Nombre ENS o dirección La dirección es la misma que su billetera. - El monto mínimo es %s + La cantidad mínima es %s El cambio mínimo es %s Tarifa no válida El saldo mínimo es %s - No se ha creado la cuenta de destino. El monto a enviar debe ser %s + comisiones o más + No se ha creado la cuenta de destino. La cantidad a enviar debe ser %s + comisiones o más Error desconocido Memo no válido. No se añadirá a la transacción. Memo @@ -893,7 +894,7 @@ Memo no válido Cobertura de tarifa de red Nonce - Número único para cada transacción. Utilízalo para reenviar o cancelar una transacción pendiente. + Número único para cada transacción. Utilícelo para reenviar o cancelar una transacción pendiente. Introduzca nonce.. Fondos insuficientes para la transferencia, ya que el total de la tarifa y el importe de la transferencia supera el saldo existente El total supera el saldo @@ -934,7 +935,7 @@ Enviando... Toque cualquier campo para editarlo Enviar %s - Está enviando **%1$s** incluida una tarifa de red de %2$s + Está enviando **%1$s** incluida una tarifa de red de %2$s. Está enviando**%1$s** y %2$s Está enviando **%1$s** La tarifa de red se cubrirá al usar %1$s energía @@ -943,18 +944,18 @@ A la dirección La transacción se firmó con éxito y se envió al nodo blockchain. El saldo de la billetera se actualizará después de un tiempo. %1$s es un activo en la red Tron. Para calcular la tarifa y realizar una transacción, deba depositar algo de Tron (TRX) en su cuenta. - El monto excede el saldo + La cantidad excede el saldo Es necesario una etiqueta de destino (memo) para completar esta transacción para la dirección especificada. Etiqueta de destino obligatoria Cantidad no válida La tarifa excede el saldo - El monto total excede el saldo + La cantidad total excede el saldo Intercambiar y enviar ¿Continuar con la conversión? Esto borrará sus datos anteriores. Confirmar Conversión El envío de cualquier otra moneda supondrá su pérdida irreversible. Seleccione la red de destino correcta - Envía cualquier token y lo convertiremos en el camino. Su destinatario obtiene exactamente lo que necesita, sin problemas. + Elige cualquier token para recibir. Su destinatario recibirá exactamente lo que seleccionaste, sin problemas. El destinatario recibirá Al destinatario Cantidad a recibir @@ -966,7 +967,7 @@ Olvidar la billetera Esto eliminará la wallet de la aplicación. La wallet en sí puede\nañadirse de nuevo. Nombre - Haz que tu token trabaje por ti + Haga que su token trabaje para Ud. El monto del staking debe ser al menos %s El monto del staking se redondeará a %1$s TRX debido a las reglas de la red. El monto de cancelación del staking se redondeará a %1$s TRX debido a las reglas de la red. @@ -1101,7 +1102,7 @@ Compatible con Web 3.0 Se requiere una transacción entrante de al menos %1$s para proceder Fondos insuficientes - Tasa fija + Tasa Fija La red cobrará una tarifa de aprobación de token para verificar que está autorizando el uso de su token para el swap. Intercambie más tokens a mejores tasas directamente en su billetera. ¡Nuevo proveedor de intercambio disponible! @@ -1176,12 +1177,12 @@ desde: %s a: %s validador: %s - Las notificaciones están activadas, pero no funcionarán hasta que permitas las notificaciones en la configuración de tu dispositivo. + Las notificaciones están activadas, pero no funcionarán hasta que permita las notificaciones en la configuración de su dispositivo. Notificaciones de transacciones Mínimo %s El monto mínimo para realizar esta transacción es %1$s. - Las tarifas de la red Tron para tokens populares pueden ser más altas. Hacer staking de TRX puede ayudar a reducir los costos de transacción. - Ahorra en las tarifas de la red Tron + Las tarifas de la red Tron para tokens populares pueden ser más altas. Hacer staking de TRX puede ayudar a reducir costes de transacción. + Ahorre en las tarifas de la red Tron Inténtelo de nuevo Ha escaneado la misma tarjeta. Para crear una billetera gemela, necesite escanear la tarjeta con no. %d Ha escaneado la tarjeta gemela incorrecta. Por favor, intente con otra @@ -1196,7 +1197,7 @@ Toque la tarjeta gemela con el número %s y no la retira hasta el final de la operación Por favor, inténtelo de nuevo más tarde. Si el problema persiste, póngase en contacto con el servicio de asistencia. ¡Algo salió mal! - Hemos encontrado un error. Código de error: %s. Póngase en contacto con nuestro servicio de asistencia. + Hemos encontrado un error. Código de error: %s. Póngase en contacto con nuestro servicio de soporte. Use %s o escanee una tarjeta/anillo para tener acceso a su billetera Error de conexión: Esta dApp utiliza la versión 1.0 de Wallet Connect, que no es compatible. Asegúrese de que la dApp sea compatible con la versión 2.0 de Wallet Connect para conectarse correctamente. Manténgase actualizado con las últimas funciones y noticias @@ -1260,7 +1261,7 @@ Accede a más de 13 000 criptomonedas. Compra, vende, intercambia y realiza staking con un solo toque.\nVincula hasta tres tarjetas para hacer copias de seguridad. Descubre Tangem Wallet Cambiar código de acceso - Manténteinformado sobre las transacciones entrantes de la billetera y las actualizaciones de Tangem. + Manténgase informado sobre las transacciones entrantes de la billetera y las actualizaciones de Tangem. Notificaciones de transacciones Establecer código de acceso Ajustes de la wallet @@ -1297,7 +1298,7 @@ El monto a recibir debe ser de al menos %s Esto puede suceder porque el proveedor actualmente no puede operar con el par que usted seleccionó. Espere un momento e inténtelo de nuevo. (Código %s) El par seleccionado no está disponible temporalmente - Algunos proveedores no están autorizados por la Autoridad de Conducta Financiera (FCA) del Reino Unido. Deberías evitar tratar con ellos. + Algunos proveedores no están autorizados por la Autoridad de Conducta Financiera del Reino Unido. Debe evitar tratar con ellos. Servicio no disponible temporalmente La cantidad de tokens a intercambiar no debe exceder %s El monto a cambiar debe ser de al menos %s @@ -1333,10 +1334,10 @@ Su opinión nos motiva a hacer Tangem Wallet aún mejor ¿Disfrutando de Tangem? Deba asociar su token antes de recibir tokens - Debes abrir una línea de confianza para tu token antes de recibirlo + Debe abrir una trustline para su token antes de recibirlo Se requiere tarifa de alquiler de red Acción requerida - ¿Te pusiste en contacto con el servicio de asistencia a través de la aplicación o por correo electrónico en los 7 días posteriores a la creación de la billetera? Si lo hiciste o no estás seguro, sigue y completa las instrucciones. + ¿Se puso en contacto con el servicio de soporte a través de la app en los 7 días posteriores a la creación de la billetera? Si lo hiciste o no estás seguro, pulse en \"Sí\" y complete las instrucciones. ¡Gracias! ¡Todo listo! No se requieren más acciones. Ahora será redirigido al sitio web oficial de Tangem. Por favor, lea y siga las instrucciones allí indicadas. ¿Alguna vez contactó al equipo de soporte de Tangem directamente desde esta aplicación? @@ -1353,16 +1354,16 @@ Esta es una tarjeta de Testnet. No puede procesar transacciones y solo deba usarse para pruebas y desarrollo. Solo para fines de prueba El saldo podría estar desactualizado. Actualice la página. - No hay suficiente %1$s. Recarga tu %2$s cuenta para asociar este token - Habilitar línea de confianza - Una línea de confianza debe estar habilitada para recibir este token. La red requiere un %1$s %2$s reserva. - Se requiere línea de confianza + No hay suficiente %1$s. Recargue su cuenta %2$s para asociar este token + Habilitar Trustline + Una trustline (línea de confianza) debe estar habilitada para recibir este token. La red requiere un %1$s %2$s reserva. + Se requiere trustline (línea de confianza) La red requerida %s no está añadida a su portafolio. Añádala primero y luego continúe con la conexión. Agregue red al portafolio Dominio malicioso Dominio desconocido Conectarse de todas formas - Error de tiempo de espera. Por favor, inténtalo de nuevo más tarde. + Error de tiempo de espera. Por favor, inténtelo de nuevo más tarde. Error al establecer WalletConnect Este dominio no puede ser verificado. Compruebe cuidadosamente la solicitud de aprobación. Para continuar, vuelva a conectar su sesión de dApp con la red requerida %s. @@ -1382,10 +1383,10 @@ Esta red %s no es compatible con Tangem Wallet y no puede conectarse. Red no compatible Actualmente, Tangem no es compatible con una red requerida por %s. - Redes no compatibles + Redes no soportadas Este dominio ha superado las verificaciones y se considera seguro, confiable y libre de amenazas conocidas o actividades sospechosas. Dominio verificado - Se seleccionó una tarjeta o un anillo incorrectos en la app + Se seleccionó una tarjeta o un anillo incorrectos en la App Tenemos algún tipo de problema Todas las dApps desconectadas Permitir gastar @@ -1414,22 +1415,22 @@ Todas las sesiones de dApp se desconectarán. Su billetera ya no estará vinculada a ninguna dApp. Desconectar todas las dApps Intente emparejar nuevamente con una URI nueva - Dominio de dApp no válido + Dominio de dApp incorrecto %s no especifica ninguna blockchain, ni obligatoria ni opcional. Asegúrese de haber utilizado el URI correcto Sin redes Por favor, genere una nueva URI e intente conectarse nuevamente Propuesta de conexión caducada Cambios estimados en la billetera La transacción no ha podido ser simulada. Por favor, proceda con precaución. - La estimación no es compatible con %s + La estimación no está soportada para %s Sugerido por %s Recargue su saldo para cubrir la tarifa de red Insuficiente %1$s Transacción maliciosa - Añada la red %s a su perfil para esta billetera + Añada la red %s a su portafolio para esta billetera La billetera no tiene las redes requeridas Nueva conexión - Conecta tu billetera a diferentes dApps + Conecte su billetera a diferentes dApps Sin sesiones No se han detectado cambios en la billetera Se han detectado riesgos potenciales o comportamiento malicioso. Conectarse o firmar transacciones puede resultar en la pérdida de fondos. @@ -1437,7 +1438,7 @@ Abra la aplicación Web3 y elija la opción WalletConnect Solicitud de Firmar de todos modos - Tipo de firma + Tipo de Firma Se requiere al menos una red para la conexión dApp Especificar las redes seleccionadas Firmado correctamente @@ -1461,6 +1462,6 @@ Use %s o escanee una tarjeta/anillo para acceder a la app ¡Bienvenido de nuevo! No, enviar todo - Réduire de %s XTZ - Para evitar pagar una comisión mayor la próxima vez que recargue su billetera, reduzca el monto en %s + Reducir en %s XTZ + Para evitar pagar una comisión mayor la próxima vez que recargue su billetera, reduzca el importe en %s XTZ diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml index 228fa133e0..0d2ee7b831 100644 --- a/core/res/src/main/res/values-fr/strings.xml +++ b/core/res/src/main/res/values-fr/strings.xml @@ -198,7 +198,7 @@ Bague principale Passphrase Coller - Politique de confidentialité + Politique de confidentialité %1$s-%2$s %1$s — %2$s En savoir plus @@ -234,7 +234,7 @@ Réseaux pris en charge Échanger termes et conditions - Conditions d\'utilisation + Conditions d\'utilisation Aujourd\'hui %d jeton @@ -311,6 +311,7 @@ Recevoir des actifs %s adresse L’envoi d’actifs sur d’autres réseaux entraînera une perte définitive. + %s réseau Envoyez des fonds en utilisant uniquement Bonjour équipe de support, j’ai rencontré une erreur avec le code : %s Erreur WalletConnect @@ -365,7 +366,7 @@ En cherche des meilleurs taux Taux flottant En utilisant la fonctionnalité d\'échange, vous acceptez les %s - En utilisant la fonctionnalité d\'échange, vous acceptez les conditions %1$s et %2$s du fournisseur + En utilisant la fonctionnalité d\'échange, vous acceptez les conditions %1$s et %2$s du fournisseur. D\'autres fournisseurs arriveront bientôt.\nRestez branchés ! Fournisseur Meilleur taux @@ -908,7 +909,7 @@ En cours d\'envoi Appuyez sur n\'importe quel champ pour le modifier Envoyer %s - Vous envoyez **%1$s** incluant des frais de réseau de %2$s + Vous envoyez **%1$s** incluant des frais de réseau de %2$s. Vous envoyez **%1$s** et %2$s Vous envoyez **%1$s** les frais de réseau seront couverts en utilisant %1$s énergie @@ -923,11 +924,20 @@ Montant invalide Les frais de commissions dépassent le solde Le total dépasse le solde + Êtes-vous sûr de vouloir modifier le token de réception ? Cela réinitialisera les données que vous avez saisies précédemment. + Changement de token Échanger et envoyer + Poursuivre l\'échange ? Cela effacera vos données précédentes. + Confirmer la conversion + L\'envoi de toute autre crypto entraînera sa perte irréversible. + Sélectionnez le réseau destinataire approprié. Envoyez n\'importe quel jeton et nous le convertirons en cours de route. Votre destinataire reçoit exactement ce dont il a besoin, en toute simplicité. Le destinataire recevra Un destinataire sera envoyé Montant à recevoir + Le destinataire reçoit %s + Êtes-vous sûr de vouloir annuler la conversion ? Vos données précédentes seront effacées. + Supprimer la conversion Envoyer avec swap Transaction envoyée Scannez la carte/ bague que vous souhaitez configurer. diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 35ce29d63a..34e2073bb1 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -4,6 +4,7 @@ Вы архивируете свой аккаунт, но в любое время можете вернуть его обратно Аккаунт Новый аккаунт + Основной аккаунт Продолжить редактирование Несохраненные изменения Не нашли свой токен? Перейдите в раздел «Рынок» на главной странице и добавьте его в свой портфель для покупки. @@ -204,7 +205,7 @@ Основное кольцо Парольная фраза Вставить - Политикой конфиденциальности + Политикой конфиденциальности %1$s-%2$s %1$s — %2$s Подробнее @@ -237,7 +238,7 @@ Поддержка Обменять условия участия - Условиями использования + Условиями использования Сегодня %d токен @@ -316,7 +317,7 @@ Получить активы %s адрес Отправка средств в другой сети может повлечь потерю средств. - %s сети + %s сеть Отправляйте средства, используя только Привет, команда поддержки, у меня возникла ошибка с кодом: %s Ошибка WalletConnect @@ -337,7 +338,7 @@ Обмен через %s Чтобы вернуть ваши деньги, посетите сайт провайдера Операция не выполнена провайдером - Ваш обмен занимает больше времени, чем ожидалось. Пожалуйста, обратитесь в службу поддержки провайдера для уточнения. + Ваш обмен занимает больше времени, чем обычно, но ваши средства полностью в безопасности и будут доставлены. Если возникли вопросы, вы всегда можете связаться с поддержкой провайдера. Отправленные средства были возвращены в %1$s на ваш кошелек в соответствии с правилами OKX или моста обмена. %2$s Сумма была возвращена в %1$s (%2$s сети) Посетите сайт провайдера для проверки @@ -370,7 +371,7 @@ Получение наилучших курсов... Плавающая ставка Пользуясь сервисом, вы соглашаетесь с %s - Пользуясь сервисом, вы соглашаетесь с %1$s и %2$s + Пользуясь сервисом, вы соглашаетесь с %1$s и %2$s. Больше провайдеров на подходе.\nСледите за обновлениями! Провайдер Лучший курс @@ -929,7 +930,7 @@ Отправка Нажмите на любое поле, чтобы изменить его Отправка %s - Вы отправляете **%1$s**, включая комиссию сети %2$s + Вы отправляете **%1$s**, включая комиссию сети %2$s. Вы отправляете **%1$s** и %2$s Вы отправляете **%1$s** Комиссия сети будет покрыта за счет использования %1$s энергии diff --git a/core/res/src/main/res/values-uk-rUA/strings.xml b/core/res/src/main/res/values-uk-rUA/strings.xml index 709edd7c9e..33db0a745b 100644 --- a/core/res/src/main/res/values-uk-rUA/strings.xml +++ b/core/res/src/main/res/values-uk-rUA/strings.xml @@ -173,6 +173,7 @@ Синхронізувати адреси Перейти до провайдера Перейти до токену + Зrozуміло Приховати година Імпортувати @@ -196,7 +197,7 @@ Основне кільце Парольна фраза Вставити - Політикою конфіденційності + Політикою конфіденційності %1$s-%2$s %1$s — %2$s Детальніше @@ -230,7 +231,7 @@ Підтримка Обмін умови участі - Умовами використання + Умовами використання Сьогодні %d токен @@ -307,6 +308,7 @@ Отримати активи %s адреса Надсилання активів в інші мережі призведе до безповоротної втрати. + %s мережа Надсилайте кошти, використовуючи лише Привіт, команда підтримки, я зіткнувся з помилкою з кодом: %s Помилка WalletConnect @@ -327,7 +329,7 @@ Обмін через %s Щоб повернути ваші кошти, відвідайте сайт провайдера Операція не виконана провайдером - Ваш обмін триває довше, ніж очікувалося. Будь ласка, зверніться до служби підтримки постачальника послуг, для уточнення. + Ваш обмін триває довше, ніж зазвичай, але ваші кошти повністю в безпеці та будуть доставлені. Якщо ви хвилюєтесь, ви завжди можете звернутися до служби підтримки провайдера. Тривалий час транзакції Сума транзакції була повернута в %1$s на ваш гаманець відповідно до правил OKX або мосту обміну. %2$s Сума була повернута в %1$s (%2$s мережі) @@ -361,7 +363,7 @@ Шукаємо найвигідніший курс... Плаваюча ставка Використовуючи сервіс обміну, ви погоджуєтеся з його %s - Використовуючи сервіс обміну, ви погоджуєтеся з його %1$s та %2$s + Використовуючи сервіс обміну, ви погоджуєтеся з його %1$s та %2$s. Незабаром з\'являться нові провайдери.\nСлідкуйте за новинами! Провайдер Найкращий курс @@ -910,7 +912,7 @@ Надсилання... Торкніться будь-якого поля, щоб змінити його Надіслати %s - Ви надсилаєте **%1$s**, включно з комісію мережі %2$s + Ви надсилаєте **%1$s**, включно з комісію мережі %2$s. Ви надсилаєте **%1$s** і %2$s Ви надсилаєте **%1$s** комісія мережі буде покрита, використовуючи %1$s енергію diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 551ec44606..7d747732ea 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -35,6 +35,7 @@ Add account Edit account %1$s in %2$s + Main account Account recovered Long tap on an account to reorder accounts Keep Editing @@ -182,6 +183,7 @@ Allow Amount Analytics + and Apply Approval Approve @@ -255,6 +257,7 @@ Import In progress Later + Learn more %1$s left Legacy Bitcoin Locked @@ -280,7 +283,7 @@ Primary ring Passphrase Paste - Privacy Policy + Privacy Policy %1$s-%2$s %1$s — %2$s Read more @@ -319,7 +322,7 @@ Tangem Tangem Wallet terms and conditions - Terms of Use + Terms of Use Today %d token @@ -419,7 +422,7 @@ Exchange by %s Visit provider’s website to refund your money Operation failed by provider - Your swap is taking longer than expected. Please go to provider support for assistance. + Your swap is taking longer than usual, but your funds are completely safe and will be delivered. For any questions, you can contact the provider’s support team. Long transaction time The transaction amount was refunded in %1$s to your wallet due to OKX or bridge rules. %2$s The amount was refunded in %1$s (%2$s network) @@ -453,7 +456,7 @@ Fetching current rates... Floating rate By using swap functionality, you agree with provider’s %s - By using swap functionality, you agree with provider’s %1$s and %2$s + By using swap functionality, you agree with provider’s %1$s and %2$s. More providers will be available soon Provider Best rate @@ -504,6 +507,7 @@ Create New Wallet Order Tangem Scan Tangem + Do you want to allow “Tangem” to use biometric authentication? To confirm your identity and open the app to %s On %s network Are you sure you want to exit the access code creation process? @@ -800,6 +804,7 @@ Another wallet has already been created on the card you\'re trying to add. If you have funds in this wallet, please withdraw it and then reset this card and add it as a backup. Save your wallet Creating a backup + Use biometrics Read more about seed phrase @@ -853,6 +858,7 @@ Access code restore Identical cards Access code + All offers Available with %s Providers facilitate transactions Search by country @@ -860,13 +866,20 @@ Other currencies Popular Fiats Search by currency + Instant By using onramp functionality, you agree with provider’s %1$s and %2$s The purchase amount should be no more than %s The amount to buy must be at least %s No available providers for this currency + Fastest Pay with + Payment method Available up to %s Available from %s + %d providers + Providers + Recently used + Recommended You will be able to complete your transaction on the third-party provider, %s Redirecting to %s... Our services are not available in this country @@ -875,6 +888,12 @@ Please select the correct country to ensure accurate payment options and services. Settings You can close this screen and check the transaction status on the token details screen. + + up to %d day + up to %d days + + %s min + You get You can close this screen and check the transaction status on the token details screen. Via You will pay @@ -1043,7 +1062,7 @@ Sending... Tap any field to change it Send %s - You are sending **%1$s** including a network fee of %2$s + You are sending **%1$s** including a network fee of %2$s. You are sending **%1$s** and %2$s You are sending **%1$s** network fee will be covered by using %1$s energy @@ -1668,4 +1687,68 @@ No, send all Reduce by %s XTZ To avoid paying an increased commission the next time you top up your wallet, reduce the amount by %s XTZ + Give Approve + Something went wrong with your previous approval, so we need a new one. Choose how you’d like to proceed. + Approve nedeed + The fee will be taken out, and your assets will be lent again. + To continue earning, approval is required. + Confirm approval + Text about your money in balance + Your %s is deposited in Aave + Earn %s% + Available + Current APY + My Funds + Your %1$s is now deposited in Aave and earning interest. You hold a%2$s token, which represents your balance and grows over time. When you top up, funds go to Aave to earn interest, minus a transaction fee. + Read more + Active + Stop earning + Earn + Total earnings + Automatic + Transfers to Aave + Text explaining that your savings are located in the protocol + Explore Aave + Explore default + Your %s is deposited in Aave + This is the current supply fee on %s. The live cost will be shown on the Receive Screen. + Current fee + All future %s top-ups will be supplied to Aave automatically, with the transaction fee deducted. + If network fees rise above maximum fee, the transaction won’t go through until they decrease. You can change this limit later. + Maximum fee + Fee policy + Write description here. In one, two or three lines will be awesome. + Some token approve needed + Every top-up of your account will be lended to Aave automatically. + Your balance works automatically + Send, swap, or sell your funds instantly, anytime you want. + Cash out instantly + How it works? + Aave is trusted by millions worldwide. Total lended value is $10.4B. + Decentralized and self-custodial + By using service, you agree with provider + Maximize your savings + Aave • Variable Interest Rate + Aave + Avg %s + Last year returns + Current interest rate is always variable and automatically computed by AAVE on-chain smart-contract based on real-time supply and demand. + Interest rate is variable + When you top up, your funds will be automatically sent to Aave to start earning interest. A small fee equal to %s will be deducted to cover the transaction. + Start earning + Your %s will be supplied to Aave and will stay instantly available + See fee policy + Network fee + Your next deposits will be automatically supplied to Aave. + Start earning + Turning off will withdraw your funds from Aave, return them to %s in your wallet, and stop earning rewards. + The network fee will be deducted from the amount you withdraw. + Stop earning + APY + Make your money work — earn interest on your balance. + Earning on your balance + Processing your deposit + Earn %s% per year + Deposit some %1$s %2$s to cover the network fee for transactions + Unable to cover %s fee diff --git a/data/settings/src/main/java/com/tangem/data/settings/DefaultSettingsRepository.kt b/data/settings/src/main/java/com/tangem/data/settings/DefaultSettingsRepository.kt index e27fce119a..5412605960 100644 --- a/data/settings/src/main/java/com/tangem/data/settings/DefaultSettingsRepository.kt +++ b/data/settings/src/main/java/com/tangem/data/settings/DefaultSettingsRepository.kt @@ -29,15 +29,15 @@ internal class DefaultSettingsRepository( private val userCountryFlow = MutableStateFlow(value = null) - override suspend fun shouldShowSaveUserWalletScreen(): Boolean { + override suspend fun shouldShowAskBiometry(): Boolean { return appPreferencesStore.getSyncOrDefault( - key = PreferencesKeys.SHOULD_SHOW_SAVE_USER_WALLET_SCREEN_KEY, + key = PreferencesKeys.SHOULD_SHOW_ASK_BIOMETRY_KEY, default = true, ) } - override suspend fun setShouldShowSaveUserWalletScreen(value: Boolean) { - appPreferencesStore.store(key = PreferencesKeys.SHOULD_SHOW_SAVE_USER_WALLET_SCREEN_KEY, value = value) + override suspend fun setShouldShowAskBiometry(value: Boolean) { + appPreferencesStore.store(key = PreferencesKeys.SHOULD_SHOW_ASK_BIOMETRY_KEY, value = value) } override suspend fun isWalletScrollPreviewEnabled(): Boolean { diff --git a/domain/settings/src/main/java/com/tangem/domain/settings/SetSaveWalletScreenShownUseCase.kt b/domain/settings/src/main/java/com/tangem/domain/settings/SetAskBiometryShownUseCase.kt similarity index 71% rename from domain/settings/src/main/java/com/tangem/domain/settings/SetSaveWalletScreenShownUseCase.kt rename to domain/settings/src/main/java/com/tangem/domain/settings/SetAskBiometryShownUseCase.kt index 593b82a683..760ec572c6 100644 --- a/domain/settings/src/main/java/com/tangem/domain/settings/SetSaveWalletScreenShownUseCase.kt +++ b/domain/settings/src/main/java/com/tangem/domain/settings/SetAskBiometryShownUseCase.kt @@ -3,13 +3,13 @@ package com.tangem.domain.settings import arrow.core.Either import com.tangem.domain.settings.repositories.SettingsRepository -class SetSaveWalletScreenShownUseCase( +class SetAskBiometryShownUseCase( private val settingsRepository: SettingsRepository, ) { suspend operator fun invoke(): Either { return Either.catch { - settingsRepository.setShouldShowSaveUserWalletScreen(value = false) + settingsRepository.setShouldShowAskBiometry(value = false) } } } \ No newline at end of file diff --git a/domain/settings/src/main/java/com/tangem/domain/settings/ShouldShowSaveWalletScreenUseCase.kt b/domain/settings/src/main/java/com/tangem/domain/settings/ShouldShowAskBiometryUseCase.kt similarity index 57% rename from domain/settings/src/main/java/com/tangem/domain/settings/ShouldShowSaveWalletScreenUseCase.kt rename to domain/settings/src/main/java/com/tangem/domain/settings/ShouldShowAskBiometryUseCase.kt index 872f62c157..0cab3f56d4 100644 --- a/domain/settings/src/main/java/com/tangem/domain/settings/ShouldShowSaveWalletScreenUseCase.kt +++ b/domain/settings/src/main/java/com/tangem/domain/settings/ShouldShowAskBiometryUseCase.kt @@ -2,7 +2,7 @@ package com.tangem.domain.settings import com.tangem.domain.settings.repositories.SettingsRepository -class ShouldShowSaveWalletScreenUseCase(private val settingsRepository: SettingsRepository) { +class ShouldShowAskBiometryUseCase(private val settingsRepository: SettingsRepository) { - suspend operator fun invoke(): Boolean = settingsRepository.shouldShowSaveUserWalletScreen() + suspend operator fun invoke(): Boolean = settingsRepository.shouldShowAskBiometry() } \ No newline at end of file diff --git a/domain/settings/src/main/java/com/tangem/domain/settings/repositories/SettingsRepository.kt b/domain/settings/src/main/java/com/tangem/domain/settings/repositories/SettingsRepository.kt index e39efd87a2..0a8f854790 100644 --- a/domain/settings/src/main/java/com/tangem/domain/settings/repositories/SettingsRepository.kt +++ b/domain/settings/src/main/java/com/tangem/domain/settings/repositories/SettingsRepository.kt @@ -7,9 +7,9 @@ import kotlinx.coroutines.flow.StateFlow @Suppress("TooManyFunctions") interface SettingsRepository { - suspend fun shouldShowSaveUserWalletScreen(): Boolean + suspend fun shouldShowAskBiometry(): Boolean - suspend fun setShouldShowSaveUserWalletScreen(value: Boolean) + suspend fun setShouldShowAskBiometry(value: Boolean) suspend fun isWalletScrollPreviewEnabled(): Boolean diff --git a/features/biometry/impl/src/main/kotlin/com/tangem/features/biometry/impl/model/AskBiometryModel.kt b/features/biometry/impl/src/main/kotlin/com/tangem/features/biometry/impl/model/AskBiometryModel.kt index 195b2c9069..0d53779766 100644 --- a/features/biometry/impl/src/main/kotlin/com/tangem/features/biometry/impl/model/AskBiometryModel.kt +++ b/features/biometry/impl/src/main/kotlin/com/tangem/features/biometry/impl/model/AskBiometryModel.kt @@ -15,7 +15,7 @@ import com.tangem.core.ui.message.SnackbarMessage import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.core.wallets.UserWalletsListRepository import com.tangem.domain.models.wallet.UserWallet -import com.tangem.domain.settings.SetSaveWalletScreenShownUseCase +import com.tangem.domain.settings.SetAskBiometryShownUseCase import com.tangem.domain.settings.repositories.SettingsRepository import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase @@ -39,7 +39,7 @@ import javax.inject.Inject internal class AskBiometryModel @Inject constructor( override val dispatchers: CoroutineDispatcherProvider, paramsContainer: ParamsContainer, - private val setSaveWalletScreenShownUseCase: SetSaveWalletScreenShownUseCase, + private val setAskBiometryShownUseCase: SetAskBiometryShownUseCase, private val settingsRepository: SettingsRepository, private val tangemSdkManager: TangemSdkManager, private val getSelectedWalletUseCase: GetSelectedWalletUseCase, @@ -66,7 +66,7 @@ internal class AskBiometryModel @Inject constructor( init { modelScope.launch { - setSaveWalletScreenShownUseCase() + setAskBiometryShownUseCase() } } diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/model/UserWalletListModel.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/model/UserWalletListModel.kt index bfdc9b284b..9ced337ddf 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/model/UserWalletListModel.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/model/UserWalletListModel.kt @@ -48,7 +48,7 @@ internal class UserWalletListModel @Inject constructor( private val userWalletsFetcher = userWalletsFetcherFactory.create( messageSender = messageSender, onlyMultiCurrency = false, - authMode = false, + isAuthMode = false, onWalletClick = { userWalletId -> router.push(AppRoute.WalletSettings(userWalletId)) }, ) diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeModel.kt index 2db69f48ee..a3d0e3c9d7 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeModel.kt @@ -5,12 +5,20 @@ import arrow.core.getOrElse import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.core.decompose.ui.UiMessageSender +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.message.DialogMessage +import com.tangem.core.ui.message.EventMessageAction import com.tangem.domain.core.wallets.UserWalletsListRepository import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.settings.CanUseBiometryUseCase +import com.tangem.domain.settings.SetAskBiometryShownUseCase +import com.tangem.domain.settings.ShouldShowAskBiometryUseCase import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.features.hotwallet.accesscode.entity.AccessCodeUM +import com.tangem.features.hotwallet.impl.R import com.tangem.hot.sdk.TangemHotSdk import com.tangem.hot.sdk.model.HotAuth import com.tangem.hot.sdk.model.UnlockHotWallet @@ -19,9 +27,11 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import kotlinx.coroutines.suspendCancellableCoroutine import timber.log.Timber import javax.inject.Inject +@Suppress("LongParameterList") @Stable @ModelScoped internal class AccessCodeModel @Inject constructor( @@ -31,6 +41,10 @@ internal class AccessCodeModel @Inject constructor( private val userWalletsListRepository: UserWalletsListRepository, private val walletsRepository: WalletsRepository, private val tangemHotSdk: TangemHotSdk, + private val shouldShowAskBiometryUseCase: ShouldShowAskBiometryUseCase, + private val setAskBiometryShownUseCase: SetAskBiometryShownUseCase, + private val canUseBiometryUseCase: CanUseBiometryUseCase, + private val uiMessageSender: UiMessageSender, ) : Model() { private val params = paramsContainer.require() @@ -87,6 +101,8 @@ internal class AccessCodeModel @Inject constructor( auth = HotAuth.Password(accessCode.toCharArray()), ) + tryToAskForBiometry() + if (walletsRepository.requireAccessCode().not()) { updatedHotWalletId = tangemHotSdk.changeAuth( unlockHotWallet = UnlockHotWallet( @@ -127,4 +143,49 @@ internal class AccessCodeModel @Inject constructor( } } } + + private suspend fun tryToAskForBiometry() { + if (!shouldAskForBiometry()) return + + suspendCancellableCoroutine { continuation -> + uiMessageSender.send( + DialogMessage( + title = resourceReference(R.string.common_attention), + message = resourceReference(R.string.hot_access_code_set_biometric_ask), + firstAction = EventMessageAction( + title = resourceReference(R.string.common_allow), + onClick = { + modelScope.launch { + setAskBiometryShownUseCase() + walletsRepository.setUseBiometricAuthentication(true) + walletsRepository.setRequireAccessCode(false) + continuation.resumeWith(Result.success(Unit)) + } + }, + ), + secondAction = EventMessageAction( + title = resourceReference(R.string.save_user_wallet_agreement_dont_allow), + onClick = { + modelScope.launch { + setAskBiometryShownUseCase() + continuation.resumeWith(Result.success(Unit)) + } + }, + ), + onDismissRequest = { + modelScope.launch { + continuation.resumeWith(Result.success(Unit)) + } + }, + ), + ) + } + } + + private suspend fun shouldAskForBiometry(): Boolean { + val canUseBiometry = canUseBiometryUseCase() + val shouldShowAskBiometry = shouldShowAskBiometryUseCase() + + return canUseBiometry && shouldShowAskBiometry + } } \ No newline at end of file diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/OnboardingEntryModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/OnboardingEntryModel.kt index e1aca5db5e..0ac07b8a3e 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/OnboardingEntryModel.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/OnboardingEntryModel.kt @@ -149,7 +149,7 @@ internal class OnboardingEntryModel @Inject constructor( doneMode: OnboardingDoneComponent.Mode = OnboardingDoneComponent.Mode.WalletCreated, ) { modelScope.launch { - if (tangemSdkManager.checkCanUseBiometry() && settingsRepository.shouldShowSaveUserWalletScreen()) { + if (tangemSdkManager.checkCanUseBiometry() && settingsRepository.shouldShowAskBiometry()) { doIfVisa { analyticsEventHandler.send(OnboardingVisaAnalyticsEvent.BiometricScreenOpened) } diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/finalize/model/MultiWalletFinalizeModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/finalize/model/MultiWalletFinalizeModel.kt index 5c62c61337..e05be61546 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/finalize/model/MultiWalletFinalizeModel.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/finalize/model/MultiWalletFinalizeModel.kt @@ -19,8 +19,8 @@ import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.domain.onboarding.repository.OnboardingRepository import com.tangem.domain.wallets.builder.ColdUserWalletBuilder -import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.repository.WalletsRepository +import com.tangem.domain.wallets.usecase.GetWalletsUseCase import com.tangem.domain.wallets.usecase.SaveWalletUseCase import com.tangem.domain.wallets.usecase.UpdateWalletUseCase import com.tangem.features.onboarding.v2.common.ui.CantLeaveBackupDialog @@ -37,7 +37,10 @@ import com.tangem.sdk.api.TangemSdkManager import com.tangem.utils.StringsSigns import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.NonCancellable -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import javax.inject.Inject @@ -52,8 +55,8 @@ internal class MultiWalletFinalizeModel @Inject constructor( private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase, private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase, private val coldUserWalletBuilderFactory: ColdUserWalletBuilder.Factory, - private val userWalletsListManager: UserWalletsListManager, private val saveWalletUseCase: SaveWalletUseCase, + private val getUserWalletsUseCase: GetWalletsUseCase, private val updateWalletUseCase: UpdateWalletUseCase, private val cardRepository: CardRepository, private val onboardingRepository: OnboardingRepository, @@ -79,8 +82,7 @@ internal class MultiWalletFinalizeModel @Inject constructor( // sets proper artwork state for initial step // (if we start from backup cards, we need to show proper artwork) ([REDACTED_TASK_KEY]) when (getInitialStep()) { - MultiWalletFinalizeUM.Step.Primary -> { /* state is already set */ - } + MultiWalletFinalizeUM.Step.Primary -> { /* state is already set */ } MultiWalletFinalizeUM.Step.BackupDevice1 -> { onEvent.emit(MultiWalletFinalizeComponent.Event.OneBackupCardAdded) } @@ -245,7 +247,7 @@ internal class MultiWalletFinalizeModel @Inject constructor( userWalletCreated } OnboardingMultiWalletComponent.Mode.AddBackup -> { - val userWallet = userWalletsListManager.userWallets.first() + val userWallet = getUserWalletsUseCase.invokeSync() .firstOrNull { it is UserWallet.Cold && it.scanResponse.primaryCard?.cardId == scanResponse.primaryCard?.cardId diff --git a/features/wallet/api/src/main/kotlin/com/tangem/features/wallet/utils/UserWalletsFetcher.kt b/features/wallet/api/src/main/kotlin/com/tangem/features/wallet/utils/UserWalletsFetcher.kt index 674541796f..97868f231b 100644 --- a/features/wallet/api/src/main/kotlin/com/tangem/features/wallet/utils/UserWalletsFetcher.kt +++ b/features/wallet/api/src/main/kotlin/com/tangem/features/wallet/utils/UserWalletsFetcher.kt @@ -14,7 +14,7 @@ interface UserWalletsFetcher { fun create( messageSender: UiMessageSender, onlyMultiCurrency: Boolean, - authMode: Boolean, + isAuthMode: Boolean, onWalletClick: (UserWalletId) -> Unit, ): UserWalletsFetcher } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt index b72ac8cf5c..6924fc073a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt @@ -10,6 +10,7 @@ import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase +import com.tangem.domain.core.wallets.UserWalletsListRepository import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.isLocked @@ -35,6 +36,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.transformers.* import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider import com.tangem.features.biometry.AskBiometryComponent +import com.tangem.features.hotwallet.HotWalletFeatureToggles import com.tangem.features.pushnotifications.api.PushNotificationsModelCallbacks import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION import com.tangem.features.wallet.deeplink.WalletDeepLinkActionListener @@ -58,7 +60,7 @@ internal class WalletModel @Inject constructor( private val walletScreenContentLoader: WalletScreenContentLoader, private val getSelectedWalletUseCase: GetSelectedWalletUseCase, private val getWalletsUseCase: GetWalletsUseCase, - private val shouldShowSaveWalletScreenUseCase: ShouldShowSaveWalletScreenUseCase, + private val shouldShowAskBiometryUseCase: ShouldShowAskBiometryUseCase, private val shouldShowMarketsTooltipUseCase: ShouldShowMarketsTooltipUseCase, private val setWalletFirstTimeUsageUseCase: SetWalletFirstTimeUsageUseCase, private val canUseBiometryUseCase: CanUseBiometryUseCase, @@ -80,6 +82,8 @@ internal class WalletModel @Inject constructor( private val setNotificationsEnabledUseCase: SetNotificationsEnabledUseCase, private val shouldSaveUserWalletsSyncUseCase: ShouldSaveUserWalletsSyncUseCase, private val getIsHuaweiDeviceWithoutGoogleServicesUseCase: GetIsHuaweiDeviceWithoutGoogleServicesUseCase, + private val hotWalletFeatureToggles: HotWalletFeatureToggles, + private val userWalletsListRepository: UserWalletsListRepository, val screenLifecycleProvider: ScreenLifecycleProvider, val innerWalletRouter: InnerWalletRouter, ) : Model() { @@ -137,7 +141,7 @@ internal class WalletModel @Inject constructor( modelScope.launch(dispatchers.main) { withContext(dispatchers.io) { delay(timeMillis = 1_800) } - if (isShowSaveWalletScreenEnabled()) { + if (shouldShowAskBiometryBottomSheet()) { innerWalletRouter.dialogNavigation.activate( configuration = WalletDialogConfig.AskForBiometry, ) @@ -159,8 +163,15 @@ internal class WalletModel @Inject constructor( } } - private suspend fun isShowSaveWalletScreenEnabled(): Boolean { - return innerWalletRouter.isWalletLastScreen() && shouldShowSaveWalletScreenUseCase() && canUseBiometryUseCase() + private suspend fun shouldShowAskBiometryBottomSheet(): Boolean { + return if (hotWalletFeatureToggles.isHotWalletEnabled) { + userWalletsListRepository.userWalletsSync().any { it is UserWallet.Cold } && + innerWalletRouter.isWalletLastScreen() && + shouldShowAskBiometryUseCase() && + canUseBiometryUseCase() + } else { + innerWalletRouter.isWalletLastScreen() && shouldShowAskBiometryUseCase() && canUseBiometryUseCase() + } } private fun subscribeToUserWalletsUpdates() = channelFlow { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletsUpdateActionResolver.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletsUpdateActionResolver.kt index 5c27b29b42..562c8a99a8 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletsUpdateActionResolver.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletsUpdateActionResolver.kt @@ -76,18 +76,19 @@ internal class WalletsUpdateActionResolver @Inject constructor( isAnyWalletNameChanged(state, wallets) -> { getRenameWalletsAction(state, wallets) } - isAnyHotWalletBackedUp(state, wallets) -> { + isAnyHotWalletBackedUpChange(state, wallets) -> { getHotWalletsBackedUpAction(state, wallets) } else -> getUpdateSelectedWalletAction(state, wallets, selectedWallet) } } - private fun isAnyHotWalletBackedUp(state: WalletScreenState, wallets: List): Boolean { + private fun isAnyHotWalletBackedUpChange(state: WalletScreenState, wallets: List): Boolean { val incompleteActivationWalletIds = state.incompleteActivationWalletIds() - return incompleteActivationWalletIds.mapNotNull { walletId -> - wallets.firstOrNull { it.walletId == walletId && it is UserWallet.Hot && it.backedUp } - }.isNotEmpty() + val walletsToUpdate = wallets.filter { + it is UserWallet.Hot && it.backedUp == incompleteActivationWalletIds.contains(it.walletId) + } + return walletsToUpdate.isNotEmpty() } private fun isWalletsCountChanged(state: WalletScreenState, wallets: List): Boolean { @@ -163,8 +164,7 @@ internal class WalletsUpdateActionResolver @Inject constructor( val incompleteActivationWalletIds = state.incompleteActivationWalletIds() val walletsToUpdate = wallets.filter { - it is UserWallet.Hot && it.backedUp && - incompleteActivationWalletIds.contains(it.walletId) + it is UserWallet.Hot && it.backedUp == incompleteActivationWalletIds.contains(it.walletId) } return Action.ReloadWarningsForWallets(walletsToUpdate) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletWarningsSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletWarningsSubscriber.kt index 1725952db7..a8c3a91d43 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletWarningsSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletWarningsSubscriber.kt @@ -13,6 +13,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.onEach internal class MultiWalletWarningsSubscriber( @@ -31,6 +32,11 @@ internal class MultiWalletWarningsSubscriber( .onEach { warnings -> val displayedState = stateHolder.getWalletState(userWallet.walletId) + // Wait until the wallet appears in the list + stateHolder.uiState.first { + it.wallets.any { walletState -> walletState.walletCardState.id == userWallet.walletId } + } + stateHolder.update(SetWarningsTransformer(userWallet.walletId, warnings)) walletWarningsAnalyticsSender.send(displayedState, warnings) walletWarningsSingleEventSender.send( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/utils/DefaultUserWalletsFetcher.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/utils/DefaultUserWalletsFetcher.kt index 6a310e5c31..1b4e9c358f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/utils/DefaultUserWalletsFetcher.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/utils/DefaultUserWalletsFetcher.kt @@ -43,7 +43,7 @@ internal class DefaultUserWalletsFetcher @AssistedInject constructor( @Assisted private val onWalletClick: (UserWalletId) -> Unit, @Assisted private val messageSender: UiMessageSender, @Assisted("onlyMultiCurrency") private val onlyMultiCurrency: Boolean, - @Assisted("authMode") private val authMode: Boolean, + @Assisted("isAuthMode") private val isAuthMode: Boolean, private val userWalletImageFetcher: UserWalletImageFetcher, dispatchers: CoroutineDispatcherProvider, ) : UserWalletsFetcher { @@ -55,7 +55,7 @@ internal class DefaultUserWalletsFetcher @AssistedInject constructor( override val userWallets: Flow> = walletsFlow.transformLatest { wallets -> val uiModels = UserWalletItemUMConverter( onClick = onWalletClick, - authMode = authMode, + isAuthMode = isAuthMode, ).convertList(wallets) .toImmutableList() @@ -64,7 +64,7 @@ internal class DefaultUserWalletsFetcher @AssistedInject constructor( combine( flow = getSelectedAppCurrencyUseCase().distinctUntilChanged(), flow2 = getBalanceHidingSettingsUseCase().distinctUntilChanged(), - flow3 = getWalletTotalBalanceUseCase(wallets.map(UserWallet::walletId)).distinctUntilChanged(), + flow3 = getTotalBalanceFlow(wallets), flow4 = userWalletImageFetcher.walletsImage(wallets, ArtworkSize.SMALL), ) { maybeAppCurrency, balanceHidingSettings, maybeBalances, artworks -> createUiModels( @@ -88,6 +88,19 @@ internal class DefaultUserWalletsFetcher @AssistedInject constructor( } .flowOn(dispatchers.default) + private fun getTotalBalanceFlow( + wallets: List, + ): Flow>> { + val walletIds = wallets.map(UserWallet::walletId) + + return if (isAuthMode) { + // We should not load balances in auth mode + flowOf(Lce.Loading(walletIds.associateWith { TotalFiatBalance.Loading })) + } else { + getWalletTotalBalanceUseCase(walletIds).distinctUntilChanged() + } + } + private fun createUiModels( wallets: List, maybeAppCurrency: Either, @@ -117,7 +130,7 @@ internal class DefaultUserWalletsFetcher @AssistedInject constructor( balance = balance, isBalanceHidden = balanceHidingSettings.isBalanceHidden, artwork = artworks[userWallet.walletId], - authMode = authMode, + isAuthMode = isAuthMode, ) .convert(userWallet) } @@ -136,7 +149,7 @@ internal class DefaultUserWalletsFetcher @AssistedInject constructor( override fun create( messageSender: UiMessageSender, @Assisted("onlyMultiCurrency") onlyMultiCurrency: Boolean, - @Assisted("authMode") authMode: Boolean, + @Assisted("isAuthMode") isAuthMode: Boolean, onWalletClick: (UserWalletId) -> Unit, ): DefaultUserWalletsFetcher } diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/utils/WcUserWalletsFetcher.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/utils/WcUserWalletsFetcher.kt index 727e85c485..dc19a55e4e 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/utils/WcUserWalletsFetcher.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/utils/WcUserWalletsFetcher.kt @@ -29,7 +29,7 @@ internal class WcUserWalletsFetcher( private val userWalletsFetcher = userWalletsFetcherFactory.create( messageSender = messageSender, onlyMultiCurrency = true, - authMode = false, + isAuthMode = false, onWalletClick = { onWalletSelected(it) }, ) diff --git a/features/welcome/impl/src/main/kotlin/com/tangem/features/welcome/impl/model/WelcomeModel.kt b/features/welcome/impl/src/main/kotlin/com/tangem/features/welcome/impl/model/WelcomeModel.kt index 3baa86b20e..d1128d02f2 100644 --- a/features/welcome/impl/src/main/kotlin/com/tangem/features/welcome/impl/model/WelcomeModel.kt +++ b/features/welcome/impl/src/main/kotlin/com/tangem/features/welcome/impl/model/WelcomeModel.kt @@ -55,7 +55,7 @@ internal class WelcomeModel @Inject constructor( private val walletsFetcher = userWalletsFetcherFactory.create( messageSender = uiMessageSender, onlyMultiCurrency = false, - authMode = true, + isAuthMode = true, onWalletClick = { walletId -> modelScope.launch { val userWallets = userWalletsListRepository.userWalletsSync()