Updated on 2026-08-14
This commit is contained in:
commit
208e63c414
73 changed files with 547 additions and 151 deletions
|
|
@ -71,7 +71,7 @@ class AppsFlyerReferralParamsHandler @Inject constructor(
|
|||
private fun storeConversionData(refcode: String, campaign: String?) {
|
||||
coroutineScope.launch {
|
||||
mutex.withLock {
|
||||
setShouldShowMobileWalletPromoUseCase()
|
||||
setShouldShowMobileWalletPromoUseCase(true)
|
||||
.onLeft { Timber.e(it) }
|
||||
appsFlyerStore.storeIfAbsent(
|
||||
value = AppsFlyerConversionData(refcode = refcode, campaign = campaign),
|
||||
|
|
|
|||
|
|
@ -2,12 +2,22 @@ package com.tangem.tap.core.security
|
|||
|
||||
import com.dexprotector.rtc.RtcStatus
|
||||
import com.tangem.security.DeviceSecurityInfoProvider
|
||||
import timber.log.Timber
|
||||
|
||||
internal class DefaultDeviceSecurityInfoProvider : DeviceSecurityInfoProvider {
|
||||
override val isRooted: Boolean
|
||||
get() = RtcStatus.getRtcStatus().root
|
||||
get() = getRtcStatusSafely()?.root == true
|
||||
override val isBootloaderUnlocked: Boolean
|
||||
get() = RtcStatus.getRtcStatus().unlockedBootloader
|
||||
get() = getRtcStatusSafely()?.unlockedBootloader == true
|
||||
override val isXposed: Boolean
|
||||
get() = RtcStatus.getRtcStatus().xposed
|
||||
get() = getRtcStatusSafely()?.xposed == true
|
||||
|
||||
private fun getRtcStatusSafely(): RtcStatus? {
|
||||
return try {
|
||||
RtcStatus.getRtcStatus()
|
||||
} catch (e: Throwable) {
|
||||
Timber.e(e)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.tangem.tap.core.ui
|
||||
|
||||
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
|
||||
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
|
||||
import javax.inject.Inject
|
||||
|
||||
class DefaultHoldToConfirmButtonFeatureToggles @Inject constructor(
|
||||
featureTogglesManager: FeatureTogglesManager,
|
||||
) : HoldToConfirmButtonFeatureToggles {
|
||||
override val isHoldToConfirmEnabled: Boolean = featureTogglesManager.isFeatureEnabled(
|
||||
"HOLD_TO_CONFIRM_BUTTON_ENABLED",
|
||||
)
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
package com.tangem.tap.di.core.ui
|
||||
|
||||
import com.tangem.core.ui.DesignFeatureToggles
|
||||
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
|
||||
import com.tangem.tap.core.ui.DefaultDesignFeatureToggles
|
||||
import com.tangem.tap.core.ui.DefaultHoldToConfirmButtonFeatureToggles
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -13,4 +15,9 @@ interface CoreUiBindsModule {
|
|||
|
||||
@Binds
|
||||
fun bindDesignFeatureToggles(impl: DefaultDesignFeatureToggles): DesignFeatureToggles
|
||||
|
||||
@Binds
|
||||
fun bindHoldToConfirmButtonFeatureToggles(
|
||||
impl: DefaultHoldToConfirmButtonFeatureToggles,
|
||||
): HoldToConfirmButtonFeatureToggles
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import com.tangem.domain.balancehiding.UpdateBalanceHidingSettingsUseCase
|
|||
import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository
|
||||
import com.tangem.domain.settings.*
|
||||
import com.tangem.domain.settings.repositories.AppRatingRepository
|
||||
import com.tangem.domain.settings.repositories.LegacySettingsRepository
|
||||
import com.tangem.domain.settings.repositories.PermissionRepository
|
||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
import com.tangem.domain.settings.usercountry.FetchUserCountryUseCase
|
||||
|
|
@ -68,10 +69,14 @@ internal object SettingsDomainModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesCanUseBiometryUseCase(tangemSdkManager: TangemSdkManager): CanUseBiometryUseCase {
|
||||
return CanUseBiometryUseCase(
|
||||
legacySettingsRepository = DefaultLegacySettingsRepository(tangemSdkManager = tangemSdkManager),
|
||||
)
|
||||
fun provideLegacySettingsRepository(tangemSdkManager: TangemSdkManager): LegacySettingsRepository {
|
||||
return DefaultLegacySettingsRepository(tangemSdkManager = tangemSdkManager)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesCanUseBiometryUseCase(legacySettingsRepository: LegacySettingsRepository): CanUseBiometryUseCase {
|
||||
return CanUseBiometryUseCase(legacySettingsRepository = legacySettingsRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
|
|||
|
|
@ -438,6 +438,7 @@ internal class DefaultTangemSdkManager(
|
|||
),
|
||||
)
|
||||
showAlert()
|
||||
break
|
||||
} else {
|
||||
delay(timeMillis = 400)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,4 +8,8 @@ internal class DefaultLegacySettingsRepository(
|
|||
) : LegacySettingsRepository {
|
||||
|
||||
override suspend fun canUseBiometry(): Boolean = tangemSdkManager.checkCanUseBiometry()
|
||||
|
||||
override suspend fun canUseBiometryStrict(): Boolean {
|
||||
return tangemSdkManager.checkCanUseBiometry() && tangemSdkManager.checkNeedEnrollBiometrics().not()
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ import com.tangem.domain.visa.model.VisaActivationRemoteState
|
|||
import com.tangem.domain.visa.model.VisaCardActivationStatus
|
||||
import com.tangem.domain.wallets.hot.HotWalletAccessCodeAttemptsRepository
|
||||
import com.tangem.domain.wallets.hot.HotWalletPasswordRequester
|
||||
import com.tangem.feature.referral.domain.MobileWalletPromoRepository
|
||||
import com.tangem.hot.sdk.TangemHotSdk
|
||||
import com.tangem.sdk.storage.AndroidSecureStorage
|
||||
import com.tangem.sdk.storage.AndroidSecureStorageV2
|
||||
|
|
@ -54,6 +55,7 @@ internal object UserWalletsListManagerModule {
|
|||
trackingContextProxy: TrackingContextProxy,
|
||||
analyticsEventHandler: AnalyticsEventHandler,
|
||||
hotWalletRepository: HotWalletRepository,
|
||||
mobileWalletPromoRepository: MobileWalletPromoRepository,
|
||||
): UserWalletsListRepository {
|
||||
val moshi = buildMoshi()
|
||||
val secureStorage = buildSecureStorage(applicationContext = applicationContext)
|
||||
|
|
@ -104,6 +106,7 @@ internal object UserWalletsListManagerModule {
|
|||
trackingContextProxy = trackingContextProxy,
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
hotWalletRepository = hotWalletRepository,
|
||||
mobileWalletPromoRepository = mobileWalletPromoRepository,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
|
|||
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
|
||||
import com.tangem.domain.wallets.hot.HotWalletAccessCodeAttemptsRepository
|
||||
import com.tangem.domain.wallets.hot.HotWalletPasswordRequester
|
||||
import com.tangem.feature.referral.domain.MobileWalletPromoRepository
|
||||
import com.tangem.hot.sdk.TangemHotSdk
|
||||
import com.tangem.hot.sdk.model.HotWalletId
|
||||
import com.tangem.sdk.api.TangemSdkManager
|
||||
|
|
@ -60,6 +61,7 @@ internal class DefaultUserWalletsListRepository(
|
|||
private val trackingContextProxy: TrackingContextProxy,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val hotWalletRepository: HotWalletRepository,
|
||||
private val mobileWalletPromoRepository: MobileWalletPromoRepository,
|
||||
) : UserWalletsListRepository {
|
||||
|
||||
override val userWallets = MutableStateFlow<List<UserWallet>?>(null)
|
||||
|
|
@ -144,6 +146,8 @@ internal class DefaultUserWalletsListRepository(
|
|||
raise(SaveWalletError.WalletAlreadySaved(messageId = R.string.user_wallet_list_error_wallet_already_saved))
|
||||
}
|
||||
|
||||
val isFirstWallet = userWallets.value?.isEmpty() == true
|
||||
|
||||
if (savePersistentInformation()) {
|
||||
publicInformationRepository.save(userWallet, canOverride)
|
||||
if (userWallet.isLocked.not()) {
|
||||
|
|
@ -166,6 +170,10 @@ internal class DefaultUserWalletsListRepository(
|
|||
wallets.addOrReplace(userWallet) { it.walletId == userWallet.walletId }
|
||||
}
|
||||
|
||||
if (isFirstWallet) {
|
||||
onFirstWalletCreated()
|
||||
}
|
||||
|
||||
// update the selectedUserWallet state if it is the only wallet
|
||||
if (userWallets.value?.size == 1) {
|
||||
selectedUserWalletRepository.set(userWallet.walletId)
|
||||
|
|
@ -239,6 +247,9 @@ internal class DefaultUserWalletsListRepository(
|
|||
val newSelected = updatedWallets?.findAvailableUserWallet(
|
||||
currentWallets.indexOfFirstOrNull { it.walletId == currentSelected.walletId } ?: 0,
|
||||
)
|
||||
if (newSelected == null) {
|
||||
onAllWalletsDeleted()
|
||||
}
|
||||
selectedUserWalletRepository.set(newSelected?.walletId)
|
||||
newSelected
|
||||
}
|
||||
|
|
@ -595,4 +606,14 @@ internal class DefaultUserWalletsListRepository(
|
|||
private fun trackWalletUpgradeEvent() {
|
||||
analyticsEventHandler.send(event = WalletSettingsAnalyticEvents.WalletUpgraded())
|
||||
}
|
||||
|
||||
private suspend fun onFirstWalletCreated() {
|
||||
// reset flag (that is set from AF deeplink) after creating a new wallet
|
||||
mobileWalletPromoRepository.setShouldShowMobileWalletPromo(false)
|
||||
}
|
||||
|
||||
private suspend fun onAllWalletsDeleted() {
|
||||
// reset flag (that is set from AF deeplink) after removing the last wallet
|
||||
mobileWalletPromoRepository.setShouldShowMobileWalletPromo(false)
|
||||
}
|
||||
}
|
||||
|
|
@ -30,10 +30,14 @@ class RootDetectedWarningComponent @AssistedInject constructor(
|
|||
|
||||
private val isShown = instanceKeeper.getOrCreateSimple { MutableStateFlow(false) }
|
||||
|
||||
suspend fun shouldShowWarning(): Boolean {
|
||||
return settingsRepository.isRootDetectedWarningShown().not() && securityInfoProvider.isSecurityExposed()
|
||||
}
|
||||
|
||||
suspend fun tryToShowWarningAndWaitContinuation() {
|
||||
if (isShown.value) return
|
||||
|
||||
if (settingsRepository.isRootDetectedWarningShown().not() && securityInfoProvider.isSecurityExposed()) {
|
||||
if (shouldShowWarning()) {
|
||||
isShown.value = true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -139,9 +139,15 @@ internal class DefaultRoutingComponent @AssistedInject constructor(
|
|||
private fun initializeInitialNavigation() {
|
||||
if (initialStack.isNullOrEmpty()) {
|
||||
componentScope.launch {
|
||||
rootDetectedWarningComponent.tryToShowWarningAndWaitContinuation()
|
||||
val initialRoute = resolveInitialRoute()
|
||||
router.replaceAll(initialRoute)
|
||||
if (rootDetectedWarningComponent.shouldShowWarning()) {
|
||||
launch(dispatchers.main) {
|
||||
rootDetectedWarningComponent.tryToShowWarningAndWaitContinuation()
|
||||
router.replaceAll(initialRoute)
|
||||
}
|
||||
} else {
|
||||
router.replaceAll(initialRoute)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class AppsFlyerReferralParamsHandlerTest {
|
|||
|
||||
private val appsFlyerStore: AppsFlyerStore = mockk(relaxUnitFun = true)
|
||||
private val setShouldShowMobileWalletPromoUseCase: SetShouldShowMobileWalletPromoUseCase = mockk {
|
||||
coEvery { this@mockk.invoke() } returns Unit.right()
|
||||
coEvery { this@mockk.invoke(true) } returns Unit.right()
|
||||
}
|
||||
private val handler = AppsFlyerReferralParamsHandler(
|
||||
appsFlyerStore = appsFlyerStore,
|
||||
|
|
|
|||
|
|
@ -75,6 +75,10 @@
|
|||
"name": "EARN_BLOCK_ENABLED",
|
||||
"version": "undefined"
|
||||
},
|
||||
{
|
||||
"name": "HOLD_TO_CONFIRM_BUTTON_ENABLED",
|
||||
"version": "undefined"
|
||||
},
|
||||
{
|
||||
"name": "WALLET_REORDER_FEATURE_ENABLED",
|
||||
"version": "undefined"
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ internal object BlockchainSDKConfigConverter : Converter<EnvironmentConfigModel,
|
|||
polygonScanApiKey = value.polygonScanApiKey,
|
||||
bittensorDwellirApiKey = value.bittensorDwellirApiKey,
|
||||
bittensorOnfinalityApiKey = value.bittensorOnfinalityKey,
|
||||
dwellirApiKey = value.dwellirApiKey,
|
||||
koinosProApiKey = value.koinosProApiKey,
|
||||
alephiumApiKey = value.alephiumTangemApiKey,
|
||||
moralisApiKey = value.moralisApiKey,
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ class EnvironmentConfigModel(
|
|||
@Json(name = "p2pApiKey") val p2pApiKey: P2PKeys?,
|
||||
@Json(name = "bittensorDwellirKey") val bittensorDwellirApiKey: String?,
|
||||
@Json(name = "bittensorOnfinalityKey") val bittensorOnfinalityKey: String?,
|
||||
@Json(name = "dwellirApiKey") val dwellirApiKey: String?,
|
||||
@Json(name = "koinosProApiKey") val koinosProApiKey: String?,
|
||||
@Json(name = "alephiumTangemApiKey") val alephiumTangemApiKey: String?,
|
||||
@Json(name = "moralisApiKey") val moralisApiKey: String?,
|
||||
|
|
|
|||
|
|
@ -1484,10 +1484,12 @@
|
|||
<string name="tangem_pay_status_completed">Abgeschlossen</string>
|
||||
<string name="tangem_pay_status_declined">Abgelehnt</string>
|
||||
<string name="tangem_pay_status_pending">Ausstehend</string>
|
||||
<string name="tangem_pay_status_reversed">Storniert</string>
|
||||
<string name="tangem_pay_terms_fees_limits">Bedingungen, Gebühren & Limits</string>
|
||||
<string name="tangem_pay_terms_limits">Bedingungen und Einschränkungen</string>
|
||||
<string name="tangem_pay_transaction_declined_notification_text">Die Bank hat diese Transaktionsanfrage abgelehnt.</string>
|
||||
<string name="tangem_pay_transaction_fee_notification_text">Diese Gebühr dient zur Deckung der Kosten für die Abwicklung Deiner Überweisung.Questa commissione copre il costo della gestione del tuo trasferimento.</string>
|
||||
<string name="tangem_pay_transaction_fee_notification_text">Diese Gebühr dient zur Deckung der Kosten für die Abwicklung Deiner Überweisung.</string>
|
||||
<string name="tangem_pay_transaction_reversed_notification_text">Die Transaktion wurde vom Händler teilweise oder vollständig storniert</string>
|
||||
<string name="tangem_pay_unfreeze_card_alert_body">Nutze Dein Geld weiterhin. Du kannst es jederzeit einfrieren.</string>
|
||||
<string name="tangem_pay_unfreeze_card_alert_title">Karte entsperren?</string>
|
||||
<string name="tangem_pay_unfreeze_card_failed">Entsperren der Karte fehlgeschlagen. Versuchen Sie es später erneut.</string>
|
||||
|
|
|
|||
|
|
@ -1481,10 +1481,12 @@
|
|||
<string name="tangem_pay_status_completed">Completado</string>
|
||||
<string name="tangem_pay_status_declined">Rechazado</string>
|
||||
<string name="tangem_pay_status_pending">Pendiente</string>
|
||||
<string name="tangem_pay_status_reversed">Revertida</string>
|
||||
<string name="tangem_pay_terms_fees_limits">Términos, tarifas y límites</string>
|
||||
<string name="tangem_pay_terms_limits">Términos y límites</string>
|
||||
<string name="tangem_pay_transaction_declined_notification_text">El banco rechazó esta solicitud de transacción.</string>
|
||||
<string name="tangem_pay_transaction_fee_notification_text">Esta tarifa cubre el costo de procesar tu transferencia.</string>
|
||||
<string name="tangem_pay_transaction_reversed_notification_text">La transacción fue revertida parcial o totalmente por el comerciante</string>
|
||||
<string name="tangem_pay_unfreeze_card_alert_body">Sigue usando tu dinero. Puedes congelarlo en cualquier momento.</string>
|
||||
<string name="tangem_pay_unfreeze_card_alert_title">¿Descongelar tu tarjeta?</string>
|
||||
<string name="tangem_pay_unfreeze_card_failed">No se pudo descongelar la tarjeta. Inténtalo de nuevo más tarde.</string>
|
||||
|
|
|
|||
|
|
@ -1487,10 +1487,12 @@
|
|||
<string name="tangem_pay_status_completed">Terminé</string>
|
||||
<string name="tangem_pay_status_declined">Refusé</string>
|
||||
<string name="tangem_pay_status_pending">En attente</string>
|
||||
<string name="tangem_pay_status_reversed">Annulée</string>
|
||||
<string name="tangem_pay_terms_fees_limits">Conditions, frais et limites</string>
|
||||
<string name="tangem_pay_terms_limits">Conditions et limites</string>
|
||||
<string name="tangem_pay_transaction_declined_notification_text">La banque a rejeté cette demande de transaction.</string>
|
||||
<string name="tangem_pay_transaction_fee_notification_text">Ces frais couvrent le coût du traitement de votre virement.</string>
|
||||
<string name="tangem_pay_transaction_reversed_notification_text">La transaction a été partiellement ou totalement annulée par le commerçant</string>
|
||||
<string name="tangem_pay_unfreeze_card_alert_body">Continuez à utiliser votre argent. Vous pouvez le geler à tout moment.</string>
|
||||
<string name="tangem_pay_unfreeze_card_alert_title">Dégeler votre carte ?</string>
|
||||
<string name="tangem_pay_unfreeze_card_failed">Échec du dégel de la carte. Réessayez plus tard.</string>
|
||||
|
|
|
|||
|
|
@ -87,10 +87,12 @@
|
|||
<string name="tangem_pay_status_completed">Completato</string>
|
||||
<string name="tangem_pay_status_declined">Rifiutato</string>
|
||||
<string name="tangem_pay_status_pending">In sospeso</string>
|
||||
<string name="tangem_pay_status_reversed">Stornata</string>
|
||||
<string name="tangem_pay_terms_fees_limits">Termini, commissioni e limiti</string>
|
||||
<string name="tangem_pay_terms_limits">Termini e limiti</string>
|
||||
<string name="tangem_pay_transaction_declined_notification_text">La banca ha rifiutato questa richiesta di transazione.</string>
|
||||
<string name="tangem_pay_transaction_fee_notification_text">Questa commissione copre il costo della gestione del tuo trasferimento.</string>
|
||||
<string name="tangem_pay_transaction_reversed_notification_text">La transazione è stata parzialmente o totalmente stornata dal commerciante</string>
|
||||
<string name="tangem_pay_unfreeze_card_alert_body">Continua a usare i tuoi soldi. Puoi congelarli in qualsiasi momento.</string>
|
||||
<string name="tangem_pay_unfreeze_card_alert_title">Sbloccare la tua carta?</string>
|
||||
<string name="tangem_pay_unfreeze_card_failed">Impossibile sbloccare la carta. Riprova più tardi.</string>
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@
|
|||
<string name="address_qr_code_message_format">このアドレスには%3$s ネットワークから%1$s (%2$s) のみを送信してください。他のトークンやネットワークを使用すると、資金を失う可能性があります。</string>
|
||||
<string name="address_type_default">デフォルト</string>
|
||||
<string name="address_type_legacy">レガシー</string>
|
||||
<string name="alert_authentication_error_message">デバイスの生体認証をリセットするか、サポートにお問い合わせください</string>
|
||||
<string name="alert_authentication_error_message">生体認証で問題が発生しました。端末の生体認証をリセットするか、サポートにお問い合わせください。</string>
|
||||
<string name="alert_authentication_error_title">認証エラー</string>
|
||||
<string name="alert_button_how_to_scan">スキャン方法</string>
|
||||
<string name="alert_button_request_support">サポートをリクエストする</string>
|
||||
|
|
@ -461,9 +461,15 @@
|
|||
<string name="domain_receive_assets_onboarding_network_name">%sネットワーク</string>
|
||||
<string name="domain_receive_assets_onboarding_title">下記のみを使用して資金を送金する</string>
|
||||
<string name="earn_best_opportunities">おすすめ</string>
|
||||
<string name="earn_clear_filter">絞り込みを解除</string>
|
||||
<string name="earn_empty">リストは現在更新中のため、一時的に空になっています。しばらくしてからご確認ください。</string>
|
||||
<string name="earn_filter_all_networks">すべてのネットワーク</string>
|
||||
<string name="earn_filter_all_types">すべての種類</string>
|
||||
<string name="earn_filter_by">絞り込み</string>
|
||||
<string name="earn_filter_my_networks">マイネットワーク</string>
|
||||
<string name="earn_filter_networks">ネットワーク</string>
|
||||
<string name="earn_mostly_used">よく使われています</string>
|
||||
<string name="earn_no_results">該当する結果はありません</string>
|
||||
<string name="earn_title">運用</string>
|
||||
<string name="email_preface_wc_error">こんにちは、サポートチームの皆さん、コード %s のエラーが発生しました。</string>
|
||||
<string name="email_subject_wc_error">WalletConnectエラー</string>
|
||||
|
|
@ -814,6 +820,7 @@
|
|||
<string name="markets_token_details_volume">取引量</string>
|
||||
<string name="markets_tooltip_message">これをドラッグするか、検索窓をタップして、マーケットから直接トークンを追加します</string>
|
||||
<string name="markets_tooltip_title">トークンを追加</string>
|
||||
<string name="markets_tooltip_v2_title">トークンを追加</string>
|
||||
<string name="markets_yield_supply_banner_description">資産を常時アクセス可能な状態に保ったまま、パワーアップさせよう。%s</string>
|
||||
<string name="markets_yield_supply_banner_title">利息モードを有効にする</string>
|
||||
<string name="mobile_wallet_requires_min_os_warning_body">モバイルウォレットを作成するには、%1$sにアップデートする必要があります</string>
|
||||
|
|
@ -827,7 +834,7 @@
|
|||
<item quantity="other">%d分前</item>
|
||||
</plurals>
|
||||
<string name="news_quick_recap">クイックまとめ</string>
|
||||
<string name="news_related_news">関連ニュース</string>
|
||||
<string name="news_related_news">ニュース</string>
|
||||
<string name="news_related_tokens">関連トークン</string>
|
||||
<string name="news_sources">関連ニュース</string>
|
||||
<string name="news_stay_in_the_loop">最新情報を入手</string>
|
||||
|
|
@ -1462,10 +1469,12 @@
|
|||
<string name="tangem_pay_status_completed">完了</string>
|
||||
<string name="tangem_pay_status_declined">拒否</string>
|
||||
<string name="tangem_pay_status_pending">保留中</string>
|
||||
<string name="tangem_pay_status_reversed">取消済み</string>
|
||||
<string name="tangem_pay_terms_fees_limits">利用規約・手数料・利用制限</string>
|
||||
<string name="tangem_pay_terms_limits">利用規約と上限条件</string>
|
||||
<string name="tangem_pay_transaction_declined_notification_text">銀行がこの取引リクエストを拒否しました。</string>
|
||||
<string name="tangem_pay_transaction_fee_notification_text">この手数料は、送金処理にかかるコストをカバーするためのものです。</string>
|
||||
<string name="tangem_pay_transaction_reversed_notification_text">この取引は加盟店により一部または全額取り消されました</string>
|
||||
<string name="tangem_pay_unfreeze_card_alert_body">資金は引き続き使用できます。いつでも一時停止できます。</string>
|
||||
<string name="tangem_pay_unfreeze_card_alert_title">カードの一時停止を解除しますか?</string>
|
||||
<string name="tangem_pay_unfreeze_card_failed">カードの凍結解除に失敗しました。しばらくしてからもう一度お試しください。</string>
|
||||
|
|
|
|||
|
|
@ -485,6 +485,7 @@
|
|||
<string name="earn_filter_all_networks">Все сети</string>
|
||||
<string name="earn_filter_all_types">Все типы</string>
|
||||
<string name="earn_filter_by">Фильтровать по</string>
|
||||
<string name="earn_filter_my_networks">Мои сети</string>
|
||||
<string name="earn_filter_networks">Сети</string>
|
||||
<string name="earn_mostly_used">Часто используемые</string>
|
||||
<string name="earn_no_results">Нет результата</string>
|
||||
|
|
@ -843,6 +844,7 @@
|
|||
<string name="markets_token_details_volume">Объем</string>
|
||||
<string name="markets_tooltip_message">Потяните вверх или коснитесь поисковой строки, чтобы добавить токены напрямую из рынка</string>
|
||||
<string name="markets_tooltip_title">Добавить токены</string>
|
||||
<string name="markets_tooltip_v2_title">Добавить токены</string>
|
||||
<string name="markets_yield_supply_banner_description">Увеличивайте доход с активов, сохраняя мгновенный доступ к ним. %s</string>
|
||||
<string name="markets_yield_supply_banner_title">Активировать режим доходности</string>
|
||||
<string name="mobile_wallet_requires_min_os_warning_body">Обновитесь до версии %1$s, чтобы создать мобильный кошелёк</string>
|
||||
|
|
@ -1514,10 +1516,12 @@
|
|||
<string name="tangem_pay_status_completed">Успешно завершено</string>
|
||||
<string name="tangem_pay_status_declined">Отклонено</string>
|
||||
<string name="tangem_pay_status_pending">В процессе</string>
|
||||
<string name="tangem_pay_status_reversed">Возврат</string>
|
||||
<string name="tangem_pay_terms_fees_limits">Тарифы и полные условия</string>
|
||||
<string name="tangem_pay_terms_limits">Тарифы и лимиты</string>
|
||||
<string name="tangem_pay_transaction_declined_notification_text">Банк отклонил транзакцию</string>
|
||||
<string name="tangem_pay_transaction_fee_notification_text">Эта комиссия покрывает стоимость обработки вашего перевода.</string>
|
||||
<string name="tangem_pay_transaction_reversed_notification_text">Транзакция частично или полностью возвращена продавцом</string>
|
||||
<string name="tangem_pay_unfreeze_card_alert_body">Продолжайте пользоваться картой, заморозить всегда успеете</string>
|
||||
<string name="tangem_pay_unfreeze_card_alert_title">Разморозить карту?</string>
|
||||
<string name="tangem_pay_unfreeze_card_failed">Не удалось разморозить карту, попробуйте еще раз</string>
|
||||
|
|
@ -1928,6 +1932,7 @@
|
|||
<string name="wc_alert_unknown_error_title">Мы обнаружили неизвестную ошибку</string>
|
||||
<string name="wc_alert_unsupported_dapps_description">Кошелек Tangem в настоящий момент не поддерживает %s</string>
|
||||
<string name="wc_alert_unsupported_dapps_title">Неподдерживаемый dApp</string>
|
||||
<string name="wc_alert_unsupported_method_description">Код ошибки: 8 005.\nЕсли проблема сохраняется — пожалуйста, свяжитесь с нашей службой поддержки.</string>
|
||||
<string name="wc_alert_unsupported_method_title">Мы обнаружили неизвестную ошибку</string>
|
||||
<string name="wc_alert_unsupported_network_description">Эта сеть %s не поддерживается Tangem Wallet и не может быть подключена.</string>
|
||||
<string name="wc_alert_unsupported_network_title">Неподдерживаемая сеть</string>
|
||||
|
|
|
|||
|
|
@ -311,6 +311,7 @@
|
|||
<string name="common_go_to_token">Перейти до токену</string>
|
||||
<string name="common_got_it">Зrozуміло</string>
|
||||
<string name="common_hide">Приховати</string>
|
||||
<string name="common_hold_to">Утримуйте, щоб %s</string>
|
||||
<string name="common_hour">година</string>
|
||||
<string name="common_import">Імпортувати</string>
|
||||
<string name="common_in_progress">В процесі</string>
|
||||
|
|
@ -337,10 +338,12 @@
|
|||
<string name="common_no">Ні</string>
|
||||
<string name="common_no_address">Немає адреси</string>
|
||||
<string name="common_not_added">Не додано</string>
|
||||
<string name="common_not_available">Недоступно</string>
|
||||
<string name="common_not_now">Не зараз</string>
|
||||
<string name="common_now">Зараз</string>
|
||||
<string name="common_ok">ОК</string>
|
||||
<string name="common_open_in_browser">Відкрити в браузері</string>
|
||||
<string name="common_or">або</string>
|
||||
<string name="common_origin_card">Основна картка</string>
|
||||
<string name="common_origin_ring">Основне кільце</string>
|
||||
<string name="common_passphrase">Парольна фраза</string>
|
||||
|
|
@ -386,6 +389,7 @@
|
|||
<string name="common_swap">Обмін</string>
|
||||
<string name="common_tangem">Tangem</string>
|
||||
<string name="common_tangem_wallet">Tangem Wallet</string>
|
||||
<string name="common_tap_and_hold_hint">Натисніть і утримуйте</string>
|
||||
<string name="common_terms_and_conditions">умови участі</string>
|
||||
<string name="common_terms_of_use">Умовами використання</string>
|
||||
<string name="common_to">До</string>
|
||||
|
|
@ -453,6 +457,7 @@
|
|||
<string name="details_manage_security_long_tap_description">Цей механізм захищає картку або кільце від безконтактних атак. Між скануванням картки та виконанням команди буде додана затримка.</string>
|
||||
<string name="details_manage_security_passcode">Пароль</string>
|
||||
<string name="details_manage_security_passcode_description">Перед виконанням будь-якої команди, що тягне за собою зміну стану картки, вам необхідно буде ввести пароль.</string>
|
||||
<string name="details_mobile_wallet_upgrade_action_title">Оновлення до апаратного гаманця</string>
|
||||
<string name="details_nft_title">NFT</string>
|
||||
<string name="details_referral_title">Реферальна програма</string>
|
||||
<string name="details_row_description_flip_to_hide">Переверніть екран пристрою вниз, щоб швидко приховати та відобразити баланси</string>
|
||||
|
|
@ -466,6 +471,7 @@
|
|||
<string name="details_row_title_signed_hashes">Підписано</string>
|
||||
<string name="details_send_feedback">Надіслати відгук</string>
|
||||
<string name="details_title">Деталі</string>
|
||||
<string name="details_wallets_section_description">Ви можете мати лише один мобільний гаманець. Оновіть його до апаратного гаманця Tangem або додайте новий апаратний гаманець.</string>
|
||||
<string name="disclaimer_error_loading">Перевірте підключення до інтернету або змініть мережу</string>
|
||||
<string name="disclaimer_title">Умови використання</string>
|
||||
<string name="domain_receive_assets_default_address">Основна адреса</string>
|
||||
|
|
@ -475,6 +481,15 @@
|
|||
<string name="domain_receive_assets_onboarding_description">Надсилання активів в інші мережі призведе до безповоротної втрати.</string>
|
||||
<string name="domain_receive_assets_onboarding_network_name">%s мережа</string>
|
||||
<string name="domain_receive_assets_onboarding_title">Надсилайте кошти, використовуючи лише</string>
|
||||
<string name="earn_best_opportunities">Найкращі можливості</string>
|
||||
<string name="earn_clear_filter">Очистити фільтр</string>
|
||||
<string name="earn_filter_all_networks">Всі мережі</string>
|
||||
<string name="earn_filter_all_types">Всі типи</string>
|
||||
<string name="earn_filter_by">Фільтрувати за</string>
|
||||
<string name="earn_filter_my_networks">Мої мережі</string>
|
||||
<string name="earn_filter_networks">Мережі</string>
|
||||
<string name="earn_mostly_used">Часто використовані</string>
|
||||
<string name="earn_no_results">Немає результатів</string>
|
||||
<string name="email_preface_wc_error">Привіт, команда підтримки, я зіткнувся з помилкою з кодом: %s</string>
|
||||
<string name="email_subject_wc_error">Помилка WalletConnect</string>
|
||||
<string name="error_wrong_wallet_tapped">Ви використали картку або кільце від іншого гаманця. Прикладіть картку або кільце, пов\'язану з цим гаманцем.</string>
|
||||
|
|
@ -555,6 +570,10 @@
|
|||
<string name="express_transaction_id">ID: %s</string>
|
||||
<string name="express_transaction_id_copied">ID транзакції скопійовано</string>
|
||||
<string name="exсhange_token_description">Обміняйте будь-який актив у своєму портфелі на цей токен</string>
|
||||
<string name="fee_selector_choose_speed_description">Більш вища швидкість означає більш швидше підтвердження, але і більш вищу мережеву комісію. %s</string>
|
||||
<string name="fee_selector_choose_speed_title">Обрати швидкість</string>
|
||||
<string name="fee_selector_choose_token_description">Оберіть, який токен буде використовуватися для оплати мережевої комісії. %s</string>
|
||||
<string name="fee_selector_choose_token_title">Обрати токен</string>
|
||||
<string name="feed_market_and_news">Ринок та Новини</string>
|
||||
<string name="feed_trending_now">В тренді</string>
|
||||
<string name="feedback_data_collection_message">Інформація нижче не є обов\'язковою. Ви можете стерти її, якщо бажаєте.</string>
|
||||
|
|
@ -570,6 +589,8 @@
|
|||
<string name="feedback_subject_support_tangem">Звернення в підтримку Tangem</string>
|
||||
<string name="feedback_subject_tx_failed">Не вдається відправити транзакцію</string>
|
||||
<string name="feedback_token_description_error">Помилка в описі монети</string>
|
||||
<string name="gasless_not_enough_funds_to_cover_token_fee">Недостатньо коштів</string>
|
||||
<string name="gasless_transaction_fee">Комісія за транзакцію</string>
|
||||
<string name="generic_error">Виникла помилка</string>
|
||||
<string name="generic_error_code">Виникла помилка. Код: %s.</string>
|
||||
<string name="generic_requires_memo_error">Вимагається memo</string>
|
||||
|
|
@ -619,15 +640,20 @@
|
|||
<string name="hw_backup_need_title">Спочатку завершіть резервне копіювання</string>
|
||||
<string name="hw_backup_no_backup">Не завершено</string>
|
||||
<string name="hw_backup_section_other_title">Інші методи</string>
|
||||
<string name="hw_backup_seed_code_description">Збережіть фразу відновлення в безпечному місці та тримайте її в таємниці, щоб захистити свої кошти, а також налаштуйте код доступу для додаткової безпеки.</string>
|
||||
<string name="hw_backup_seed_description">Збережіть фразу відновлення у безпечному місці і тримайте її у таємниці.</string>
|
||||
<string name="hw_backup_seed_title">Фраза відновлення</string>
|
||||
<string name="hw_backup_to_secure_description">Щоб захистити свій гаманець за допомогою коду доступу, завершіть процес резервного копіювання.</string>
|
||||
<string name="hw_backup_to_upgrade_description">Щоб покращити гаманець до апаратного, спочатку створіть резервну копію.</string>
|
||||
<string name="hw_backup_upgrade_description">Оновіть свій мобільний гаманець до апаратного гаманця Tangem для найвищого рівня безпеки. Імпортуйте існуючий гаманець або створіть новий.</string>
|
||||
<string name="hw_backup_upgrade_title">Оновлення до холодного гаманця</string>
|
||||
<string name="hw_create_keys_description">Ваші приватні ключі надійно зашифровані та зберігаються на вашому телефоні</string>
|
||||
<string name="hw_create_keys_title">Ключі зберігаються у застосунку</string>
|
||||
<string name="hw_create_seed_description">Створіть або відновіть свій гаманець за допомогою вашої фрази відновлення.</string>
|
||||
<string name="hw_create_seed_title">Резервна копія</string>
|
||||
<string name="hw_create_title">Мобільний гаманець</string>
|
||||
<string name="hw_create_upgrade_description">Перемістіть свій мобільний гаманець на Tangem картку або кільце у будь який час.</string>
|
||||
<string name="hw_create_upgrade_title">Оновлення до апаратного гаманця</string>
|
||||
<string name="hw_import_existing_wallet">Імпортувати існуючий гаманець</string>
|
||||
<string name="hw_import_seed_phrase_already_imported">Ця фраза відновлення вже була імпортована</string>
|
||||
<string name="hw_mobile_wallet">Мобільний гаманець</string>
|
||||
|
|
@ -821,6 +847,7 @@
|
|||
<string name="markets_token_details_volume">Обсяг</string>
|
||||
<string name="markets_tooltip_message">Потягніть вгору або торкніться панелі пошуку, щоб додати токени безпосередньо з маркету</string>
|
||||
<string name="markets_tooltip_title">Додати токени</string>
|
||||
<string name="markets_tooltip_v2_title">Додати токени</string>
|
||||
<string name="markets_yield_supply_banner_description">Збільшуйте дохід з активів, зберігаючи миттєвий доступ до них. %s</string>
|
||||
<string name="markets_yield_supply_banner_title">Активувати режим дохідності</string>
|
||||
<string name="mobile_wallet_requires_min_os_warning_body">Оновіться до версії %1$s, щоб створити мобільний гаманець</string>
|
||||
|
|
@ -1017,6 +1044,7 @@
|
|||
<string name="onboarding_wallet_info_title_fourth">Відновлення коду доступу</string>
|
||||
<string name="onboarding_wallet_info_title_second">Ідентичні картки</string>
|
||||
<string name="onboarding_wallet_info_title_third">Код доступу</string>
|
||||
<string name="only_one_mobile_wallet_explanation">Ви можете мати лише один мобільний гаманець. Оновіть його до апаратного гаманця Tangem або додайте новий апаратний гаманець.</string>
|
||||
<string name="onramp_all_offers_button_title">Усі пропозиції</string>
|
||||
<string name="onramp_avaiable_with_payment_methods">Доступно з %s</string>
|
||||
<string name="onramp_choose_provider_title_hint">Tangem забезпечує доступ к покупці через сторонніх провайдерів згідно з їхніми умовами</string>
|
||||
|
|
@ -1131,6 +1159,7 @@
|
|||
<string name="reset_card_to_factory_button_title">Скинути картку</string>
|
||||
<string name="reset_card_to_factory_condition_1">Я розумію, що після виконання цієї дії у мене більше не буде доступу до поточного гаманця</string>
|
||||
<string name="reset_card_to_factory_condition_2">Я розумію, що не можу використати цю картку для відновлення свого коду доступу на інших картках поточного гаманця</string>
|
||||
<string name="reset_card_to_factory_condition_3">Я розумію, що повністю втрачу доступ до своєї картки Tangem Pay та всіх коштів на ній без можливості відновлення.</string>
|
||||
<string name="reset_card_with_backup_to_factory_message">Скидання до заводських налаштувань призведе до повного видалення гаманця з обраної картки або кільця. Ви не зможете відновити поточний гаманець або використати цю картку або кільце для відновлення коду доступу.</string>
|
||||
<string name="reset_card_without_backup_to_factory_message">Скидання до заводських налаштувань призведе до повного видалення гаманця з обраної картки або кільця. Ви не зможете відновити поточний гаманець.</string>
|
||||
<string name="reset_cards_dialog_complete_description">Усі пристрої Tangem було скинуто.</string>
|
||||
|
|
@ -1279,6 +1308,11 @@
|
|||
<string name="settings_card_settings_footer">Підготуйтеся до сканування кільця або картки, яку ви хочете налаштувати.</string>
|
||||
<string name="settings_forget_wallet">Забути гаманець</string>
|
||||
<string name="settings_forget_wallet_footer">Це призведе до видалення гаманця з застосунку. Сам гаманець можна додати знову.</string>
|
||||
<string name="settings_upgrade_banner_advantage_feature_title">Простий у використанні</string>
|
||||
<string name="settings_upgrade_banner_description">Зберігає ваші криптовалюти в безпеці та офлайн. Тонкий, як кредитна картка, безпечніший за банківське сховище.</string>
|
||||
<string name="settings_upgrade_banner_seed_phrase_feature_title">Без seed-фрази</string>
|
||||
<string name="settings_upgrade_banner_superiority_feature_title">Найкращий у своєму класі</string>
|
||||
<string name="settings_upgrade_banner_title">Холодний гаманець Tangem</string>
|
||||
<string name="settings_wallet_name_title">Ім\'я</string>
|
||||
<string name="stake_token_description">Змусьте свій токен працювати</string>
|
||||
<string name="staking_account_initialization_footer">Мережева комісія — це невелика оплата, необхідна для обробки та підтвердження вашої транзакції в блокчейні.</string>
|
||||
|
|
@ -1444,6 +1478,7 @@
|
|||
<string name="swap_story_third_subtitle">Просто та зручно — обмін токенів в декілька дотиків</string>
|
||||
<string name="swap_story_third_title">Легше, ніж будь-коли</string>
|
||||
<string name="swap_via_provider">Обмін через провайдера</string>
|
||||
<string name="swap_your_assets_title">Ваші активи</string>
|
||||
<string name="swapping_alert_cex_description">Сума включає: \n• комісію постачальника послуг\n• комісію мережі за відправлення %s з біржі назад на адресу користувача.</string>
|
||||
<string name="swapping_alert_cex_description_with_slippage">У суму входить:\n- комісія провайдера\n- мережева комісія за відправку %1$s з біржі назад на адресу користувача. \n\nПроскакування провайдера становить до %2$s</string>
|
||||
<string name="swapping_alert_dex_description">Сума включає комісію постачальника послуг.</string>
|
||||
|
|
@ -1458,6 +1493,7 @@
|
|||
<string name="swapping_insufficient_funds">Недостатньо коштів</string>
|
||||
<string name="swapping_permission_header">Надати дозвіл</string>
|
||||
<string name="swapping_swap_action">Обміняти</string>
|
||||
<string name="swapping_swap_action_in_progress">Обмін...</string>
|
||||
<string name="swapping_to_title">Ви отримаєте</string>
|
||||
<string name="swapping_token_list_title">Оберіть токен</string>
|
||||
<string name="swapping_token_not_available">недоступно</string>
|
||||
|
|
@ -1476,14 +1512,18 @@
|
|||
<string name="tangem_pay_freeze_card_freeze">Заморозити</string>
|
||||
<string name="tangem_pay_freeze_card_success">Вашу картку заморожено.</string>
|
||||
<string name="tangem_pay_get_help">Звернутися до підтримки</string>
|
||||
<string name="tangem_pay_history_item_spend_mc_declined_reason">Причина: %s</string>
|
||||
<string name="tangem_pay_other">Інше</string>
|
||||
<string name="tangem_pay_rooted_device_subtitle">Неможливо використовувати на пристроях з root-правами.</string>
|
||||
<string name="tangem_pay_status_completed">Завершено</string>
|
||||
<string name="tangem_pay_status_declined">Відхилено</string>
|
||||
<string name="tangem_pay_status_pending">В очікуванні</string>
|
||||
<string name="tangem_pay_status_reversed">Скасовано</string>
|
||||
<string name="tangem_pay_terms_fees_limits">Умови, комісії та ліміти</string>
|
||||
<string name="tangem_pay_terms_limits">Умови та обмеження</string>
|
||||
<string name="tangem_pay_transaction_declined_notification_text">Банк відхилив цей запит на транзакцію.</string>
|
||||
<string name="tangem_pay_transaction_fee_notification_text">Ця комісія покриває витрати на обробку вашого переказу.</string>
|
||||
<string name="tangem_pay_transaction_reversed_notification_text">Транзакцію було частково або повністю скасовано продавцем</string>
|
||||
<string name="tangem_pay_unfreeze_card_alert_body">Продовжуйте користуватися карткою. Заморозити можна в будь-який момент.</string>
|
||||
<string name="tangem_pay_unfreeze_card_alert_title">Розморозити картку?</string>
|
||||
<string name="tangem_pay_unfreeze_card_failed">Не вдалося розморозити картку. Спробуйте пізніше.</string>
|
||||
|
|
@ -1650,6 +1690,7 @@
|
|||
<string name="universal_error">Виникла помилка. Код помилки: %s. Спробуйте, будь ласка, знову. Якщо проблема знову виникає — зверніться до нашої служби підтримки.</string>
|
||||
<string name="unlock_wallet_description_full">Використовуйте %s або відскануйте картку/кільце, щоб отримати доступ до свого гаманця</string>
|
||||
<string name="unsupported_wc_version">Не вдалося встановити з\'єднання: Цей dApp використовує Wallet Connect версії 1.0, яка не підтримується. Будь ласка, переконайтеся, що dApp підтримує Wallet Connect версії 2.0 для успішного підключення.</string>
|
||||
<string name="upgrade_to_hardware_wallet_button_title">Оновлення до апаратного гаманця</string>
|
||||
<string name="user_push_notification_agreement_argument_one">Будьте в курсі останніх функцій та новин</string>
|
||||
<string name="user_push_notification_agreement_argument_one_subtitle">Миттєві сповіщення про транзакції, обміни та важливі оновлення.</string>
|
||||
<string name="user_push_notification_agreement_argument_one_title">Сповіщення про транзакції</string>
|
||||
|
|
@ -1797,7 +1838,18 @@
|
|||
<string name="warning_button_ok">Зрозуміло!</string>
|
||||
<string name="warning_button_really_cool">Дуже круто!</string>
|
||||
<string name="warning_button_refresh">Оновити</string>
|
||||
<string name="warning_clore_migration_button">Почати міграцію</string>
|
||||
<string name="warning_clore_migration_copy_button">Копіювати</string>
|
||||
<string name="warning_clore_migration_description">Щоб зберегти доступ до своїх коштів, розпочніть міграцію згідно з офіційними рекомендаціями Clore.</string>
|
||||
<string name="warning_clore_migration_error_signing_not_supported">Підписання повідомлень не підтримується в цій мережі</string>
|
||||
<string name="warning_clore_migration_error_wallet_manager_not_found">Неможливо підписати повідомлення. Будь ласка, спробуйте пізніше.</string>
|
||||
<string name="warning_clore_migration_message">Згідно з офіційною документацією Clore, усі монети, отримані до 21 грудня, будуть мігровані в токен Clore (ERC-20); монети, отримані після цієї дати, — ні. Рішення для переказу перебуває в розробці — стежте за оновленнями.</string>
|
||||
<string name="warning_clore_migration_message_label">Повідомлення</string>
|
||||
<string name="warning_clore_migration_open_portal_button">Відкрити портал клейму</string>
|
||||
<string name="warning_clore_migration_sheet_description">Щоб зберегти доступ до своїх активів, почніть міграцію у відповідності до офіційної інструкції Clore.</string>
|
||||
<string name="warning_clore_migration_sheet_title">Міграція мережі Clore</string>
|
||||
<string name="warning_clore_migration_sign_button">Підписати</string>
|
||||
<string name="warning_clore_migration_signature_label">Підпис</string>
|
||||
<string name="warning_clore_migration_title">Міграція мережі Clore</string>
|
||||
<string name="warning_demo_mode_message">Ви перебуваєте в демонстраційному режимі</string>
|
||||
<string name="warning_demo_mode_title">Демонстраційний режим активовано</string>
|
||||
|
|
@ -1979,6 +2031,7 @@
|
|||
<string name="welcome_create_wallet_feature_delivery">Швидка доставка</string>
|
||||
<string name="welcome_create_wallet_feature_one_tap">Почніть в один клік</string>
|
||||
<string name="welcome_create_wallet_feature_seamless">Просто та надійно</string>
|
||||
<string name="welcome_create_wallet_feature_seedphrase">Без seed-фрази</string>
|
||||
<string name="welcome_create_wallet_feature_use">Простий у використанні</string>
|
||||
<string name="welcome_create_wallet_hardware_description">Створіть апаратний гаманець з Tangem. Тонкий, як банківська картка, надійний, як банківське сховище.</string>
|
||||
<string name="welcome_create_wallet_mobile_description">Створіть або імпортуйте програмний гаманець</string>
|
||||
|
|
|
|||
|
|
@ -330,10 +330,12 @@
|
|||
<string name="tangem_pay_status_completed">已完成</string>
|
||||
<string name="tangem_pay_status_declined">已拒絕</string>
|
||||
<string name="tangem_pay_status_pending">處理中</string>
|
||||
<string name="tangem_pay_status_reversed">已撤銷</string>
|
||||
<string name="tangem_pay_terms_fees_limits">條款、費用與限制</string>
|
||||
<string name="tangem_pay_terms_limits">條款與限制</string>
|
||||
<string name="tangem_pay_transaction_declined_notification_text">銀行拒絕了此交易請求。</string>
|
||||
<string name="tangem_pay_transaction_fee_notification_text">此費用用於支付處理您轉帳的成本。</string>
|
||||
<string name="tangem_pay_transaction_reversed_notification_text">該交易已被商家部分或全額撤銷</string>
|
||||
<string name="tangem_pay_unfreeze_card_alert_body">繼續使用您的資金。您可以隨時凍結。</string>
|
||||
<string name="tangem_pay_unfreeze_card_alert_title">解凍您的卡片?</string>
|
||||
<string name="tangem_pay_unfreeze_card_failed">無法解凍卡片。請稍後再試。</string>
|
||||
|
|
|
|||
|
|
@ -1436,6 +1436,7 @@
|
|||
<string name="swap_give_permission_fee_footer">The network will charge a token approval fee to verify that you are authorizing the use of your token for the swap.</string>
|
||||
<string name="swap_promo_text">Exchange more tokens at better rates directly in your wallet.</string>
|
||||
<string name="swap_promo_title">New Swap Provider Available!</string>
|
||||
<string name="swap_search_suggestion_hint">Looking for something else?/nTry searching or explore another crypto!</string>
|
||||
<string name="swap_story_fifth_subtitle">Feel confident with round-the-clock support to help with any issues</string>
|
||||
<string name="swap_story_fifth_title">Always Here</string>
|
||||
<string name="swap_story_first_subtitle">Multiple trusted providers in one place—swap any asset effortlessly in your wallet</string>
|
||||
|
|
@ -1489,10 +1490,12 @@
|
|||
<string name="tangem_pay_status_completed">Completed</string>
|
||||
<string name="tangem_pay_status_declined">Declined</string>
|
||||
<string name="tangem_pay_status_pending">Pending</string>
|
||||
<string name="tangem_pay_status_reversed">Reversed</string>
|
||||
<string name="tangem_pay_terms_fees_limits">Terms, Fees & Limits</string>
|
||||
<string name="tangem_pay_terms_limits">Terms and Limits</string>
|
||||
<string name="tangem_pay_transaction_declined_notification_text">The bank rejected this transaction request.</string>
|
||||
<string name="tangem_pay_transaction_fee_notification_text">This fee goes to cover the cost of handling your transfer.</string>
|
||||
<string name="tangem_pay_transaction_reversed_notification_text">The transaction was partially or fully reversed by the merchant</string>
|
||||
<string name="tangem_pay_unfreeze_card_alert_body">Keep using your money. You can freeze anytime.</string>
|
||||
<string name="tangem_pay_unfreeze_card_alert_title">Unfreeze your card?</string>
|
||||
<string name="tangem_pay_unfreeze_card_failed">Failed to unfreeze the card. Try again later.</string>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
package com.tangem.core.ui
|
||||
|
||||
interface HoldToConfirmButtonFeatureToggles {
|
||||
val isHoldToConfirmEnabled: Boolean
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.compose.ui.unit.sp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.SpacerW4
|
||||
import com.tangem.core.ui.components.TextShimmer
|
||||
import com.tangem.core.ui.components.account.AccountCharIcon
|
||||
import com.tangem.core.ui.components.account.AccountIconSize
|
||||
import com.tangem.core.ui.components.account.AccountResIcon
|
||||
|
|
@ -257,22 +258,26 @@ fun ExpandedPortfolioHeader(
|
|||
}
|
||||
}
|
||||
|
||||
val text = when (val fiatAmountState = state.fiatAmountState) {
|
||||
is TokenItemState.FiatAmountState.Content -> fiatAmountState.text
|
||||
is TokenItemState.FiatAmountState.TextContent -> fiatAmountState.text
|
||||
else -> null
|
||||
when (val fiatAmountState = state.fiatAmountState) {
|
||||
is TokenItemState.FiatAmountState.Content -> FiatAmount(
|
||||
text = fiatAmountState.text,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
modifier = balanceTextModifier,
|
||||
)
|
||||
is TokenItemState.FiatAmountState.TextContent -> FiatAmount(
|
||||
text = fiatAmountState.text,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
modifier = balanceTextModifier,
|
||||
)
|
||||
is TokenItemState.FiatAmountState.Loading -> TextShimmer(
|
||||
style = TangemTheme.typography.caption1,
|
||||
textSizeHeight = true,
|
||||
modifier = balanceTextModifier
|
||||
.padding(horizontal = 4.dp)
|
||||
.fillMaxWidth(fraction = 0.2f),
|
||||
)
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
Text(
|
||||
text = text.orEmpty().orMaskWithStars(isBalanceHidden),
|
||||
modifier = balanceTextModifier
|
||||
.alignByBaseline()
|
||||
.padding(horizontal = 4.dp),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.caption1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
|
||||
if (isCollapsable) {
|
||||
|
|
@ -284,4 +289,18 @@ fun ExpandedPortfolioHeader(
|
|||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RowScope.FiatAmount(text: String, isBalanceHidden: Boolean, modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
text = text.orMaskWithStars(isBalanceHidden),
|
||||
modifier = modifier
|
||||
.alignByBaseline()
|
||||
.padding(horizontal = 4.dp),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.caption1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
|
|
@ -103,7 +103,7 @@ object DateTimeFormatters {
|
|||
*/
|
||||
val localFullDate: DateTimeFormatter by lazy {
|
||||
val locale = Locale.getDefault()
|
||||
val datePattern = DateFormat.getBestDateTimePattern(locale, "dd MMMM")
|
||||
val datePattern = DateFormat.getBestDateTimePattern(locale, "d MMMM")
|
||||
val timeSkeleton = if (is12HourFormat) "h:mm a" else "HH:mm"
|
||||
val timePattern = DateFormat.getBestDateTimePattern(locale, timeSkeleton)
|
||||
val fullPattern = "$datePattern, $timePattern"
|
||||
|
|
|
|||
|
|
@ -30,6 +30,10 @@ class JobHolder {
|
|||
job = null
|
||||
}
|
||||
|
||||
suspend fun join() {
|
||||
job?.join()
|
||||
}
|
||||
|
||||
fun isEmpty() = job == null
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ internal object TangemPayTxHistoryItemStatusConverter : Converter<String, Tangem
|
|||
"RESERVED" -> TangemPayTxHistoryItem.Status.RESERVED
|
||||
"COMPLETED" -> TangemPayTxHistoryItem.Status.COMPLETED
|
||||
"DECLINED" -> TangemPayTxHistoryItem.Status.DECLINED
|
||||
"REVERSED" -> TangemPayTxHistoryItem.Status.REVERSED
|
||||
else -> TangemPayTxHistoryItem.Status.UNKNOWN
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,15 +107,16 @@ internal class WcEthSendTransactionUseCase @AssistedInject constructor(
|
|||
val newState = when (action) {
|
||||
is WcEthTxAction.UpdateApprovalAmount -> {
|
||||
val extras = uncompiled.extras as EthereumTransactionExtras
|
||||
val callData = ApprovalERC20TokenCallData(
|
||||
spenderAddress = uncompiled.sourceAddress,
|
||||
val compiledData = extras.callData?.data ?: return
|
||||
val approvalCallData = ApprovalERC20TokenCallData(compiledData) ?: return
|
||||
val newApprovalCallData = approvalCallData.copy(
|
||||
amount = action.amount?.amount?.let {
|
||||
BlockchainAmount(currencySymbol = it.currencySymbol, decimals = it.decimals, value = it.value)
|
||||
},
|
||||
)
|
||||
approvalAmount = action.amount
|
||||
isIgnoreDAppFee = true
|
||||
uncompiled.copy(extras = extras.copy(callData = callData))
|
||||
uncompiled.copy(extras = extras.copy(callData = newApprovalCallData))
|
||||
}
|
||||
is WcEthTxAction.UpdateFee -> uncompiled.copy(fee = action.fee)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,15 +92,16 @@ internal class WcEthSignTransactionUseCase @AssistedInject constructor(
|
|||
val newState = when (action) {
|
||||
is WcEthTxAction.UpdateApprovalAmount -> {
|
||||
val extras = uncompiled.extras as EthereumTransactionExtras
|
||||
val callData = ApprovalERC20TokenCallData(
|
||||
spenderAddress = uncompiled.sourceAddress,
|
||||
val compiledData = extras.callData?.data ?: return
|
||||
val approvalCallData = ApprovalERC20TokenCallData(compiledData) ?: return
|
||||
val newApprovalCallData = approvalCallData.copy(
|
||||
amount = action.amount?.amount?.let {
|
||||
BlockchainAmount(currencySymbol = it.currencySymbol, decimals = it.decimals, value = it.value)
|
||||
},
|
||||
)
|
||||
approvalAmount = action.amount
|
||||
isIgnoreDAppFee = true
|
||||
uncompiled.copy(extras = extras.copy(callData = callData))
|
||||
uncompiled.copy(extras = extras.copy(callData = newApprovalCallData))
|
||||
}
|
||||
is WcEthTxAction.UpdateFee -> uncompiled.copy(fee = action.fee)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ dependencies {
|
|||
implementation(projects.domain.tokens.models)
|
||||
implementation(projects.domain.wallets)
|
||||
implementation(projects.domain.wallets.models)
|
||||
implementation(projects.domain.settings)
|
||||
|
||||
/** DI */
|
||||
implementation(deps.hilt.android)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.tangem.common.core.TangemSdkError
|
|||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.settings.repositories.LegacySettingsRepository
|
||||
import com.tangem.domain.wallets.hot.HotWalletAccessor
|
||||
import com.tangem.domain.wallets.hot.HotWalletPasswordRequester
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
|
|
@ -17,18 +18,21 @@ import kotlinx.coroutines.SupervisorJob
|
|||
import kotlinx.coroutines.launch
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class DefaultHotWalletAccessor @Inject constructor(
|
||||
private val tangemHotSdk: TangemHotSdk,
|
||||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
private val hotWalletPasswordRequester: HotWalletPasswordRequester,
|
||||
private val walletsRepository: WalletsRepository,
|
||||
private val legacySettingsRepository: LegacySettingsRepository,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
) : HotWalletAccessor {
|
||||
|
||||
private val scope = CoroutineScope(context = SupervisorJob() + dispatchers.io)
|
||||
|
||||
private var contextualUnlockHotWallet: ConcurrentHashMap<HotWalletId, UnlockHotWallet?> = ConcurrentHashMap()
|
||||
private val contextualUnlockHotWallet: ConcurrentHashMap<HotWalletId, UnlockHotWallet?> = ConcurrentHashMap()
|
||||
|
||||
override suspend fun signHashes(hotWalletId: HotWalletId, dataToSign: List<DataToSign>): List<SignedData> =
|
||||
hotSdkRequest(hotWalletId) { unlock ->
|
||||
|
|
@ -82,7 +86,7 @@ class DefaultHotWalletAccessor @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun <T> hotSdkRequest(hotWalletId: HotWalletId, block: suspend (unlock: UnlockHotWallet) -> T): T {
|
||||
val isAccessCodeRequired = walletsRepository.requireAccessCode()
|
||||
val isAccessCodeRequired = isAccessCodeRequired()
|
||||
|
||||
val auth = when (hotWalletId.authType) {
|
||||
HotWalletId.AuthType.NoPassword -> HotAuth.NoAuth
|
||||
|
|
@ -134,7 +138,7 @@ class DefaultHotWalletAccessor @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun updateBiometryAuthIfNeeded(hotWalletId: HotWalletId, originalAuth: HotAuth) {
|
||||
val isAccessCodeRequired = walletsRepository.requireAccessCode()
|
||||
val isAccessCodeRequired = isAccessCodeRequired()
|
||||
val isUseBiometricAuthenticationEnabled = walletsRepository.useBiometricAuthentication()
|
||||
|
||||
if (originalAuth is HotAuth.Password && isUseBiometricAuthenticationEnabled && isAccessCodeRequired.not()) {
|
||||
|
|
@ -167,7 +171,7 @@ class DefaultHotWalletAccessor @Inject constructor(
|
|||
): T = runSuspendCatching {
|
||||
block(auth)
|
||||
}.getOrElse { exception ->
|
||||
if (auth is HotAuth.Biometry && exception.isBiometryError()) {
|
||||
if (auth is HotAuth.Biometry && (exception.isBiometryError() || exception.isBiometryReset())) {
|
||||
val shouldRetryBiometry = exception is TangemSdkError.AuthenticationCanceled
|
||||
|
||||
// fallback to password if biometry fails
|
||||
|
|
@ -215,6 +219,14 @@ class DefaultHotWalletAccessor @Inject constructor(
|
|||
?: throw TangemSdkError.UserCancelled()
|
||||
}
|
||||
|
||||
private suspend fun isAccessCodeRequired(): Boolean {
|
||||
return walletsRepository.requireAccessCode() || legacySettingsRepository.canUseBiometryStrict().not()
|
||||
}
|
||||
|
||||
private fun Throwable.isBiometryReset(): Boolean {
|
||||
return this is IllegalStateException
|
||||
}
|
||||
|
||||
private fun Throwable.isBiometryError(): Boolean {
|
||||
return this is TangemSdkError.AuthenticationFailed ||
|
||||
this is TangemSdkError.AuthenticationCanceled ||
|
||||
|
|
|
|||
|
|
@ -4,5 +4,8 @@ import com.tangem.domain.settings.repositories.LegacySettingsRepository
|
|||
|
||||
class CanUseBiometryUseCase(private val legacySettingsRepository: LegacySettingsRepository) {
|
||||
|
||||
@Deprecated("You probably want to use strict() instead. Check implementation", ReplaceWith("strict()"))
|
||||
suspend operator fun invoke(): Boolean = legacySettingsRepository.canUseBiometry()
|
||||
|
||||
suspend fun strict(): Boolean = legacySettingsRepository.canUseBiometryStrict()
|
||||
}
|
||||
|
|
@ -3,4 +3,6 @@ package com.tangem.domain.settings.repositories
|
|||
interface LegacySettingsRepository {
|
||||
|
||||
suspend fun canUseBiometry(): Boolean
|
||||
|
||||
suspend fun canUseBiometryStrict(): Boolean
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.domain.transaction.usecase.gasless
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.getOrElse
|
||||
import arrow.core.raise.Raise
|
||||
import arrow.core.raise.catch
|
||||
import arrow.core.raise.either
|
||||
|
|
@ -128,25 +129,32 @@ class GetFeeForGaslessUseCase(
|
|||
} ?: raiseIllegalStateError("native currency not found for network ${network.id}")
|
||||
|
||||
val nativeBalance = nativeCurrencyStatus.value.amount ?: BigDecimal.ZERO
|
||||
return if (nativeBalance >= feeValue) {
|
||||
val nativeCoinSelectedResult =
|
||||
TransactionFeeExtended(transactionFee = initialFee, feeTokenId = nativeCurrencyStatus.currency.id)
|
||||
return if (nativeBalance >= feeValue) {
|
||||
nativeCoinSelectedResult
|
||||
} else {
|
||||
findTokensToPayFee(
|
||||
walletManager = walletManager,
|
||||
initialTxFee = initialFee,
|
||||
nativeCurrencyStatus = nativeCurrencyStatus,
|
||||
networkCurrenciesStatuses = networkCurrenciesStatuses,
|
||||
)
|
||||
).getOrElse { error ->
|
||||
when (error) {
|
||||
GaslessError.NotEnoughFunds -> nativeCoinSelectedResult
|
||||
else -> raise(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("NullableToStringCall")
|
||||
private suspend fun Raise<GetFeeError>.findTokensToPayFee(
|
||||
private suspend fun findTokensToPayFee(
|
||||
walletManager: EthereumWalletManager,
|
||||
initialTxFee: TransactionFee,
|
||||
nativeCurrencyStatus: CryptoCurrencyStatus,
|
||||
networkCurrenciesStatuses: List<CryptoCurrencyStatus>,
|
||||
): TransactionFeeExtended {
|
||||
): Either<GetFeeError, TransactionFeeExtended> = either {
|
||||
val initialFee = initialTxFee.normal as? Fee.Ethereum
|
||||
?: raiseIllegalStateError(
|
||||
error = "only Fee.Ethereum supported, but was ${initialTxFee.normal::class.qualifiedName}",
|
||||
|
|
@ -178,6 +186,6 @@ class GetFeeForGaslessUseCase(
|
|||
tokenForPayFeeStatus = tokenForPayFeeStatus,
|
||||
nativeCurrencyStatus = nativeCurrencyStatus,
|
||||
initialFee = initialFee,
|
||||
).bind()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import arrow.core.raise.either
|
|||
import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager
|
||||
import com.tangem.blockchain.blockchains.ethereum.tokenmethods.TransferERC20TokenCallData
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.BlockchainSdkError
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
|
|
@ -89,6 +90,7 @@ internal class TokenFeeCalculator(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
suspend fun calculateTokenFee(
|
||||
walletManager: EthereumWalletManager,
|
||||
tokenForPayFeeStatus: CryptoCurrencyStatus,
|
||||
|
|
@ -119,8 +121,17 @@ internal class TokenFeeCalculator(
|
|||
)
|
||||
|
||||
val feeTransferGasLimit = when (feeTransferGasLimitResult) {
|
||||
is Result.Failure -> raise(GaslessError.DataError(feeTransferGasLimitResult.error))
|
||||
is Result.Success -> feeTransferGasLimitResult.data
|
||||
is Result.Failure -> {
|
||||
// If there is a dust on the balance, the gas limit estimation will fail with code
|
||||
if (feeTransferGasLimitResult.error is BlockchainSdkError.WrappedThrowable) {
|
||||
val cause = feeTransferGasLimitResult.error.cause
|
||||
if (cause is BlockchainSdkError.Ethereum.InsufficientFundsForOperation) {
|
||||
raise(GaslessError.NotEnoughFunds)
|
||||
}
|
||||
}
|
||||
raise(GaslessError.DataError(feeTransferGasLimitResult.error))
|
||||
}
|
||||
}.increaseByPercent(PERCENT_TO_INCREASE_TRANSFER_GASLIMIT)
|
||||
|
||||
val baseGas = gaslessTransactionRepository.getBaseGasForTransaction()
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ sealed class TangemPayTxHistoryItem {
|
|||
RESERVED,
|
||||
COMPLETED,
|
||||
DECLINED,
|
||||
REVERSED,
|
||||
UNKNOWN,
|
||||
}
|
||||
}
|
||||
|
|
@ -53,12 +53,13 @@ sealed class WcAnalyticEvents(
|
|||
network: Set<Network>,
|
||||
domainVerification: CheckDAppResult,
|
||||
) : WcAnalyticEvents(
|
||||
event = "dApp Connection Requested",
|
||||
event = "DApp Connection Requested",
|
||||
params = mapOf(
|
||||
AnalyticsParam.DAPP_NAME to dAppName,
|
||||
AnalyticsParam.DAPP_URL to dAppUrl,
|
||||
NETWORKS to network.joinToString(",") { it.name },
|
||||
DOMAIN_VERIFICATION to domainVerification.toAnalyticVerificationStatus(),
|
||||
AnalyticsParam.ACCOUNT_DERIVATION to "0",
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -120,7 +120,9 @@ internal class AddTokenModel @Inject constructor(
|
|||
selectedPortfolio: SelectedPortfolio,
|
||||
): Boolean = coldWalletAndHasMissedDerivationsUseCase.invoke(
|
||||
userWalletId = selectedPortfolio.userWallet.walletId,
|
||||
networksWithDerivationPath = mapOf(selectedNetwork.selectedNetwork.networkId to null),
|
||||
networksWithDerivationPath = mapOf(
|
||||
selectedNetwork.selectedNetwork.networkId to selectedNetwork.cryptoCurrency.network.derivationPath.value,
|
||||
),
|
||||
)
|
||||
|
||||
private fun processError(error: Throwable?) {
|
||||
|
|
|
|||
|
|
@ -254,7 +254,7 @@ private fun LazyListScope.relatedNews(relatedNews: RelatedNews) {
|
|||
articleConfigUM = article,
|
||||
onArticleClick = { relatedNews.onArticledClicked(article.id) },
|
||||
modifier = articleModifier
|
||||
.height(164.dp)
|
||||
.heightIn(min = 164.dp)
|
||||
.width(216.dp),
|
||||
colors = TangemBlockCardColors.copy(containerColor = TangemTheme.colors.background.action),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -31,14 +31,12 @@ import com.tangem.hot.sdk.TangemHotSdk
|
|||
import com.tangem.hot.sdk.model.HotAuth
|
||||
import com.tangem.hot.sdk.model.HotWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
import com.tangem.utils.coroutines.saveIn
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import javax.inject.Inject
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
|
|
@ -67,6 +65,8 @@ internal class AccessCodeModel @Inject constructor(
|
|||
|
||||
private val params = paramsContainer.require<AccessCodeComponent.Params>()
|
||||
|
||||
private val settingCodeJobHolder = JobHolder()
|
||||
|
||||
internal val uiState: StateFlow<AccessCodeUM>
|
||||
field = MutableStateFlow(getInitialState())
|
||||
|
||||
|
|
@ -201,38 +201,32 @@ internal class AccessCodeModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun setCode(userWalletId: UserWalletId, accessCode: String) {
|
||||
private suspend fun setCode(userWalletId: UserWalletId, accessCode: String) = coroutineScope {
|
||||
if (settingCodeJobHolder.isActive) {
|
||||
return@coroutineScope
|
||||
}
|
||||
|
||||
params.callbacks.onAccessCodeUpdateStarted(params.userWalletId)
|
||||
|
||||
modelScope.launch {
|
||||
val userWallet = getUserWalletUseCase(userWalletId)
|
||||
.getOrElse { error("User wallet with id $userWalletId not found") }
|
||||
.requireHotWallet()
|
||||
val userWallet = getUserWalletUseCase(userWalletId)
|
||||
.getOrElse { error("User wallet with id $userWalletId not found") }
|
||||
.requireHotWallet()
|
||||
|
||||
tryToAskForBiometry()
|
||||
tryToAskForBiometry()
|
||||
|
||||
val settingCodeJob = launch(dispatchers.main) {
|
||||
setCodeOperation(userWallet, accessCode)
|
||||
params.callbacks.onAccessCodeUpdated(params.userWalletId)
|
||||
}
|
||||
val settingCodeJob = launch(dispatchers.main) {
|
||||
setCodeOperation(userWallet, accessCode)
|
||||
params.callbacks.onAccessCodeUpdated(params.userWalletId)
|
||||
}.saveIn(settingCodeJobHolder)
|
||||
|
||||
setLoadingIfLongJob(settingCodeJob)
|
||||
}
|
||||
setLoadingIfLongJob(settingCodeJob)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set access code for hot wallet
|
||||
* !!! Be aware that order of operations is important here !!!
|
||||
*/
|
||||
private suspend fun setCodeOperation(userWallet: UserWallet.Hot, accessCode: String) {
|
||||
userWalletsListRepository.setLock(
|
||||
userWalletId = userWallet.walletId,
|
||||
lockMethod = UserWalletsListRepository.LockMethod.AccessCode(accessCode.toCharArray()),
|
||||
)
|
||||
|
||||
if (walletsRepository.useBiometricAuthentication()) {
|
||||
userWalletsListRepository.setLock(
|
||||
userWalletId = userWallet.walletId,
|
||||
lockMethod = UserWalletsListRepository.LockMethod.Biometric,
|
||||
)
|
||||
}
|
||||
|
||||
val unlockHotWallet = getHotWalletContextualUnlockUseCase(userWallet.hotWalletId)
|
||||
.getOrNull()
|
||||
?: run {
|
||||
|
|
@ -243,22 +237,39 @@ internal class AccessCodeModel @Inject constructor(
|
|||
hotWalletAccessor.unlockContextual(userWallet.hotWalletId)
|
||||
}
|
||||
|
||||
var updatedHotWalletId = tangemHotSdk.changeAuth(
|
||||
val newHotWalletIdWithPass = tangemHotSdk.changeAuth(
|
||||
unlockHotWallet = unlockHotWallet,
|
||||
auth = HotAuth.Password(accessCode.toCharArray()),
|
||||
)
|
||||
|
||||
if (walletsRepository.requireAccessCode().not() && canUseBiometryUseCase()) {
|
||||
updatedHotWalletId = tangemHotSdk.changeAuth(
|
||||
userWalletsListRepository.saveWithoutLock(
|
||||
userWallet.copy(hotWalletId = newHotWalletIdWithPass),
|
||||
canOverride = true,
|
||||
)
|
||||
|
||||
userWalletsListRepository.setLock(
|
||||
userWalletId = userWallet.walletId,
|
||||
lockMethod = UserWalletsListRepository.LockMethod.AccessCode(accessCode.toCharArray()),
|
||||
)
|
||||
|
||||
if (walletsRepository.requireAccessCode().not() && canUseBiometryUseCase.strict()) {
|
||||
val newHotWalletIdWithBiometry = tangemHotSdk.changeAuth(
|
||||
unlockHotWallet = unlockHotWallet,
|
||||
auth = HotAuth.Biometry,
|
||||
)
|
||||
|
||||
userWalletsListRepository.saveWithoutLock(
|
||||
userWallet.copy(hotWalletId = newHotWalletIdWithBiometry),
|
||||
canOverride = true,
|
||||
)
|
||||
}
|
||||
|
||||
userWalletsListRepository.saveWithoutLock(
|
||||
userWallet.copy(hotWalletId = updatedHotWalletId),
|
||||
canOverride = true,
|
||||
)
|
||||
if (walletsRepository.useBiometricAuthentication()) {
|
||||
userWalletsListRepository.setLock(
|
||||
userWalletId = userWallet.walletId,
|
||||
lockMethod = UserWalletsListRepository.LockMethod.Biometric,
|
||||
)
|
||||
}
|
||||
|
||||
clearHotWalletContextualUnlockUseCase.invoke(params.userWalletId)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -219,7 +219,7 @@ internal class HotAccessCodeRequestModel @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun HotWalletPasswordRequester.AttemptRequest.isBiometryButtonVisible(): Boolean =
|
||||
hasBiometry && canUseBiometryUseCase()
|
||||
hasBiometry && canUseBiometryUseCase.strict()
|
||||
|
||||
private fun dismissState() {
|
||||
uiState.update {
|
||||
|
|
|
|||
|
|
@ -3,10 +3,13 @@ package com.tangem.features.hotwallet.forgetwallet.ui
|
|||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
|
|
@ -89,7 +92,13 @@ internal fun ForgetWalletContent(state: ForgetWalletUM, modifier: Modifier = Mod
|
|||
@Composable
|
||||
private fun CheckboxItem(checked: Boolean, onCheckedChange: () -> Unit, text: String, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
modifier = modifier
|
||||
.clickable(
|
||||
indication = null,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = { onCheckedChange() },
|
||||
)
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.Top,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -34,13 +34,12 @@ internal sealed class CustomTokenAnalyticsEvent(
|
|||
class AddTokenToAnotherAccount(
|
||||
currencySymbol: String,
|
||||
derivationPath: String,
|
||||
source: ManageTokensSource,
|
||||
) : CustomTokenAnalyticsEvent(
|
||||
) : AnalyticsEvent(
|
||||
category = "Settings / Account",
|
||||
event = "Button - Add Token To Another Account",
|
||||
params = mapOf(
|
||||
AnalyticsParam.Key.TOKEN_PARAM to currencySymbol,
|
||||
AnalyticsParam.Key.DERIVATION to derivationPath,
|
||||
AnalyticsParam.Key.SOURCE to source.analyticsName,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -239,7 +239,6 @@ internal class DefaultAddCustomTokenComponent @AssistedInject constructor(
|
|||
val event = CustomTokenAnalyticsEvent.AddTokenToAnotherAccount(
|
||||
currencySymbol = currency.symbol,
|
||||
derivationPath = currency.network.derivationPath.value.orEmpty(),
|
||||
source = params.source,
|
||||
)
|
||||
analyticsEventHandler.send(event)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,7 +120,9 @@ internal class AddTokenModel @Inject constructor(
|
|||
selectedPortfolio: SelectedPortfolio,
|
||||
): Boolean = coldWalletAndHasMissedDerivationsUseCase.invoke(
|
||||
userWalletId = selectedPortfolio.userWallet.walletId,
|
||||
networksWithDerivationPath = mapOf(selectedNetwork.selectedNetwork.networkId to null),
|
||||
networksWithDerivationPath = mapOf(
|
||||
selectedNetwork.selectedNetwork.networkId to selectedNetwork.cryptoCurrency.network.derivationPath.value,
|
||||
),
|
||||
)
|
||||
|
||||
private fun processError(error: Throwable?) {
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ dependencies {
|
|||
implementation(projects.domain.account.status)
|
||||
implementation(projects.domain.appTheme)
|
||||
implementation(projects.domain.appTheme.models)
|
||||
implementation(projects.data.common)
|
||||
|
||||
/** DI */
|
||||
implementation(deps.hilt.android)
|
||||
|
|
@ -68,6 +69,10 @@ dependencies {
|
|||
implementation(deps.compose.shimmer)
|
||||
implementation(deps.compose.coil)
|
||||
|
||||
/** Tangem libraries */
|
||||
implementation(tangemDeps.blockchain)
|
||||
implementation(projects.libs.blockchainSdk)
|
||||
|
||||
/** Other */
|
||||
implementation(deps.decompose.ext.compose)
|
||||
implementation(deps.kotlin.immutable.collections)
|
||||
|
|
|
|||
|
|
@ -8,18 +8,28 @@ import com.arkivanov.decompose.router.stack.StackNavigation
|
|||
import com.arkivanov.decompose.router.stack.popToFirst
|
||||
import com.arkivanov.decompose.router.stack.pushNew
|
||||
import com.arkivanov.decompose.router.stack.replaceAll
|
||||
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||
import com.tangem.blockchainsdk.utils.toCoinId
|
||||
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.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.data.common.currency.getCoinId
|
||||
import com.tangem.data.common.currency.getTokenId
|
||||
import com.tangem.data.common.currency.isCustomCoin
|
||||
import com.tangem.data.common.currency.isCustomToken
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.account.derivationIndex
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.onramp.GetHotCryptoUseCase
|
||||
import com.tangem.domain.onramp.model.HotCryptoCurrency
|
||||
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
|
||||
|
|
@ -70,6 +80,7 @@ internal class HotCryptoModel @Inject constructor(
|
|||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
|
||||
val portfolioSelectorController: PortfolioSelectorController,
|
||||
private val networkFactory: NetworkFactory,
|
||||
private val portfolioFetcherFactory: PortfolioFetcher.Factory,
|
||||
) : Model(), OnrampAddTokenComponent.Callbacks by callbackDelegate {
|
||||
|
||||
|
|
@ -172,10 +183,15 @@ internal class HotCryptoModel @Inject constructor(
|
|||
|
||||
if (isSingleAccount) {
|
||||
val account = hotCryptoPortfolioData.wallet.accounts.first().account
|
||||
val tokenToAdd = AddHotCryptoData(
|
||||
val cryptoCurrency = updateCryptoCurrency(
|
||||
cryptoCurrency = currency.cryptoCurrency,
|
||||
userWallet = userWallet,
|
||||
account = account,
|
||||
)
|
||||
val tokenToAdd = AddHotCryptoData(
|
||||
cryptoCurrency = requireNotNull(cryptoCurrency),
|
||||
userWallet = userWallet,
|
||||
account = account,
|
||||
isMorePortfolioAvailable = false,
|
||||
)
|
||||
hotCryptoToAddDataFlow.emit(tokenToAdd)
|
||||
|
|
@ -188,10 +204,15 @@ internal class HotCryptoModel @Inject constructor(
|
|||
.selectedAccountWithData(requireNotNull(portfolioFetcher))
|
||||
.filterNotNull()
|
||||
.map { (_, selectedAccount) ->
|
||||
AddHotCryptoData(
|
||||
val cryptoCurrency = updateCryptoCurrency(
|
||||
cryptoCurrency = currency.cryptoCurrency,
|
||||
userWallet = userWallet,
|
||||
account = selectedAccount,
|
||||
)
|
||||
AddHotCryptoData(
|
||||
cryptoCurrency = requireNotNull(cryptoCurrency),
|
||||
userWallet = userWallet,
|
||||
account = selectedAccount,
|
||||
isMorePortfolioAvailable = true,
|
||||
)
|
||||
}
|
||||
|
|
@ -251,6 +272,47 @@ internal class HotCryptoModel @Inject constructor(
|
|||
return@isEnabled isNotAddedHotCrypto
|
||||
}
|
||||
}
|
||||
|
||||
// todo account move to common module
|
||||
private fun updateCryptoCurrency(
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
userWallet: UserWallet,
|
||||
account: AccountStatus,
|
||||
): CryptoCurrency? {
|
||||
val derivationIndex = account.account.derivationIndex ?: return null
|
||||
val blockchain = cryptoCurrency.network.toBlockchain()
|
||||
|
||||
val network = networkFactory.create(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = null,
|
||||
accountIndex = derivationIndex,
|
||||
userWallet = userWallet,
|
||||
) ?: return null
|
||||
|
||||
return when (cryptoCurrency) {
|
||||
is CryptoCurrency.Coin -> {
|
||||
val id = getCoinId(network, network.toBlockchain().toCoinId())
|
||||
cryptoCurrency.copy(
|
||||
id = id,
|
||||
network = network,
|
||||
isCustom = isCustomCoin(network),
|
||||
)
|
||||
}
|
||||
|
||||
is CryptoCurrency.Token -> {
|
||||
val id = getTokenId(
|
||||
network = network,
|
||||
rawTokenId = cryptoCurrency.id.rawCurrencyId,
|
||||
contractAddress = cryptoCurrency.contractAddress,
|
||||
)
|
||||
cryptoCurrency.copy(
|
||||
id = id,
|
||||
network = network,
|
||||
isCustom = isCustomToken(id, network),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ModelScoped
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import com.tangem.core.ui.extensions.stringReference
|
|||
import com.tangem.core.ui.message.ToastMessage
|
||||
import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
|
||||
import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.BackendId
|
||||
import com.tangem.domain.wallets.usecase.ColdWalletAndHasMissedDerivationsUseCase
|
||||
|
|
@ -47,10 +49,14 @@ internal class OnrampAddTokenModel @Inject constructor(
|
|||
params.tokenToAdd
|
||||
.distinctUntilChanged()
|
||||
.mapLatest { tokenToAdd: AddHotCryptoData ->
|
||||
addTokenJob.cancel()
|
||||
addTokenJob.join()
|
||||
val backendId = tokenToAdd.cryptoCurrency.network.backendId
|
||||
val userWalletId = tokenToAdd.account.accountId.userWalletId
|
||||
val isTangemIconVisible = needColdWalletInteraction(userWalletId, backendId)
|
||||
val isTangemIconVisible = needColdWalletInteraction(
|
||||
walletId = userWalletId,
|
||||
backendId = backendId,
|
||||
cryptoCurrency = tokenToAdd.cryptoCurrency,
|
||||
)
|
||||
uiBuilder.updateContent(
|
||||
tokenToAdd = tokenToAdd,
|
||||
isTangemIconVisible = isTangemIconVisible,
|
||||
|
|
@ -79,7 +85,9 @@ internal class OnrampAddTokenModel @Inject constructor(
|
|||
userWalletId = accountId.userWalletId,
|
||||
currencyId = cryptoCurrency.id,
|
||||
network = cryptoCurrency.network,
|
||||
).firstOrNull()
|
||||
)
|
||||
.filter { (_, status) -> status.value !is CryptoCurrencyStatus.Loading }
|
||||
.firstOrNull()
|
||||
if (status == null) {
|
||||
processError(error = null)
|
||||
} else {
|
||||
|
|
@ -88,11 +96,14 @@ internal class OnrampAddTokenModel @Inject constructor(
|
|||
uiState.value = um.toggleProgress(false)
|
||||
}
|
||||
|
||||
private suspend fun needColdWalletInteraction(walletId: UserWalletId, backendId: BackendId): Boolean =
|
||||
coldWalletAndHasMissedDerivationsUseCase.invoke(
|
||||
userWalletId = walletId,
|
||||
networksWithDerivationPath = mapOf(backendId to null),
|
||||
)
|
||||
private suspend fun needColdWalletInteraction(
|
||||
walletId: UserWalletId,
|
||||
backendId: BackendId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): Boolean = coldWalletAndHasMissedDerivationsUseCase.invoke(
|
||||
userWalletId = walletId,
|
||||
networksWithDerivationPath = mapOf(backendId to cryptoCurrency.network.derivationPath.value),
|
||||
)
|
||||
|
||||
private fun processError(error: Throwable?) {
|
||||
val message = error?.message?.let { stringReference(it) }
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@ internal class DefaultMobileWalletPromoRepository @Inject constructor(
|
|||
return appPreferencesStore.getSyncOrDefault(key = SHOULD_SHOW_MOBILE_WALLET_PROMO_KEY, default = false)
|
||||
}
|
||||
|
||||
override suspend fun setShouldShowMobileWalletPromo(value: Boolean) {
|
||||
override suspend fun setShouldShowMobileWalletPromo(shouldShowPromo: Boolean) {
|
||||
appPreferencesStore.editData { preferences ->
|
||||
preferences[SHOULD_SHOW_MOBILE_WALLET_PROMO_KEY] = value
|
||||
preferences[SHOULD_SHOW_MOBILE_WALLET_PROMO_KEY] = shouldShowPromo
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,5 +4,5 @@ interface MobileWalletPromoRepository {
|
|||
|
||||
suspend fun shouldShowMobileWalletPromo(): Boolean
|
||||
|
||||
suspend fun setShouldShowMobileWalletPromo(value: Boolean)
|
||||
suspend fun setShouldShowMobileWalletPromo(shouldShowPromo: Boolean)
|
||||
}
|
||||
|
|
@ -9,10 +9,10 @@ class SetShouldShowMobileWalletPromoUseCase @Inject constructor(
|
|||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(): Either<Throwable, Unit> = Either.catch {
|
||||
suspend operator fun invoke(shouldShowPromo: Boolean): Either<Throwable, Unit> = Either.catch {
|
||||
val wallets = userWalletsListRepository.userWallets.value
|
||||
if (wallets.isNullOrEmpty()) {
|
||||
mobileWalletPromoRepository.setShouldShowMobileWalletPromo(true)
|
||||
mobileWalletPromoRepository.setShouldShowMobileWalletPromo(shouldShowPromo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -31,6 +31,8 @@ import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorRelo
|
|||
import com.tangem.features.send.v2.api.subcomponents.feeSelector.analytics.CommonSendFeeAnalyticEvents
|
||||
import com.tangem.features.send.v2.api.subcomponents.feeSelector.analytics.CommonSendFeeAnalyticEvents.GasPriceInserter
|
||||
import com.tangem.features.send.v2.feeselector.model.transformers.*
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
import com.tangem.utils.coroutines.saveIn
|
||||
import com.tangem.utils.transformer.update
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
|
|
@ -62,6 +64,7 @@ internal class FeeSelectorLogic @AssistedInject constructor(
|
|||
) : FeeSelectorIntents {
|
||||
|
||||
private var appCurrency: AppCurrency = AppCurrency.Default
|
||||
private val loadFeeJobHolder = JobHolder()
|
||||
val uiState = MutableStateFlow(params.state)
|
||||
|
||||
val isGaslessEnabled = sendFeatureToggles.isGaslessTransactionsEnabled &&
|
||||
|
|
@ -113,7 +116,7 @@ internal class FeeSelectorLogic @AssistedInject constructor(
|
|||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}.saveIn(loadFeeJobHolder)
|
||||
}
|
||||
|
||||
private fun isFeeApproximate(amountType: AmountType): Boolean {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import com.tangem.core.decompose.model.ParamsContainer
|
|||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.navigation.share.ShareManager
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
|
|
@ -118,6 +119,7 @@ internal class SendConfirmModel @Inject constructor(
|
|||
private val manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase,
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val createAndSendGaslessTransactionUseCase: CreateAndSendGaslessTransactionUseCase,
|
||||
private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
|
||||
sendBalanceUpdaterFactory: SendBalanceUpdater.Factory,
|
||||
) : Model(), SendConfirmClickIntents, FeeSelectorModelCallback, SendNotificationsComponent.ModelCallback {
|
||||
|
||||
|
|
@ -597,7 +599,8 @@ internal class SendConfirmModel @Inject constructor(
|
|||
val confirmUM = uiState.value.confirmUM
|
||||
val isContent = confirmUM is ConfirmUM.Content
|
||||
val isReadyToSend = isContent && !confirmUM.isSending
|
||||
val isHoldToConfirm = userWallet.isHotWallet && isContent
|
||||
val isHoldToConfirm = holdToConfirmButtonFeatureToggles.isHoldToConfirmEnabled &&
|
||||
userWallet.isHotWallet && isContent
|
||||
return NavigationButton(
|
||||
textReference = getPrimaryButtonText(confirmUM, isHoldToConfirm),
|
||||
iconRes = walletInterationIcon(userWallet),
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ 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.navigation.Router
|
||||
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
|
|
@ -96,6 +97,7 @@ internal class SendWithSwapConfirmModel @Inject constructor(
|
|||
private val swapAlertFactory: SwapAlertFactory,
|
||||
private val appRouter: AppRouter,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
|
||||
swapTransactionSenderFactory: SwapTransactionSender.Factory,
|
||||
paramsContainer: ParamsContainer,
|
||||
) : Model(), FeeSelectorModelCallback, SendNotificationsComponent.ModelCallback {
|
||||
|
|
@ -502,7 +504,8 @@ internal class SendWithSwapConfirmModel @Inject constructor(
|
|||
val confirmUM = state.confirmUM
|
||||
val isContent = confirmUM is ConfirmUM.Content
|
||||
val isReadyToSend = isContent && !confirmUM.isTransactionInProcess
|
||||
val isHoldToConfirm = params.userWallet.isHotWallet && isContent
|
||||
val isHoldToConfirm = holdToConfirmButtonFeatureToggles.isHoldToConfirmEnabled &&
|
||||
params.userWallet.isHotWallet && isContent
|
||||
params.callback.onResult(
|
||||
route = SendWithSwapRoute.Confirm,
|
||||
sendWithSwapUM = state.copy(
|
||||
|
|
|
|||
|
|
@ -30,9 +30,11 @@ import com.tangem.features.feed.components.market.details.portfolio.add.AddToPor
|
|||
import com.tangem.features.send.v2.api.SendFeatureToggles
|
||||
import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents
|
||||
import com.tangem.features.swap.SwapComponent
|
||||
import com.tangem.utils.extensions.isZero
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import java.math.BigDecimal
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
internal class DefaultSwapComponent @AssistedInject constructor(
|
||||
|
|
@ -104,10 +106,12 @@ internal class DefaultSwapComponent @AssistedInject constructor(
|
|||
val dataState by model.dataStateStateFlow.collectAsStateWithLifecycle()
|
||||
val fromCryptoCurrency by remember { derivedStateOf { dataState.fromCryptoCurrency } }
|
||||
val feePaidCryptoCurrency by remember { derivedStateOf { dataState.feePaidCryptoCurrency } }
|
||||
val amount by remember { derivedStateOf { dataState.amount } }
|
||||
val shouldHideBlock by remember {
|
||||
derivedStateOf { toBigDecimalOrZero(dataState.amount).isZero() || model.uiState.isInsufficientFunds }
|
||||
}
|
||||
|
||||
LaunchedEffect(fromCryptoCurrency, feePaidCryptoCurrency, amount.isNullOrBlank()) {
|
||||
if (amount.isNullOrBlank()) {
|
||||
LaunchedEffect(fromCryptoCurrency, feePaidCryptoCurrency, shouldHideBlock) {
|
||||
if (shouldHideBlock) {
|
||||
slotNavigation.dismiss()
|
||||
return@LaunchedEffect
|
||||
}
|
||||
|
|
@ -201,6 +205,10 @@ internal class DefaultSwapComponent @AssistedInject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun toBigDecimalOrZero(bigDecimalString: String?): BigDecimal {
|
||||
return bigDecimalString?.replace(",", ".")?.toBigDecimalOrNull() ?: BigDecimal.ZERO
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : SwapComponent.Factory {
|
||||
override fun create(context: AppComponentContext, params: SwapComponent.Params): DefaultSwapComponent
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ 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.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.utils.InputNumberFormatter
|
||||
|
|
@ -160,6 +161,7 @@ internal class SwapModel @Inject constructor(
|
|||
private val getUserWalletsUseCase: GetWalletsUseCase,
|
||||
private val getTangemPayCustomerIdUseCase: GetTangemPayCustomerIdUseCase,
|
||||
private val appsFlyerStore: AppsFlyerStore,
|
||||
private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<SwapComponent.Params>()
|
||||
|
|
@ -192,6 +194,7 @@ internal class SwapModel @Inject constructor(
|
|||
appCurrencyProvider = Provider(selectedAppCurrencyFlow::value),
|
||||
isAccountsModeProvider = Provider { isAccountsMode },
|
||||
iGaslessFeeSupportedForNetwork = iGaslessFeeSupportedForNetwork,
|
||||
holdToConfirmButtonFeatureToggles = holdToConfirmButtonFeatureToggles,
|
||||
)
|
||||
|
||||
private val inputNumberFormatter =
|
||||
|
|
@ -2200,6 +2203,11 @@ internal class SwapModel @Inject constructor(
|
|||
return
|
||||
}
|
||||
|
||||
if (newState is FeeSelectorUM.Error) {
|
||||
state.value = newState.copy(isHidden = true)
|
||||
return
|
||||
}
|
||||
|
||||
state.value = newState
|
||||
|
||||
// If fee currency is same as from currency, we need to reload quotes to update fee info
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency
|
|||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.isHotWallet
|
||||
import com.tangem.domain.promo.models.StoryContent
|
||||
|
|
@ -60,7 +61,7 @@ import kotlin.math.min
|
|||
/**
|
||||
* State builder creates a specific states for SwapScreen
|
||||
*/
|
||||
@Suppress("LargeClass", "TooManyFunctions")
|
||||
@Suppress("LargeClass", "TooManyFunctions", "LongParameterList")
|
||||
internal class StateBuilder(
|
||||
private val userWalletProvider: Provider<UserWallet>,
|
||||
private val actions: UiActions,
|
||||
|
|
@ -68,8 +69,12 @@ internal class StateBuilder(
|
|||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val isAccountsModeProvider: Provider<Boolean>,
|
||||
private val iGaslessFeeSupportedForNetwork: IsGaslessFeeSupportedForNetwork,
|
||||
private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
|
||||
) {
|
||||
|
||||
private val isHoldToConfirmEnabled: Boolean =
|
||||
holdToConfirmButtonFeatureToggles.isHoldToConfirmEnabled && userWalletProvider().isHotWallet
|
||||
|
||||
private val iconStateConverter by lazy(::CryptoCurrencyToIconStateConverter)
|
||||
|
||||
private val tokensDataConverter = TokensDataConverter(
|
||||
|
|
@ -127,7 +132,7 @@ internal class StateBuilder(
|
|||
swapButton = SwapButton(
|
||||
walletInteractionIcon = walletInterationIcon(userWalletProvider()),
|
||||
isEnabled = false,
|
||||
isHoldToConfirm = userWalletProvider().isHotWallet,
|
||||
isHoldToConfirm = isHoldToConfirmEnabled,
|
||||
onClick = {},
|
||||
),
|
||||
onRefresh = {},
|
||||
|
|
@ -187,7 +192,7 @@ internal class StateBuilder(
|
|||
swapButton = SwapButton(
|
||||
walletInteractionIcon = walletInterationIcon(userWalletProvider()),
|
||||
isEnabled = false,
|
||||
isHoldToConfirm = userWalletProvider().isHotWallet,
|
||||
isHoldToConfirm = isHoldToConfirmEnabled,
|
||||
onClick = { },
|
||||
),
|
||||
changeCardsButtonState = ChangeCardsButtonState.DISABLED,
|
||||
|
|
@ -253,7 +258,7 @@ internal class StateBuilder(
|
|||
swapButton = SwapButton(
|
||||
walletInteractionIcon = walletInterationIcon(userWalletProvider()),
|
||||
isEnabled = false,
|
||||
isHoldToConfirm = userWalletProvider().isHotWallet,
|
||||
isHoldToConfirm = isHoldToConfirmEnabled,
|
||||
onClick = {},
|
||||
),
|
||||
providerState = ProviderState.Loading(),
|
||||
|
|
@ -376,7 +381,7 @@ internal class StateBuilder(
|
|||
swapButton = SwapButton(
|
||||
walletInteractionIcon = walletInterationIcon(userWalletProvider()),
|
||||
isEnabled = getSwapButtonEnabled(notifications),
|
||||
isHoldToConfirm = userWalletProvider().isHotWallet,
|
||||
isHoldToConfirm = isHoldToConfirmEnabled,
|
||||
onClick = actions.onSwapClick,
|
||||
),
|
||||
changeCardsButtonState = getChangeCardsButtonState(isReverseSwapPossible),
|
||||
|
|
@ -505,7 +510,7 @@ internal class StateBuilder(
|
|||
swapButton = SwapButton(
|
||||
walletInteractionIcon = walletInterationIcon(userWalletProvider()),
|
||||
isEnabled = false,
|
||||
isHoldToConfirm = userWalletProvider().isHotWallet,
|
||||
isHoldToConfirm = isHoldToConfirmEnabled,
|
||||
onClick = actions.onSwapClick,
|
||||
),
|
||||
changeCardsButtonState = getChangeCardsButtonState(isReverseSwapPossible),
|
||||
|
|
@ -603,7 +608,7 @@ internal class StateBuilder(
|
|||
swapButton = SwapButton(
|
||||
walletInteractionIcon = walletInterationIcon(userWalletProvider()),
|
||||
isEnabled = false,
|
||||
isHoldToConfirm = userWalletProvider().isHotWallet,
|
||||
isHoldToConfirm = isHoldToConfirmEnabled,
|
||||
onClick = { },
|
||||
),
|
||||
changeCardsButtonState = getChangeCardsButtonState(isReverseSwapPossible),
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ internal object TangemPayTxHistoryDetailsConverter :
|
|||
is TangemPayTxHistoryItem.Spend -> {
|
||||
val amountPrefix = when {
|
||||
this.amount.isZero() -> ""
|
||||
this.status == TangemPayTxHistoryItem.Status.REVERSED -> StringsSigns.MINUS
|
||||
this.status == TangemPayTxHistoryItem.Status.DECLINED ||
|
||||
this.amount.isPositive() -> StringsSigns.MINUS
|
||||
else -> StringsSigns.PLUS
|
||||
|
|
@ -205,6 +206,10 @@ internal object TangemPayTxHistoryDetailsConverter :
|
|||
text = resourceReference(R.string.tangem_pay_status_declined),
|
||||
style = LabelStyle.WARNING,
|
||||
)
|
||||
TangemPayTxHistoryItem.Status.REVERSED -> LabelUM(
|
||||
text = resourceReference(R.string.tangem_pay_status_reversed),
|
||||
style = LabelStyle.REGULAR,
|
||||
)
|
||||
TangemPayTxHistoryItem.Status.RESERVED,
|
||||
TangemPayTxHistoryItem.Status.UNKNOWN,
|
||||
-> null
|
||||
|
|
@ -227,24 +232,33 @@ internal object TangemPayTxHistoryDetailsConverter :
|
|||
containerColor = null,
|
||||
)
|
||||
is TangemPayTxHistoryItem.Spend -> when (this.status) {
|
||||
TangemPayTxHistoryItem.Status.DECLINED ->
|
||||
TangemPayTxHistoryDetailsUM.NotificationState(
|
||||
config = NotificationConfig(
|
||||
title = if (declinedReason.isNullOrEmpty()) {
|
||||
resourceReference(R.string.tangem_pay_transaction_declined_notification_text)
|
||||
} else {
|
||||
resourceReference(
|
||||
id = R.string.tangem_pay_history_item_spend_mc_declined_reason,
|
||||
formatArgs = wrappedList(requireNotNull(declinedReason)),
|
||||
)
|
||||
},
|
||||
subtitle = TextReference.EMPTY,
|
||||
iconResId = R.drawable.ic_token_info_24,
|
||||
),
|
||||
titleColor = themedColor { TangemTheme.colors.text.warning },
|
||||
iconTint = themedColor { TangemTheme.colors.icon.warning },
|
||||
containerColor = themedColor { TangemColorPalette.Amaranth.copy(alpha = 0.1F) },
|
||||
)
|
||||
TangemPayTxHistoryItem.Status.DECLINED -> TangemPayTxHistoryDetailsUM.NotificationState(
|
||||
config = NotificationConfig(
|
||||
title = if (declinedReason.isNullOrEmpty()) {
|
||||
resourceReference(R.string.tangem_pay_transaction_declined_notification_text)
|
||||
} else {
|
||||
resourceReference(
|
||||
id = R.string.tangem_pay_history_item_spend_mc_declined_reason,
|
||||
formatArgs = wrappedList(requireNotNull(declinedReason)),
|
||||
)
|
||||
},
|
||||
subtitle = TextReference.EMPTY,
|
||||
iconResId = R.drawable.ic_token_info_24,
|
||||
),
|
||||
titleColor = themedColor { TangemTheme.colors.text.warning },
|
||||
iconTint = themedColor { TangemTheme.colors.icon.warning },
|
||||
containerColor = themedColor { TangemColorPalette.Amaranth.copy(alpha = 0.1F) },
|
||||
)
|
||||
TangemPayTxHistoryItem.Status.REVERSED -> TangemPayTxHistoryDetailsUM.NotificationState(
|
||||
config = NotificationConfig(
|
||||
title = resourceReference(R.string.tangem_pay_transaction_reversed_notification_text),
|
||||
subtitle = TextReference.EMPTY,
|
||||
iconResId = R.drawable.ic_token_info_24,
|
||||
),
|
||||
titleColor = themedColor { TangemTheme.colors.text.tertiary },
|
||||
iconTint = themedColor { TangemTheme.colors.icon.secondary },
|
||||
containerColor = null,
|
||||
)
|
||||
TangemPayTxHistoryItem.Status.PENDING,
|
||||
TangemPayTxHistoryItem.Status.COMPLETED,
|
||||
TangemPayTxHistoryItem.Status.RESERVED,
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ internal class TangemPayTxHistoryItemsConverter(
|
|||
val localDate = spend.date.withZone(DateTimeZone.getDefault())
|
||||
val amountPrefix = when {
|
||||
spend.amount.isZero() -> ""
|
||||
spend.status == TangemPayTxHistoryItem.Status.REVERSED -> StringsSigns.MINUS
|
||||
spend.status == TangemPayTxHistoryItem.Status.DECLINED || spend.amount.isPositive() -> StringsSigns.MINUS
|
||||
else -> StringsSigns.PLUS
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ private const val NFT_COLLECTIONS_CONTENT_TYPE = "NFTCollections"
|
|||
internal fun LazyListScope.nftCollections(state: WalletNFTItemUM, modifier: Modifier = Modifier) {
|
||||
item(key = NFT_COLLECTIONS_CONTENT_TYPE, contentType = NFT_COLLECTIONS_CONTENT_TYPE) {
|
||||
WalletNFTItem(
|
||||
modifier = modifier.animateItem(),
|
||||
modifier = modifier,
|
||||
state = state,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ internal fun LazyListScope.organizeTokensButton(
|
|||
) {
|
||||
item(key = ORGANIZE_BUTTON_CONTENT_TYPE, contentType = ORGANIZE_BUTTON_CONTENT_TYPE) {
|
||||
RoundedActionButton(
|
||||
modifier = modifier.animateItem().testTag(MainScreenTestTags.ORGANIZE_TOKENS_BUTTON),
|
||||
modifier = modifier.testTag(MainScreenTestTags.ORGANIZE_TOKENS_BUTTON),
|
||||
config = ActionButtonConfig(
|
||||
text = resourceReference(id = R.string.organize_tokens_title),
|
||||
iconResId = R.drawable.ic_filter_24,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import com.tangem.features.walletconnect.transaction.entity.common.WcTransaction
|
|||
import com.tangem.features.walletconnect.transaction.entity.send.WcSendTransactionItemUM
|
||||
import com.tangem.features.walletconnect.transaction.entity.send.WcSendTransactionUM
|
||||
import com.tangem.features.walletconnect.utils.WcNotificationsFactory
|
||||
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
|
||||
import com.tangem.domain.models.wallet.isHotWallet
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
|
@ -26,6 +27,7 @@ internal class WcSendTransactionUMConverter @Inject constructor(
|
|||
private val networkInfoUMConverter: WcNetworkInfoUMConverter,
|
||||
private val requestBlockUMConverter: WcTransactionRequestBlockUMConverter,
|
||||
private val notificationsFactory: WcNotificationsFactory,
|
||||
private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
|
||||
) : Converter<WcSendTransactionUMConverter.Input, WcSendTransactionUM?> {
|
||||
|
||||
override fun convert(value: Input): WcSendTransactionUM? {
|
||||
|
|
@ -63,7 +65,8 @@ internal class WcSendTransactionUMConverter @Inject constructor(
|
|||
}
|
||||
},
|
||||
feeErrorNotification = feeErrorNotification,
|
||||
isHoldToConfirmEnabled = value.context.session.wallet.isHotWallet,
|
||||
isHoldToConfirmEnabled = holdToConfirmButtonFeatureToggles.isHoldToConfirmEnabled &&
|
||||
value.context.session.wallet.isHotWallet,
|
||||
),
|
||||
feeSelectorUM = when (value.feeState) {
|
||||
WcTransactionFeeState.None -> FeeSelectorUM.Loading
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import com.tangem.features.walletconnect.transaction.entity.common.WcTransaction
|
|||
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionRequestInfoUM
|
||||
import com.tangem.features.walletconnect.transaction.entity.sign.WcSignTransactionItemUM
|
||||
import com.tangem.features.walletconnect.transaction.entity.sign.WcSignTransactionUM
|
||||
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
|
||||
import com.tangem.domain.models.wallet.isHotWallet
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
|
@ -19,6 +20,7 @@ internal class WcSignTransactionUMConverter @Inject constructor(
|
|||
private val appInfoContentUMConverter: WcTransactionAppInfoContentUMConverter,
|
||||
private val networkInfoUMConverter: WcNetworkInfoUMConverter,
|
||||
private val requestBlockUMConverter: WcTransactionRequestBlockUMConverter,
|
||||
private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
|
||||
) : Converter<WcSignTransactionUMConverter.Input, WcSignTransactionUM> {
|
||||
|
||||
override fun convert(value: Input) = WcSignTransactionUM(
|
||||
|
|
@ -36,7 +38,8 @@ internal class WcSignTransactionUMConverter @Inject constructor(
|
|||
isLoading = value.signState.domainStep == WcSignStep.Signing,
|
||||
address = WcAddressConverter.convert(value.context.derivationState),
|
||||
walletInteractionIcon = walletInterationIcon(value.context.session.wallet),
|
||||
isHoldToConfirmEnabled = value.context.session.wallet.isHotWallet,
|
||||
isHoldToConfirmEnabled = holdToConfirmButtonFeatureToggles.isHoldToConfirmEnabled &&
|
||||
value.context.session.wallet.isHotWallet,
|
||||
),
|
||||
transactionRequestInfo = WcTransactionRequestInfoUM(
|
||||
requestBlockUMConverter.convert(
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import com.tangem.features.walletconnect.transaction.entity.common.WcTransaction
|
|||
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionRequestInfoUM
|
||||
import com.tangem.features.walletconnect.transaction.entity.sign.WcSignTransactionItemUM
|
||||
import com.tangem.features.walletconnect.transaction.entity.sign.WcSignTransactionUM
|
||||
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
|
||||
import com.tangem.domain.models.wallet.isHotWallet
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
|
@ -19,6 +20,7 @@ internal class WcSignTypedDataUMConverter @Inject constructor(
|
|||
private val appInfoContentUMConverter: WcTransactionAppInfoContentUMConverter,
|
||||
private val networkInfoUMConverter: WcNetworkInfoUMConverter,
|
||||
private val requestBlockUMConverter: WcTransactionRequestBlockUMConverter,
|
||||
private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
|
||||
) : Converter<WcSignTypedDataUMConverter.Input, WcSignTransactionUM> {
|
||||
|
||||
override fun convert(value: Input): WcSignTransactionUM = WcSignTransactionUM(
|
||||
|
|
@ -36,7 +38,8 @@ internal class WcSignTypedDataUMConverter @Inject constructor(
|
|||
address = WcAddressConverter.convert(value.context.derivationState),
|
||||
isLoading = value.signState.domainStep == WcSignStep.Signing,
|
||||
walletInteractionIcon = walletInterationIcon(value.context.session.wallet),
|
||||
isHoldToConfirmEnabled = value.context.session.wallet.isHotWallet,
|
||||
isHoldToConfirmEnabled = holdToConfirmButtonFeatureToggles.isHoldToConfirmEnabled &&
|
||||
value.context.session.wallet.isHotWallet,
|
||||
),
|
||||
transactionRequestInfo = WcTransactionRequestInfoUM(
|
||||
blocks = buildList {
|
||||
|
|
|
|||
|
|
@ -254,7 +254,7 @@ internal class WelcomeModel @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun canUnlockWithBiometrics(): Boolean {
|
||||
return canUseBiometryUseCase() && walletsRepository.useBiometricAuthentication()
|
||||
return canUseBiometryUseCase.strict() && walletsRepository.useBiometricAuthentication()
|
||||
}
|
||||
|
||||
private suspend fun nonBiometricUnlockWallet(userWalletId: UserWalletId) {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ 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.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
|
||||
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
|
|
@ -62,6 +63,7 @@ internal class YieldSupplyApproveModel @Inject constructor(
|
|||
private val yieldSupplyGetContractAddressUseCase: YieldSupplyGetContractAddressUseCase,
|
||||
private val yieldSupplyPendingTracker: YieldSupplyPendingTracker,
|
||||
private val yieldSupplyAlertFactory: YieldSupplyAlertFactory,
|
||||
private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
|
||||
) : Model(), YieldSupplyNotificationsComponent.ModelCallback {
|
||||
|
||||
private val params: YieldSupplyApproveComponent.Params = paramsContainer.require()
|
||||
|
|
@ -98,7 +100,8 @@ internal class YieldSupplyApproveModel @Inject constructor(
|
|||
yieldSupplyFeeUM = YieldSupplyFeeUM.Loading,
|
||||
isPrimaryButtonEnabled = false,
|
||||
isTransactionSending = false,
|
||||
isHoldToConfirmEnabled = params.userWallet.isHotWallet,
|
||||
isHoldToConfirmEnabled = holdToConfirmButtonFeatureToggles.isHoldToConfirmEnabled &&
|
||||
params.userWallet.isHotWallet,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import com.tangem.core.analytics.models.Basic
|
|||
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.ui.HoldToConfirmButtonFeatureToggles
|
||||
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
|
|
@ -69,6 +70,7 @@ internal class YieldSupplyStartEarningModel @Inject constructor(
|
|||
private val yieldSupplyRepository: YieldSupplyRepository,
|
||||
private val yieldSupplyPendingTracker: YieldSupplyPendingTracker,
|
||||
private val appsFlyerStore: AppsFlyerStore,
|
||||
private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
|
||||
) : Model(), YieldSupplyNotificationsComponent.ModelCallback {
|
||||
|
||||
private val params: YieldSupplyStartEarningComponent.Params = paramsContainer.require()
|
||||
|
|
@ -313,7 +315,10 @@ internal class YieldSupplyStartEarningModel @Inject constructor(
|
|||
ifRight = { wallet ->
|
||||
userWallet = wallet
|
||||
uiState.update {
|
||||
it.copy(isHoldToConfirmEnabled = wallet.isHotWallet)
|
||||
it.copy(
|
||||
isHoldToConfirmEnabled = holdToConfirmButtonFeatureToggles.isHoldToConfirmEnabled &&
|
||||
wallet.isHotWallet,
|
||||
)
|
||||
}
|
||||
getCurrenciesStatusUpdates()
|
||||
},
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ 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.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
|
||||
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
|
|
@ -66,6 +67,7 @@ internal class YieldSupplyStopEarningModel @Inject constructor(
|
|||
private val yieldSupplyRepository: YieldSupplyRepository,
|
||||
private val yieldSupplyPendingTracker: YieldSupplyPendingTracker,
|
||||
private val appsFlyerStore: AppsFlyerStore,
|
||||
private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
|
||||
) : Model(), YieldSupplyNotificationsComponent.ModelCallback {
|
||||
|
||||
private val params: YieldSupplyStopEarningComponent.Params = paramsContainer.require()
|
||||
|
|
@ -102,7 +104,8 @@ internal class YieldSupplyStopEarningModel @Inject constructor(
|
|||
yieldSupplyFeeUM = YieldSupplyFeeUM.Loading,
|
||||
isPrimaryButtonEnabled = false,
|
||||
isTransactionSending = false,
|
||||
isHoldToConfirmEnabled = params.userWallet.isHotWallet,
|
||||
isHoldToConfirmEnabled = holdToConfirmButtonFeatureToggles.isHoldToConfirmEnabled &&
|
||||
params.userWallet.isHotWallet,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
# https://github.com/tangem/tangem-sdk-android/
|
||||
# https://github.com/tangem/vico
|
||||
|
||||
tangemBlockchainSdk = "develop-1419"
|
||||
tangemBlockchainSdk = "develop-1425"
|
||||
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
|
||||
tangemCardSdk = "develop-577"
|
||||
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ internal enum class ProviderTypeIdMapping(val id: String, val providerType: Prov
|
|||
TronGrid(id = "tron", providerType = ProviderType.Tron.TronGrid),
|
||||
BittensorDwellir(id = "dwellirBittensor", providerType = ProviderType.Bittensor.Dwellir),
|
||||
BittensorOnfinality(id = "onfinalityBittensor", providerType = ProviderType.Bittensor.Onfinality),
|
||||
AlephZeroDwellir(id = "dwellirAlephZero", providerType = ProviderType.AlephZero.Dwellir),
|
||||
KoinosPro(id = "koinospro", providerType = ProviderType.Koinos.KoinosPro),
|
||||
AlephiumTangem(id = "tangemAlephium", providerType = ProviderType.Alephium.Tangem),
|
||||
Blink(id = "blink", providerType = ProviderType.Blink),
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ internal enum class BuildType(
|
|||
BuildConfigField.LogEnabled(isEnabled = true),
|
||||
BuildConfigField.TesterMenuAvailability(isEnabled = true),
|
||||
BuildConfigField.MockDataSource(isEnabled = false),
|
||||
BuildConfigField.ABTestsEnabled(isEnabled = true),
|
||||
BuildConfigField.ABTestsEnabled(isEnabled = false),
|
||||
),
|
||||
),
|
||||
|
||||
|
|
@ -114,7 +114,7 @@ internal enum class BuildType(
|
|||
BuildConfigField.LogEnabled(isEnabled = false),
|
||||
BuildConfigField.TesterMenuAvailability(isEnabled = false),
|
||||
BuildConfigField.MockDataSource(isEnabled = false),
|
||||
BuildConfigField.ABTestsEnabled(isEnabled = true),
|
||||
BuildConfigField.ABTestsEnabled(isEnabled = false),
|
||||
),
|
||||
),
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue