diff --git a/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt b/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt index 21ab576e7f..472b865f49 100644 --- a/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt +++ b/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt @@ -2,6 +2,7 @@ package com.tangem.tap import com.tangem.TangemSdkLogger import com.tangem.blockchainsdk.BlockchainSDKFactory +import com.tangem.blockchainsdk.signer.TransactionSignerFactory import com.tangem.common.routing.AppRouter import com.tangem.core.analytics.filter.OneTimeEventFilter import com.tangem.core.featuretoggle.manager.FeatureTogglesManager @@ -119,4 +120,6 @@ interface ApplicationEntryPoint { fun getPushNotificationsFeatureToggles(): PushNotificationsFeatureToggles fun getTangemAppLogger(): TangemAppLoggerInitializer + + fun getTransactionSignerFactory(): TransactionSignerFactory } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/TangemApplication.kt b/app/src/main/java/com/tangem/tap/TangemApplication.kt index ef8490029a..6b8016f11f 100644 --- a/app/src/main/java/com/tangem/tap/TangemApplication.kt +++ b/app/src/main/java/com/tangem/tap/TangemApplication.kt @@ -11,9 +11,15 @@ import com.tangem.LogFormat import com.tangem.TangemSdkLogger import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder import com.tangem.blockchainsdk.BlockchainSDKFactory +import com.tangem.blockchainsdk.signer.TransactionSignerFactory import com.tangem.common.routing.AppRouter import com.tangem.core.analytics.Analytics +import com.tangem.core.analytics.api.ParamsInterceptor import com.tangem.core.analytics.filter.OneTimeEventFilter +import com.tangem.core.analytics.models.AnalyticsEvent +import com.tangem.core.analytics.models.AnalyticsParam +import com.tangem.core.analytics.models.Basic +import com.tangem.core.analytics.models.Basic.TransactionSent.WalletForm import com.tangem.core.featuretoggle.manager.FeatureTogglesManager import com.tangem.datasource.api.common.MoshiConverter import com.tangem.datasource.api.common.createNetworkLoggingInterceptor @@ -191,6 +197,9 @@ abstract class TangemApplication : Application(), ImageLoaderFactory { private val tangemAppLoggerInitializer: TangemAppLoggerInitializer get() = entryPoint.getTangemAppLogger() + + private val transactionSignerFactory: TransactionSignerFactory + get() = entryPoint.getTransactionSignerFactory() // endregion override fun onCreate() { @@ -273,6 +282,7 @@ abstract class TangemApplication : Application(), ImageLoaderFactory { shareManager = shareManager, appRouter = appRouter, pushNotificationsFeatureToggles = pushNotificationsFeatureToggles, + transactionSignerFactory = transactionSignerFactory, ), ), ) @@ -315,6 +325,25 @@ abstract class TangemApplication : Application(), ImageLoaderFactory { logConfig = LogConfig.analyticsHandlers, jsonConverter = MoshiConverter.sdkMoshiConverter, ) + + Analytics.addParamsInterceptor( + interceptor = object : ParamsInterceptor { + override fun id(): String = "SendTransactionSignerInfoInterceptor" + + override fun canBeAppliedTo(event: AnalyticsEvent): Boolean = event is Basic.TransactionSent + + override fun intercept(params: MutableMap) { + val isLastSignWithRing = store.state.globalState.isLastSignWithRing + + params[AnalyticsParam.WALLET_FORM] = if (isLastSignWithRing) { + WalletForm.Ring.name + } else { + WalletForm.Card.name + } + } + }, + ) + factory.build(Analytics, buildData) // ExceptionHandler.append(blockchainExceptionHandler) TODO: [REDACTED_JIRA] } diff --git a/app/src/main/java/com/tangem/tap/common/analytics/paramsInterceptor/CardContextInterceptor.kt b/app/src/main/java/com/tangem/tap/common/analytics/paramsInterceptor/CardContextInterceptor.kt index e7c6feee04..edc221250e 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/paramsInterceptor/CardContextInterceptor.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/paramsInterceptor/CardContextInterceptor.kt @@ -10,7 +10,11 @@ import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.IntroductionProcess import com.tangem.tap.common.analytics.events.MainScreen +import com.tangem.tap.common.extensions.inject import com.tangem.tap.features.demo.DemoHelper +import com.tangem.tap.proxy.redux.DaggerGraphState +import com.tangem.tap.store +import kotlinx.coroutines.runBlocking /** [REDACTED_AUTHOR] @@ -19,6 +23,8 @@ class CardContextInterceptor( private val scanResponse: ScanResponse, ) : ParamsInterceptor { + private val walletsRepository = store.inject(DaggerGraphState::walletsRepository) + private val userWalletId = UserWalletIdBuilder.scanResponse(scanResponse).build() override fun id(): String = CardContextInterceptor.id() @@ -33,7 +39,7 @@ class CardContextInterceptor( override fun intercept(params: MutableMap) { val card = scanResponse.card params[AnalyticsParam.BATCH] = card.batchId - params[AnalyticsParam.PRODUCT_TYPE] = getProductType(scanResponse) + params[AnalyticsParam.PRODUCT_TYPE] = getProductType() params[AnalyticsParam.FIRMWARE] = card.firmwareVersion.stringValue if (userWalletId != null) { params[AnalyticsParam.USER_WALLET_ID] = userWalletId.stringValue @@ -44,7 +50,13 @@ class CardContextInterceptor( } } - private fun getProductType(scanResponse: ScanResponse): String { + private fun getProductType(): String { + if (scanResponse.productType != ProductType.Ring && userWalletId != null) { + val isWalletWithRing = runBlocking { walletsRepository.isWalletWithRing(userWalletId) } + + if (isWalletWithRing) return "Ring" + } + return when (scanResponse.productType) { ProductType.Note -> "Note" ProductType.Twins -> "Twin" @@ -53,18 +65,18 @@ class CardContextInterceptor( ProductType.Ring -> "Ring" ProductType.Start2Coin -> "Start2Coin" ProductType.Visa -> "VISA" - else -> if (DemoHelper.isDemoCard(scanResponse)) { - if (DemoHelper.isTestDemoCard(scanResponse)) { - "Demo Test" - } else { - when (scanResponse.card.cardId.substring(0..1)) { - "AC" -> "Demo Wallet" - "AB" -> "Demo Note" - else -> "Demo Other" - } - } - } else { - "Other" + else -> if (DemoHelper.isDemoCard(scanResponse)) getDemoCardProductType() else "Other" + } + } + + private fun getDemoCardProductType(): String { + return if (DemoHelper.isTestDemoCard(scanResponse)) { + "Demo Test" + } else { + when (scanResponse.card.cardId.substring(0..1)) { + "AC" -> "Demo Wallet" + "AB" -> "Demo Note" + else -> "Demo Other" } } } diff --git a/app/src/main/java/com/tangem/tap/common/libs/blockchainsdk/DefaultTransactionSignerFactory.kt b/app/src/main/java/com/tangem/tap/common/libs/blockchainsdk/DefaultTransactionSignerFactory.kt new file mode 100644 index 0000000000..edd0ded835 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/libs/blockchainsdk/DefaultTransactionSignerFactory.kt @@ -0,0 +1,22 @@ +package com.tangem.tap.common.libs.blockchainsdk + +import com.tangem.Message +import com.tangem.TangemSdk +import com.tangem.blockchain.common.TransactionSigner +import com.tangem.blockchainsdk.signer.TransactionSignerFactory +import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.domain.TangemSigner +import com.tangem.tap.store + +internal class DefaultTransactionSignerFactory : TransactionSignerFactory { + + override fun createTransactionSigner(cardId: String?, sdk: TangemSdk): TransactionSigner { + return TangemSigner( + cardId = cardId, + tangemSdk = sdk, + initialMessage = Message(), + ) { signResponse -> + store.dispatch(action = GlobalAction.IsSignWithRing(signResponse.isRing)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt index 2b9e953b46..909760ca25 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt @@ -69,6 +69,8 @@ sealed class GlobalAction : Action { val walletPublicKey: ByteArray, ) : GlobalAction() + data class IsSignWithRing(val isSignWithRing: Boolean) : GlobalAction() + data class SetConfigManager(val configManager: ConfigManager) : GlobalAction() data class SetFeedbackManager(val feedbackManager: LegacyFeedbackManager) : GlobalAction() diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt index 6b011082d2..4d66eb4670 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt @@ -71,6 +71,7 @@ fun globalReducer(action: Action, state: AppState, appStateHolder: AppStateHolde ) globalState.copy(scanResponse = globalState.scanResponse.copy(card = newCardInstance)) } + is GlobalAction.IsSignWithRing -> globalState.copy(isLastSignWithRing = action.isSignWithRing) is GlobalAction.SetFeedbackManager -> { globalState.copy(feedbackManager = action.feedbackManager) } diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt index 420951087b..48f7bce192 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt @@ -25,6 +25,7 @@ data class GlobalState( val exchangeManager: CurrencyExchangeManager = CurrencyExchangeManager.dummy(), val userCountryCode: String? = null, val appThemeMode: AppThemeMode = AppThemeMode.DEFAULT, + val isLastSignWithRing: Boolean = false, ) : StateType typealias CryptoCurrencyName = String diff --git a/app/src/main/java/com/tangem/tap/di/domain/SettingsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/SettingsDomainModule.kt index 1b5860c2c0..1cc943d75b 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/SettingsDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/SettingsDomainModule.kt @@ -123,6 +123,14 @@ internal object SettingsDomainModule { return ShouldShowSwapPromoWalletUseCase(promoSettingsRepository) } + @Provides + @Singleton + fun provideShouldShowRingPromoUseCase( + promoSettingsRepository: PromoSettingsRepository, + ): ShouldShowRingPromoUseCase { + return ShouldShowRingPromoUseCase(promoSettingsRepository) + } + @Provides @Singleton fun provideShouldShowTravalaPromoWalletUseCase( diff --git a/app/src/main/java/com/tangem/tap/di/libs/blockchainsdk/TransactionSignerFactoryModule.kt b/app/src/main/java/com/tangem/tap/di/libs/blockchainsdk/TransactionSignerFactoryModule.kt new file mode 100644 index 0000000000..36bc5ce13a --- /dev/null +++ b/app/src/main/java/com/tangem/tap/di/libs/blockchainsdk/TransactionSignerFactoryModule.kt @@ -0,0 +1,23 @@ +package com.tangem.tap.di.libs.blockchainsdk + +import com.tangem.blockchainsdk.signer.TransactionSignerFactory +import com.tangem.tap.common.libs.blockchainsdk.DefaultTransactionSignerFactory +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +/** +[REDACTED_AUTHOR] + */ +@Module +@InstallIn(SingletonComponent::class) +internal class TransactionSignerFactoryModule { + + @Provides + @Singleton + fun provideTransactionSignerFactory(): TransactionSignerFactory { + return DefaultTransactionSignerFactory() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt b/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt index f316b2e433..f53d9d0736 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt @@ -5,17 +5,15 @@ import com.tangem.TangemSdk import com.tangem.blockchain.common.TransactionSigner import com.tangem.blockchain.common.Wallet import com.tangem.common.CompletionResult -import com.tangem.domain.common.TapWorkarounds.isTangemTwins -import com.tangem.domain.models.scan.CardDTO +import com.tangem.domain.models.scan.isRing import com.tangem.tap.domain.tasks.SignHashesTask import kotlinx.coroutines.suspendCancellableCoroutine import kotlin.coroutines.resume class TangemSigner( - private val card: CardDTO, + private val cardId: String?, private val tangemSdk: TangemSdk, private val initialMessage: Message, - private val accessCode: String? = null, private val signerCallback: (TangemSignerResponse) -> Unit, ) : TransactionSigner { @@ -24,21 +22,20 @@ class TangemSigner( publicKey: Wallet.PublicKey, ): CompletionResult> { return suspendCancellableCoroutine { continuation -> - val isCardNotBackedUp = card.backupStatus?.isActive != true && !card.isTangemTwins val task = SignHashesTask(hashes, publicKey) tangemSdk.startSessionWithRunnable( runnable = task, - cardId = card.cardId.takeIf { isCardNotBackedUp }, + cardId = cardId, initialMessage = initialMessage, - accessCode = accessCode, ) { result -> when (result) { is CompletionResult.Success -> { signerCallback( TangemSignerResponse( - result.data.totalSignedHashes, - result.data.remainingSignatures, + totalSignedHashes = result.data.totalSignedHashes, + remainingSignatures = result.data.remainingSignatures, + isRing = result.data.batchId?.let(::isRing) ?: false, ), ) if (continuation.isActive) { @@ -70,4 +67,5 @@ class TangemSigner( data class TangemSignerResponse( val totalSignedHashes: Int?, val remainingSignatures: Int?, + val isRing: Boolean, ) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/sdk/TangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/sdk/TangemSdkManager.kt index 2dbfaec5b7..8b6a8cc74f 100644 --- a/app/src/main/java/com/tangem/tap/domain/sdk/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/sdk/TangemSdkManager.kt @@ -134,5 +134,9 @@ interface TangemSdkManager { initialMessage: Message, ): CompletionResult + fun changeProductType(isRing: Boolean) + + fun clearProductType() + // endregion } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/sdk/impl/DefaultTangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/sdk/impl/DefaultTangemSdkManager.kt index 835a2fee69..c2d455b867 100644 --- a/app/src/main/java/com/tangem/tap/domain/sdk/impl/DefaultTangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/sdk/impl/DefaultTangemSdkManager.kt @@ -9,7 +9,6 @@ import com.tangem.TangemSdk import com.tangem.common.* import com.tangem.common.authentication.AuthenticationManager import com.tangem.common.authentication.keystore.KeystoreManager -import com.tangem.common.card.FirmwareVersion import com.tangem.common.core.* import com.tangem.common.extensions.ByteArrayKey import com.tangem.common.services.secure.SecureStorage @@ -137,6 +136,14 @@ class DefaultTangemSdkManager( scanResponse: ScanResponse, shouldReset: Boolean, ): CompletionResult { + tangemSdk.config.setupForProduct( + if (scanResponse.cardTypesResolver.isRing()) { + ProductType.RING + } else { + ProductType.CARD + }, + ) + return runTaskAsync( runnable = CreateProductWalletTask( cardTypesResolver = scanResponse.cardTypesResolver, @@ -144,9 +151,14 @@ class DefaultTangemSdkManager( shouldReset = shouldReset, ), cardId = scanResponse.card.cardId, - initialMessage = Message(resources.getString(R.string.initial_message_create_wallet_body)), + initialMessage = if (scanResponse.cardTypesResolver.isRing()) { + Message(resources.getString(R.string.initial_message_create_wallet_body_ring)) + } else { + Message(resources.getString(R.string.initial_message_create_wallet_body)) + }, iconScanRes = if (scanResponse.cardTypesResolver.isRing()) R.drawable.img_hand_scan_ring else null, ) + .doOnResult { tangemSdk.config.setupForProduct(ProductType.ANY) } } override suspend fun importWallet( @@ -160,17 +172,31 @@ class DefaultTangemSdkManager( } catch (e: TangemSdkError.MnemonicException) { return CompletionResult.Failure(e) } + + tangemSdk.config.setupForProduct( + if (scanResponse.cardTypesResolver.isRing()) { + ProductType.RING + } else { + ProductType.CARD + }, + ) + return runTaskAsync( - CreateProductWalletTask( + runnable = CreateProductWalletTask( cardTypesResolver = scanResponse.cardTypesResolver, derivationStyleProvider = scanResponse.derivationStyleProvider, mnemonic = defaultMnemonic, passphrase = passphrase, shouldReset = shouldReset, ), - scanResponse.card.cardId, - Message(resources.getString(R.string.initial_message_create_wallet_body)), + cardId = scanResponse.card.cardId, + initialMessage = if (scanResponse.cardTypesResolver.isRing()) { + Message(resources.getString(R.string.initial_message_create_wallet_body_ring)) + } else { + Message(resources.getString(R.string.initial_message_create_wallet_body)) + }, ) + .doOnResult { tangemSdk.config.setupForProduct(ProductType.ANY) } } private fun sendScanResultsToAnalytics(result: CompletionResult) { @@ -381,6 +407,16 @@ class DefaultTangemSdkManager( return runTaskAsync(task, null, initialMessage) } + override fun changeProductType(isRing: Boolean) { + tangemSdk.config.setupForProduct( + type = if (isRing) ProductType.RING else ProductType.CARD, + ) + } + + override fun clearProductType() { + tangemSdk.config.setupForProduct(type = ProductType.ANY) + } + override suspend fun finalizeTwin( secondCardPublicKey: ByteArray, issuerKeyPair: KeyPair, @@ -398,18 +434,5 @@ class DefaultTangemSdkManager( companion object { private const val MAX_INITIALIZE_ATTEMPTS = 10 - - @Deprecated("Use [DefaultCardSdkProvider] instead") - val config = Config( - linkedTerminal = true, - allowUntrustedCards = true, - filter = CardFilter( - allowedCardTypes = FirmwareVersion.FirmwareType.values().toList(), - maxFirmwareVersion = FirmwareVersion(major = 6, minor = 33), - batchIdFilter = CardFilter.Companion.ItemFilter.Deny( - items = setOf("0027", "0030", "0031", "0035"), - ), - ), - ) } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/sdk/impl/MockTangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/sdk/impl/MockTangemSdkManager.kt index 0dffc198a7..460b353cdf 100644 --- a/app/src/main/java/com/tangem/tap/domain/sdk/impl/MockTangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/sdk/impl/MockTangemSdkManager.kt @@ -174,6 +174,10 @@ class MockTangemSdkManager( return MockProvider.createSecondTwinWallet() } + override fun changeProductType(isRing: Boolean) = Unit + + override fun clearProductType() = Unit + override suspend fun finalizeTwin( secondCardPublicKey: ByteArray, issuerKeyPair: KeyPair, diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/SignHashesTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/SignHashesTask.kt index 3a8a5bd48c..e5616aacd7 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/SignHashesTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/SignHashesTask.kt @@ -12,6 +12,7 @@ class TangemSignHashesResponse( val signatures: List, val totalSignedHashes: Int?, val remainingSignatures: Int?, + val batchId: String?, ) : CommandResponse class SignHashesTask( @@ -22,12 +23,14 @@ class SignHashesTask( SignHashesCommand(hashes.toTypedArray(), publicKey.seedKey, publicKey.derivationPath).run(session) { response -> when (response) { is CompletionResult.Success -> { + val card = session.environment.card callback( CompletionResult.Success( TangemSignHashesResponse( - response.data.signatures, - response.data.totalSignedHashes, - session.environment.card?.wallet(publicKey.seedKey)?.remainingSignatures, + signatures = response.data.signatures, + totalSignedHashes = response.data.totalSignedHashes, + remainingSignatures = card?.wallet(publicKey.seedKey)?.remainingSignatures, + batchId = card?.batchId, ), ), ) diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt index d72eb92e23..2fcfc2411a 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt @@ -24,6 +24,7 @@ import com.tangem.domain.common.configs.CardConfig import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.CardDTO.Companion.RING_BATCH_IDS +import com.tangem.domain.models.scan.CardDTO.Companion.RING_BATCH_PREFIX import com.tangem.domain.models.scan.ProductType import com.tangem.domain.models.scan.ScanResponse import com.tangem.operations.ScanTask @@ -248,7 +249,7 @@ private class ScanWalletProcessor( } private fun getWalletProductType(card: CardDTO): ProductType { - if (RING_BATCH_IDS.contains(card.batchId)) { + if (RING_BATCH_IDS.contains(card.batchId) || card.batchId.startsWith(RING_BATCH_PREFIX)) { return ProductType.Ring } return if (card.firmwareVersion >= FirmwareVersion.Ed25519Slip0010Available && diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt index 8c78ffa3a5..a553b5540b 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt @@ -211,11 +211,13 @@ class WalletConnectSdkHelper { } private suspend fun sendTransaction(data: WcTransactionData, cardId: String?): String? { + val sdk = store.inject(DaggerGraphState::cardSdkConfigRepository).sdk + val result = (data.walletManager as TransactionSender).send( transactionData = data.transaction, - signer = CommonSigner( - tangemSdk = store.inject(DaggerGraphState::cardSdkConfigRepository).sdk, + signer = store.inject(DaggerGraphState::transactionSignerFactory).createTransactionSigner( cardId = cardId, + sdk = sdk, ), ) return when (result) { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt index 3538c3bb27..33244fc56c 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt @@ -2,6 +2,7 @@ package com.tangem.tap.features.onboarding.products.wallet.redux import android.net.Uri import com.tangem.common.CompletionResult +import com.tangem.common.card.Card import com.tangem.feature.onboarding.data.model.CreateWalletResponse import com.tangem.feature.onboarding.presentation.wallet2.analytics.SeedPhraseSource import com.tangem.tap.domain.tasks.product.CreateProductWalletTaskResponse @@ -59,6 +60,7 @@ sealed class BackupAction : Action { data object ErrorInBackupCard : BackupAction() data object StartAddingPrimaryCard : BackupAction() data object ScanPrimaryCard : BackupAction() + data class SetHasRing(val hasRing: Boolean) : BackupAction() /** * Check for unfinished backup of standard Wallets @@ -68,7 +70,7 @@ sealed class BackupAction : Action { data object StartAddingBackupCards : BackupAction() data object AddBackupCard : BackupAction() { - data object Success : BackupAction() + data class Success(val card: Card) : BackupAction() data class ChangeButtonLoading(val isLoading: Boolean) : BackupAction() } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt index 95b2fd2452..21feb2beda 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt @@ -16,8 +16,11 @@ import com.tangem.domain.common.extensions.withIOContext import com.tangem.domain.common.extensions.withMainContext import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.models.scan.CardDTO +import com.tangem.domain.models.scan.CardDTO.Companion.RING_BATCH_IDS +import com.tangem.domain.models.scan.CardDTO.Companion.RING_BATCH_PREFIX import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.wallets.builder.UserWalletBuilder +import com.tangem.domain.wallets.builder.UserWalletIdBuilder import com.tangem.domain.wallets.models.Artwork import com.tangem.feature.onboarding.data.model.CreateWalletResponse import com.tangem.feature.onboarding.presentation.wallet2.analytics.SeedPhraseSource @@ -180,11 +183,14 @@ private fun handleWalletAction(action: Action) { BackupService.State.FinalizingPrimaryCard -> BackupAction.PrepareToWritePrimaryCard is BackupService.State.FinalizingBackupCard -> BackupAction.PrepareToWriteBackupCard(backupState.index) else -> { - if (onboardingWalletState.backupState.backupStep == BackupStep.InitBackup || + if (onboardingWalletState.backupState.backupStep == null || + onboardingWalletState.backupState.backupStep == BackupStep.InitBackup || onboardingWalletState.backupState.backupStep == BackupStep.Finished ) { Analytics.send(Onboarding.Backup.ScreenOpened()) - BackupAction.IntroduceBackup + + val isWallet2 = scanResponse?.cardTypesResolver?.isWallet2() ?: false + if (isWallet2) BackupAction.StartBackup else BackupAction.IntroduceBackup } else { null } @@ -200,6 +206,14 @@ private fun handleWalletAction(action: Action) { private fun handleFinishOnboardind(scanResponse: ScanResponse) { val backupState = store.state.onboardingWalletState.backupState val updatedScanResponse = updateScanResponseAfterBackup(scanResponse, backupState) + + val userWalletId = UserWalletIdBuilder.scanResponse(scanResponse).build() + if (backupState.hasRing && userWalletId != null) { + scope.launch { + store.inject(DaggerGraphState::walletsRepository).setHasWalletsWithRing(userWalletId = userWalletId) + } + } + OnboardingHelper.trySaveWalletAndNavigateToWalletScreen( scanResponse = updatedScanResponse, accessCode = backupState.accessCode, @@ -442,7 +456,7 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction) when (result) { is CompletionResult.Success -> { updateArtworks(backupService.addedBackupCardsCount, result.data) - store.dispatchOnMain(BackupAction.AddBackupCard.Success) + store.dispatchOnMain(BackupAction.AddBackupCard.Success(result.data)) } is CompletionResult.Failure -> { val crashlytics = FirebaseCrashlytics.getInstance() @@ -496,11 +510,12 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction) } } is BackupAction.WritePrimaryCard -> { - val iconScanRes = if (scanResponse?.cardTypesResolver?.isRing() == true) { - R.drawable.img_hand_scan_ring - } else { - null - } + val isRing = scanResponse?.cardTypesResolver?.isRing() == true + val iconScanRes = if (isRing) R.drawable.img_hand_scan_ring else null + + store.dispatchOnMain(BackupAction.SetHasRing(hasRing = isRing)) + + tangemSdkManager.changeProductType(isRing) backupService.proceedBackup(iconScanRes = iconScanRes) { result -> when (result) { is CompletionResult.Success -> { @@ -508,10 +523,19 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction) } is CompletionResult.Failure -> Unit } + tangemSdkManager.clearProductType() } } is BackupAction.WriteBackupCard -> { - backupService.proceedBackup { result -> + val cardIndex = if (action.cardNumber > 0) action.cardNumber - 1 else action.cardNumber + val backupCard = backupState.backupCards.getOrNull(cardIndex) + val isRing = backupCard.isRing() + val iconScanRes = if (isRing) R.drawable.img_hand_scan_ring else null + + store.dispatchOnMain(BackupAction.SetHasRing(hasRing = isRing)) + + tangemSdkManager.changeProductType(isRing) + backupService.proceedBackup(iconScanRes = iconScanRes) { result -> when (result) { is CompletionResult.Success -> { val backupValidator = BackupValidator() @@ -536,6 +560,8 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction) } } } + + tangemSdkManager.clearProductType() } } is BackupAction.DiscardBackup -> { @@ -629,6 +655,10 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction) } } +private fun Card?.isRing(): Boolean { + return this?.let { RING_BATCH_IDS.contains(batchId) || batchId.startsWith(RING_BATCH_PREFIX) } ?: false +} + fun updateArtworks(addedBackupCardsCount: Int, card: Card) { mainScope.launch { withIOContext { @@ -653,7 +683,7 @@ internal fun gatherCardIds(backupState: BackupState, card: CardDTO?): List { showInterruptOnboardingDialog() diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt index 035db68bae..65f998acf3 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt @@ -1,6 +1,5 @@ package com.tangem.tap.features.onboarding.products.wallet.redux -import com.tangem.domain.common.util.cardTypesResolver import com.tangem.tap.backupService import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction @@ -53,7 +52,6 @@ private object ReducerForGlobalAction { maxBackupCards = MAX_BACKUP_CARDS, canSkipBackup = action.canSkipBackup, ), - isRingOnboarding = action.scanResponse.cardTypesResolver.isRing(), ) } is GlobalAction.Onboarding.StartForUnfinishedBackup -> { @@ -82,7 +80,7 @@ private object OnboardingWallet2Reducer { } private object BackupReducer { - @Suppress("ComplexMethod") + @Suppress("ComplexMethod", "LongMethod") fun reduce(action: BackupAction, state: BackupState): BackupState { return when (action) { is BackupAction.IntroduceBackup -> BackupState( @@ -90,9 +88,26 @@ private object BackupReducer { maxBackupCards = 2, canSkipBackup = state.canSkipBackup, ) - BackupAction.StartAddingPrimaryCard -> state.copy(backupStep = BackupStep.ScanOriginCard) - BackupAction.StartAddingBackupCards -> state.copy(backupStep = BackupStep.AddBackupCards) - BackupAction.AddBackupCard.Success -> state.copy(backupCardsNumber = state.backupCardsNumber + 1) + BackupAction.StartAddingPrimaryCard -> { + state.copy( + backupStep = BackupStep.ScanOriginCard, + maxBackupCards = 2, + canSkipBackup = state.canSkipBackup, + ) + } + BackupAction.StartAddingBackupCards -> { + state.copy( + backupStep = BackupStep.AddBackupCards, + maxBackupCards = 2, + canSkipBackup = state.canSkipBackup, + ) + } + is BackupAction.AddBackupCard.Success -> { + state.copy( + backupCards = state.backupCards + action.card, + backupCardsNumber = state.backupCardsNumber + 1, + ) + } is BackupAction.AddBackupCard.ChangeButtonLoading -> state.copy(showBtnLoading = action.isLoading) BackupAction.ShowAccessCodeInfoScreen -> state.copy(backupStep = BackupStep.SetAccessCode) BackupAction.ShowEnterAccessCodeScreen -> state.copy( @@ -107,7 +122,9 @@ private object BackupReducer { is BackupAction.PrepareToWritePrimaryCard -> if (state.primaryCardId == null) { state.copy( primaryCardId = backupService.primaryCardId, + primaryCardBatchId = backupService.primaryCardBatchId, backupCardIds = backupService.backupCardIds, + backupCardBatchIds = backupService.backupCardsBatchIds, backupCardsNumber = backupService.backupCardIds.size, backupStep = BackupStep.WritePrimaryCard, ) @@ -117,7 +134,9 @@ private object BackupReducer { is BackupAction.PrepareToWriteBackupCard -> if (state.primaryCardId == null) { state.copy( primaryCardId = backupService.primaryCardId, + primaryCardBatchId = backupService.primaryCardBatchId, backupCardIds = backupService.backupCardIds, + backupCardBatchIds = backupService.backupCardsBatchIds, backupCardsNumber = backupService.backupCardIds.size, backupStep = BackupStep.WriteBackupCard(action.cardNumber), ) @@ -129,6 +148,14 @@ private object BackupReducer { is BackupAction.FinishBackup -> state.copy(backupStep = BackupStep.Finished) BackupAction.OnAccessCodeDialogClosed -> state.copy(backupStep = BackupStep.AddBackupCards) BackupAction.DiscardBackup -> BackupState() + is BackupAction.SetHasRing -> { + // Don't update if state.has ring is already true + if (state.hasRing) { + state + } else { + state.copy(hasRing = action.hasRing) + } + } else -> state } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt index 853678f202..6f140911e6 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt @@ -2,6 +2,7 @@ package com.tangem.tap.features.onboarding.products.wallet.redux import android.graphics.Bitmap import android.net.Uri +import com.tangem.common.card.Card import com.tangem.domain.redux.StateDialog import org.rekotlin.StateType @@ -14,7 +15,6 @@ data class OnboardingWalletState( val backupState: BackupState = BackupState(), val walletImages: WalletImages = WalletImages(), val showConfetti: Boolean = false, - val isRingOnboarding: Boolean = false, ) : StateType { @Suppress("MagicNumber") @@ -29,6 +29,7 @@ data class OnboardingWalletState( OnboardingWalletStep.CreateWallet -> 1 OnboardingWalletStep.Backup -> { when (backupState.backupStep) { + null -> 2 BackupStep.InitBackup -> 2 BackupStep.ScanOriginCard -> 3 BackupStep.AddBackupCards -> 4 @@ -65,18 +66,22 @@ enum class OnboardingWalletStep { data class BackupState( val primaryCardId: String? = null, + val primaryCardBatchId: String? = null, val backupCardsNumber: Int = 0, + val backupCards: List = emptyList(), val backupCardIds: List = emptyList(), + val backupCardBatchIds: List = emptyList(), @Transient val backupCardsArtworks: Map = emptyMap(), val accessCode: String = "", val accessCodeError: AccessCodeError? = null, - val backupStep: BackupStep = BackupStep.InitBackup, + val backupStep: BackupStep? = null, val maxBackupCards: Int = 2, val canSkipBackup: Boolean = true, val isInterruptedBackup: Boolean = false, val showBtnLoading: Boolean = false, val hasBackupError: Boolean = false, + val hasRing: Boolean = false, ) enum class AccessCodeError { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt index fb9acfefd5..e7c4765a87 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt @@ -28,6 +28,7 @@ import com.tangem.core.analytics.models.Basic import com.tangem.core.ui.extensions.setStatusBarColor import com.tangem.datasource.utils.isNullOrEmpty import com.tangem.domain.common.util.cardTypesResolver +import com.tangem.domain.models.scan.isRing import com.tangem.feature.onboarding.data.model.CreateWalletResponse import com.tangem.feature.onboarding.presentation.wallet2.analytics.SeedPhraseSource import com.tangem.feature.onboarding.presentation.wallet2.viewmodel.SeedPhraseMediator @@ -187,6 +188,7 @@ class OnboardingWalletFragment : when { state.wallet2State != null -> { seedPhraseStateHandler.newState(this, state, seedPhraseViewModel) + seedPhraseViewModel.setCardArtworkUri(cardArtworkUri = state.walletImages.primaryCardImage.toString()) updateWalletImagesState(state.walletImages) } else -> { @@ -247,6 +249,7 @@ class OnboardingWalletFragment : private fun setBackupState(state: BackupState) { when (state.backupStep) { + null -> Unit BackupStep.InitBackup -> showBackupIntro(state) BackupStep.ScanOriginCard -> showScanOriginCard() BackupStep.AddBackupCards -> showAddBackupCards(state) @@ -397,13 +400,26 @@ class OnboardingWalletFragment : reInitCardsWidgetIfNeeded(state.backupCardsNumber) prepareViewForFinalizeStep() - val cardIdFormatter = CardIdFormatter(CardIdDisplayFormat.LastMasked(4)) - tvHeader.text = getText(R.string.common_origin_card) - tvBody.text = getString( - R.string.onboarding_subtitle_scan_primary_card_format, - state.primaryCardId?.let { cardIdFormatter.getFormattedCardId(it) }, + val isRing = state.primaryCardBatchId != null && isRing(batchId = state.primaryCardBatchId) + + tvHeader.text = getText( + if (isRing) R.string.common_origin_ring else R.string.common_origin_card, ) - layoutButtonsCommon.btnWalletMainAction.text = getText(R.string.onboarding_button_backup_origin) + + tvBody.text = if (isRing) { + getString(R.string.onboarding_subtitle_scan_ring) + } else { + val cardIdFormatter = CardIdFormatter(CardIdDisplayFormat.LastMasked(4)) + getString( + R.string.onboarding_subtitle_scan_primary_card_format, + state.primaryCardId?.let { cardIdFormatter.getFormattedCardId(it) }, + ) + } + + layoutButtonsCommon.btnWalletMainAction.text = getText( + if (isRing) R.string.onboarding_button_backup_ring else R.string.onboarding_button_backup_origin, + ) + layoutButtonsCommon.btnWalletMainAction.setIconResource(R.drawable.ic_tangem_24) layoutButtonsCommon.btnWalletMainAction.setOnClickListener { store.dispatch(BackupAction.WritePrimaryCard) } @@ -427,16 +443,26 @@ class OnboardingWalletFragment : prepareViewForFinalizeStep() val cardNumber = (state.backupStep as? BackupStep.WriteBackupCard)?.cardNumber ?: 1 - val cardIdFormatter = CardIdFormatter(CardIdDisplayFormat.LastMasked(4)) - tvHeader.text = getString(R.string.onboarding_title_backup_card_format, cardNumber) - tvBody.text = getString( - R.string.onboarding_subtitle_scan_backup_card_format, - cardIdFormatter.getFormattedCardId(state.backupCardIds[cardNumber - 1]), + + val isRing = isRing(batchId = state.backupCardBatchIds[cardNumber - 1]) + tvHeader.text = getString( + if (isRing) R.string.onboarding_title_backup_ring else R.string.onboarding_title_backup_card, ) + + tvBody.text = if (isRing) { + getString(R.string.onboarding_subtitle_scan_ring) + } else { + val cardIdFormatter = CardIdFormatter(CardIdDisplayFormat.LastMasked(4)) + getString( + R.string.onboarding_subtitle_scan_backup_card_format, + state.primaryCardId?.let { cardIdFormatter.getFormattedCardId(it) }, + ) + } + layoutButtonsCommon.btnWalletMainAction.text = getString( - R.string.onboarding_button_backup_card_format, - cardNumber, + if (isRing) R.string.onboarding_button_backup_ring else R.string.onboarding_button_backup_origin, ) + layoutButtonsCommon.btnWalletMainAction.setIconResource(R.drawable.ic_tangem_24) layoutButtonsCommon.btnWalletMainAction.setOnClickListener { store.dispatch(BackupAction.WriteBackupCard(cardNumber)) diff --git a/app/src/main/java/com/tangem/tap/network/auth/DefaultExpressAuthProvider.kt b/app/src/main/java/com/tangem/tap/network/auth/DefaultExpressAuthProvider.kt index 99ddfe6b5b..7331b9f95c 100644 --- a/app/src/main/java/com/tangem/tap/network/auth/DefaultExpressAuthProvider.kt +++ b/app/src/main/java/com/tangem/tap/network/auth/DefaultExpressAuthProvider.kt @@ -1,12 +1,17 @@ package com.tangem.tap.network.auth +import com.tangem.datasource.local.preferences.AppPreferencesStore +import com.tangem.datasource.local.preferences.PreferencesKeys +import com.tangem.datasource.local.preferences.utils.getSyncOrDefault import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.lib.auth.ExpressAuthProvider +import kotlinx.coroutines.runBlocking import java.util.UUID import java.util.concurrent.atomic.AtomicReference internal class DefaultExpressAuthProvider( private val userWalletsStore: UserWalletsStore, + private val appPreferencesStore: AppPreferencesStore, ) : ExpressAuthProvider { private var uuid = AtomicReference(UUID.randomUUID()) @@ -18,4 +23,18 @@ internal class DefaultExpressAuthProvider( override fun getSessionId(): String { return uuid.get().toString() } + + override fun getRefCode(): String { + val addedWalletsWithRings = runBlocking { + appPreferencesStore.getSyncOrDefault( + key = PreferencesKeys.ADDED_WALLETS_WITH_RING_KEY, + default = emptySet(), + ) + } + + val userWalletId = userWalletsStore.selectedUserWalletOrNull?.walletId?.stringValue + ?: error("No user id provided") + + return if (addedWalletsWithRings.contains(userWalletId)) "ring" else "" + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/network/auth/di/AuthModule.kt b/app/src/main/java/com/tangem/tap/network/auth/di/AuthModule.kt index 41aa591911..614f6725ac 100644 --- a/app/src/main/java/com/tangem/tap/network/auth/di/AuthModule.kt +++ b/app/src/main/java/com/tangem/tap/network/auth/di/AuthModule.kt @@ -2,6 +2,7 @@ package com.tangem.tap.network.auth.di import com.tangem.datasource.api.common.AuthProvider import com.tangem.datasource.config.ConfigManager +import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.lib.auth.ExpressAuthProvider import com.tangem.lib.auth.StakeKitAuthProvider @@ -29,8 +30,14 @@ class AuthModule { @Provides @Singleton - fun provideExpressAuthProvider(userWalletsStore: UserWalletsStore): ExpressAuthProvider { - return DefaultExpressAuthProvider(userWalletsStore = userWalletsStore) + fun provideExpressAuthProvider( + userWalletsStore: UserWalletsStore, + appPreferencesStore: AppPreferencesStore, + ): ExpressAuthProvider { + return DefaultExpressAuthProvider( + userWalletsStore = userWalletsStore, + appPreferencesStore = appPreferencesStore, + ) } @Provides diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt index fb01befe82..2008fae3cc 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt @@ -7,6 +7,7 @@ import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.blockchain.extensions.Result +import com.tangem.domain.common.TapWorkarounds.isTangemTwins import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.tokens.model.CryptoCurrency @@ -105,8 +106,9 @@ suspend fun buyErc20TestnetTokens(card: CardDTO, walletManager: EthereumWalletMa val coinValue = walletManager.wallet.amounts[AmountType.Coin]?.value ?: BigDecimal.ZERO if (coinValue < fee.amount.value) return + val isCardNotBackedUp = card.backupStatus?.isActive != true && !card.isTangemTwins val signer = TangemSigner( - card = card, + cardId = card.cardId.takeIf { isCardNotBackedUp }, tangemSdk = store.inject(DaggerGraphState::cardSdkConfigRepository).sdk, initialMessage = Message(), ) { signResponse -> @@ -117,6 +119,8 @@ suspend fun buyErc20TestnetTokens(card: CardDTO, walletManager: EthereumWalletMa remainingSignatures = signResponse.remainingSignatures, ), ) + + store.dispatch(action = GlobalAction.IsSignWithRing(signResponse.isRing)) } walletManager.send( diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt index 45b2035717..b9501c2a0e 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt @@ -2,6 +2,7 @@ package com.tangem.tap.proxy.redux import com.tangem.TangemSdkLogger import com.tangem.blockchainsdk.BlockchainSDKFactory +import com.tangem.blockchainsdk.signer.TransactionSignerFactory import com.tangem.common.routing.AppRouter import com.tangem.core.navigation.email.EmailSender import com.tangem.core.navigation.share.ShareManager @@ -87,4 +88,5 @@ data class DaggerGraphState( val appRouter: AppRouter? = null, val pushNotificationsFeatureToggles: PushNotificationsFeatureToggles? = null, val pushNotificationsRouter: PushNotificationsRouter? = null, + val transactionSignerFactory: TransactionSignerFactory? = null, ) : StateType \ No newline at end of file diff --git a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt index 5fc6c4e12c..94637867c0 100644 --- a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt +++ b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt @@ -170,6 +170,7 @@ sealed class AnalyticsParam { const val BATCH = "Batch" const val TYPE = "Type" const val FEE_TYPE = "Fee Type" + const val WALLET_FORM = "WalletForm" const val PERMISSION_TYPE = "Permission Type" const val PRODUCT_TYPE = "Product Type" const val FIRMWARE = "Firmware" diff --git a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/Basic.kt b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/Basic.kt index 6fb52dfe29..3c1d48f13b 100644 --- a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/Basic.kt +++ b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/Basic.kt @@ -67,6 +67,10 @@ sealed class Basic( enum class MemoType { Empty, Full, Null } + + enum class WalletForm { + Card, Ring + } } class ScanError(error: Throwable) : Basic( diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/common/SwitchEnvironmentInterceptor.kt b/core/datasource/src/main/java/com/tangem/datasource/api/common/SwitchEnvironmentInterceptor.kt index dbfbc5e54f..e47f1f8494 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/common/SwitchEnvironmentInterceptor.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/common/SwitchEnvironmentInterceptor.kt @@ -46,7 +46,9 @@ internal class SwitchEnvironmentInterceptor( private fun Request.Builder.addHeaders(headers: Map>): Request.Builder { headers.forEach { (name, valueProvider) -> - addHeader(name = name, value = valueProvider()) + val value = valueProvider() + + if (value.isNotBlank()) addHeader(name = name, value = value) } return this diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/common/config/Express.kt b/core/datasource/src/main/java/com/tangem/datasource/api/common/config/Express.kt index 134d24a3ac..9835b3388c 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/common/config/Express.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/common/config/Express.kt @@ -51,6 +51,7 @@ internal class Express( put(key = "user-id", value = Provider(expressAuthProvider::getUserId)) put(key = "session-id", value = Provider(expressAuthProvider::getSessionId)) putAll(from = RequestHeader.AppVersionPlatformHeaders(appVersionProvider).values) + put(key = "refcode", value = Provider(expressAuthProvider::getRefCode)) } private fun getApiKey(isProd: Boolean): String { diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt index d2c600c572..10f76c5e66 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt @@ -99,18 +99,15 @@ object PreferencesKeys { val apiConfigsEnvironmentKey by lazy { stringPreferencesKey(name = "apiConfigsEnvironment") } + val ADDED_WALLETS_WITH_RING_KEY by lazy { stringSetPreferencesKey(name = "addedWalletsWithRing") } + + val SHOULD_SHOW_RING_PROMO_KEY by lazy { booleanPreferencesKey(name = "shouldShowRingPromo") } + // region Permission fun getShouldShowPermission(permission: String) = booleanPreferencesKey("shouldShowPushPermission_$permission") fun getShouldShowInitialPermissionScreen(permission: String) = booleanPreferencesKey("shouldShowInitialPushPermissionScreen_$permission") - - fun getIsFirstTimeAskingPermission(permission: String) = - booleanPreferencesKey("shouldAskInitialPushPermission_$permission") - - fun getPermissionLaunchCount(permission: String) = intPreferencesKey("pushPermissionLaunchCount_$permission") - - fun getPermissionDaysCount(permission: String) = longPreferencesKey("pushPermissionDaysCount_$permission") // endregion fun getUserTokensKey(userWalletId: String) = stringPreferencesKey(name = "user_tokens_$userWalletId") diff --git a/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/ProdApiConfigsManagerTest.kt b/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/ProdApiConfigsManagerTest.kt index d818bd6c9f..c7e95f9f49 100644 --- a/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/ProdApiConfigsManagerTest.kt +++ b/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/ProdApiConfigsManagerTest.kt @@ -44,6 +44,7 @@ internal class ProdApiConfigsManagerTest(private val model: Model) { every { appVersionProvider.versionName } returns VERSION_NAME every { expressAuthProvider.getUserId() } returns EXPRESS_USER_ID every { expressAuthProvider.getSessionId() } returns EXPRESS_SESSION_ID + every { expressAuthProvider.getRefCode() } returns EXPRESS_REF_CODE every { stakeKitAuthProvider.getApiKey() } returns STAKE_KIT_API_KEY } @@ -65,6 +66,7 @@ internal class ProdApiConfigsManagerTest(private val model: Model) { const val VERSION_NAME = "debug" const val EXPRESS_USER_ID = "express_user_id" const val EXPRESS_SESSION_ID = "express_session_id" + const val EXPRESS_REF_CODE = "express_ref_code" const val EXPRESS_API_KEY = "express_api_key" const val EXPRESS_DEV_API_KEY = "express_dev_api_key" const val STAKE_KIT_API_KEY = "stake_kit_api_key" @@ -111,6 +113,7 @@ internal class ProdApiConfigsManagerTest(private val model: Model) { }, "user-id" to Provider { EXPRESS_USER_ID }, "session-id" to Provider { EXPRESS_SESSION_ID }, + "refcode" to Provider { EXPRESS_REF_CODE }, "version" to Provider { VERSION_NAME }, "platform" to Provider { "android" }, ), diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml index 516445f46f..242e76cf07 100644 --- a/core/res/src/main/res/values-de/strings.xml +++ b/core/res/src/main/res/values-de/strings.xml @@ -97,6 +97,10 @@ %d tag %d tage + + Tag + Tage + Entfernen Deaktiviert Erledigt @@ -115,12 +119,14 @@ Adressen abrufen Zum Anbieter gehen Zum Token + Stunde Importieren In Arbeit Später %1$s übrig Gesperrt Hauptnetz + Monat Netzgebühr Der überwiesene Betrag wird um %1$s (%2$s) gekürzt, um die gewählte Gebührenhöhe zu decken. Weiter @@ -129,6 +135,7 @@ Jetzt OK Hauptkarte + Primärring Passphrase Einfügen Datenschutzbestimmungen @@ -143,6 +150,7 @@ Änderungen speichern Suchen Token suchen + sek. Seed phrase Aktion auswählen Verkaufen @@ -168,6 +176,7 @@ Es ist ein Fehler aufgetreten. Bitte versuche es erneut. Nicht erreichbar staking beenden + Woche Ja Vertragsadresse kopiert! Verfügbare Netzwerke @@ -286,6 +295,7 @@ Rückmeldung Feedback zu Tangem Eine Transaktion kann nicht gesendet werden + Fehler in der Coinbeschreibung Nur diese Das Netzwerk erhebt eine Token-Genehmigungsgebühr, um zu überprüfen, ob Sie die Verwendung Ihres Tokens für den Swap genehmigen. Gib das Genehmigungslimit für das ausgewählte Token an @@ -301,6 +311,7 @@ Um den Zugangscode zu ändern, halte die Karte wie oben gezeigt an das Gerät und entferne sie erst am Ende des Vorgangs. Um den Passcode zu ändern, halte die Karte wie oben gezeigt an das Gerät und entferne sie erst am Ende des Vorgangs. Um die Wallet zu erstellen, halte die Karte wie oben gezeigt an das Gerät und entferne sie erst am Ende des Vorgangs. + Um die Wallet zu erstellen, tippe auf den Ring wie oben gezeigt und entferne ihn erst am Ende des Vorgangs Halte deine Karte Nr. %s des Sets an dein Smartphone Legen Sie die Karte zum Scannen an Tippen um zu signieren @@ -343,8 +354,8 @@ Hochstimmen Wallet auswählen Das Wallet unterstützt nicht mehr als ein Netzwerk - Um mit dem Kauf, Tausch oder Erhalt dieses Vermögenswerts zu beginnen, füge diesen Token zu mindestens 1 Netzwerk hinzu - Dieses Asset ist nicht verfügbar + Um dieses Asset zu kaufen, zu tauschen oder zu erhalten, füge diesen Deinem Portfolio hinzu + Dieses Asset wird derzeit in der Wallet nicht unterstützt. Dieses Asset ist für dieses Wallet nicht verfügbar Zum Portfolio hinzufügen Hinzufügen @@ -378,6 +389,10 @@ Top-Verlierer Beliebt Über %s + + %d Börse + %d Börsen + Bewertung, basierend auf %d Bewertungen, basierend auf %d @@ -389,6 +404,12 @@ Umlaufmenge Die Gesamtzahl der Coins, die für den Handel verfügbar sind und auf dem Markt zirkulieren Umlaufmenge + Keine Börsen gefunden + Umtausch + Vorsicht + Riskant + Vertrauenswürdig + Börsen Erfahrene Käufer Nettokäufer mit der zusätzlichen Anforderung, mindestens 100 ausgehende Transaktionen zu haben Erfahrene Käufer @@ -407,6 +428,7 @@ Liquidität Liquiditätsindex Leer + Gelistet auf Niedrig MarketCap Der Gesamtmarktwert einer Kryptowährung, berechnet sich durch Multiplikation des aktuellen Preises des Coins mit der Gesamtzahl der im Umlauf befindlichen Coins @@ -429,6 +451,7 @@ Handelsvolumen (24h) Der Gesamtbetrag einer Kryptowährung, der innerhalb der letzten 24 Stunden gehandelt wurde, wobei das Aktivitäts- und Liquiditätsniveau auf dem Markt angegeben wird Handelsvolumen (24h) + Volumen Rufe dies auf oder tippe auf die Suchleiste, um Token direkt vom Markt hinzuzufügen Token hinzufügen NFC ist auf deinem Gerät nicht verfügbar @@ -451,9 +474,11 @@ Der Sicherungsvorgang ist teilweise abgeschlossen. Du kannst ihn jetzt nicht beenden. Die Passphrase ist eine fortschrittliche Sicherheitsfunktion, die von Krypto-Wallets verwendet wird. Sie fügt ein zusätzliches Wort oder eine Phrase deiner Wahl zu der bereits bestehenden Wiederherstellungsphrase hinzu, um einen brandneuen Satz von Adressen zu erzeugen. Hinzufügen einer Sicherungskarte + Karte scannen Scanne die Karte Nr. %d Jetzt sichern Scannen der Hauptkarte + Ring scannen Weiter zu meiner Wallet Backup abschließen Krypto empfangen @@ -567,6 +592,8 @@ Mir ist klar, dass ich diese Karte nicht verwenden kann, um meinen Zugangscode auf den anderen Karten der aktuellen Wallet wiederherzustellen Durch das Zurücksetzen auf Werkseinstellungen wird die Wallet vollständig von der ausgewählten Karte gelöscht. Du kannst die aktuelle Wallet nicht wiederherstellen oder die Karte verwenden, um den Zugangscode wiederherzustellen. Beim Zurücksetzen auf die Werkseinstellungen wird die Wallet der ausgewählten Karte vollständig gelöscht und aus der App entfernt. Es ist nicht möglich, die aktuelle Wallet wiederherzustellen. + Ringbesitzer erhalten bis zum 15.11. 3 provisionsfreie Swaps auf Changelly! + Jetzt mit 0 %% Gebühren tauschen! Besitzt du eine Bankkarte aus einem anderen Land und eine Aufenthaltserlaubnis oder Registrierung außerhalb der Russischen Föderation? Russische Bankkarten werden derzeit nicht akzeptiert Melde dich bei der App an und überprüfe dein Guthaben, ohne die Karte zu scannen @@ -691,6 +718,7 @@ Gesperrt Migrieren Natives Staking + Derzeit sind keine aktiven Validatoren für das Staking verfügbar. Bitte versuchen Sie es später erneut. Verdiente Belohnungen werden an Deine Wallet gesendet und stehen Dir sofort zur Verfügung Stake sicher und verdienen Belohnungen Sicher staken und tägliche Belohnungen verdienen. @@ -721,8 +749,8 @@ Händisch Pro Block Täglich + Alle %s Täglich - Alle %1$s Sek. Pro Epoche Pro Ära Stündlich @@ -733,6 +761,7 @@ Mehr staken Du setzt %1$s ein und erhältst jährlich %2$s Zum Entsperren antippen + Tippe zum Entsperren oder Abstimmen Zum abheben antippen Stake %s Staking beenden%s @@ -747,7 +776,7 @@ Validatoren Strategischer Partner Abstimmung - Abstimmung gesperrt + Abstimmung Zurückziehen Deine Einsätze Bewahre deine Krypto-Assets sicher auf, während die privaten Schlüssel auf deiner Karte bleiben diff --git a/core/res/src/main/res/values-es/strings.xml b/core/res/src/main/res/values-es/strings.xml index ba3e1b02eb..ea242dd220 100644 --- a/core/res/src/main/res/values-es/strings.xml +++ b/core/res/src/main/res/values-es/strings.xml @@ -343,8 +343,8 @@ Votar a favor Elegir wallet La billetera no admite más de una red - Para comprar, intercambiar o recibir este activo, agréguelo a su cartera - Este activo no está disponible + Para comprar, intercambiar o recibir este activo, agréguelo a su portafolio + Este activo no está disponible en la billetera Añadir al portafolio Agregue un token Redes disponibles @@ -565,6 +565,8 @@ Entiendo que no puedo usar esta tarjeta para recuperar mi código de acceso en las otras tarjetas de la wallet actual El restablecimiento de fábrica eliminará completamente la billetera de la tarjeta seleccionada. No podrá restaurar la billetera actual ni usar la tarjeta para recuperar el código de acceso. El restablecimiento de fábrica eliminará completamente la billetera de la tarjeta seleccionada y lo eliminará de la app. No podrá restaurar la billetera actual. + Si tienes un Anillo Tangem, ¡3 swaps sin comisión en Changelly hasta el 15/11! + ¡Intercambia con 0%% de comisión! ¿Tiene una tarjeta bancaria de otro país y un permiso de residencia o registro fuera de la Federación Rusa? Las tarjetas bancarias rusas no se aceptan actualmente Inicie sesión en la app y comprueba su saldo sin escanear la tarjeta @@ -689,6 +691,7 @@ Bloqueado Migrar Native staking + No hay validadores activos disponibles para hacer staking en este momento. Por favor, inténtalo de nuevo más tarde. Recomp. won se le enviará y estará disponible para su uso inmediato Haga staking de forma segura y comience a ganar recompensas Haga de forma segura y comience a ganar recompensas diarias @@ -720,7 +723,6 @@ Bloquear Día Cada día - Cada %1$s segundo(s) Época Era Hora @@ -771,7 +773,7 @@ Hacer swap de esta cantidad de tokens seleccionados causará un impacto significativo en el precio y reducirá su resultado. Fondos insuficientes Dar autorización - Intercambie + Intercambiar Usted recibe Elige token no disponible diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml index e310d6723c..e57b8a91b3 100644 --- a/core/res/src/main/res/values-fr/strings.xml +++ b/core/res/src/main/res/values-fr/strings.xml @@ -343,8 +343,8 @@ Vote positif Choisissez un portefeuille Le portefeuille ne prend pas en charge plus d\'un réseau - Pour commencer à acheter, échanger ou recevoir cet actif, ajoutez ce jeton à au moins 1 réseau - Cet actif n\'est pas disponible + Pour acheter, échanger ou recevoir cet actif, ajoutez-le à votre portefeuille + Cet actif n\'est plus pris en charge dans le portefeuille Ajouter au portfolio Ajoutez un jeton Réseaux disponibles @@ -565,6 +565,8 @@ Je comprends que je ne peux pas utiliser cette carte pour récupérer mon code d\'accès sur les autres cartes du portefeuille actuel La réinitialisation aux paramètres d\'usine supprimera complètement le portefeuille de la carte sélectionnée. Vous ne pourrez pas restaurer le portefeuille actuel ni utiliser la carte pour récupérer le code d\'accès. La réinitialisation aux paramètres d\'usine supprimera complètement le portefeuille de la carte sélectionnée et le supprimera de l\'application. Vous ne pourrez pas restaurer le portefeuille actuel. + Les propriétaires du Tangem Ring ont droit à 3 swap gratis sur Changelly jusqu\'au 15/11 ! + Échangez avec 0 %% de frais maintenant! Avez-vous une carte bancaire d\'un autre pays et un permis de séjour ou une inscription en dehors de la Fédération de Russie ? Les cartes bancaires russes ne sont actuellement pas acceptées Connectez-vous à l\'application et vérifiez votre solde sans scanner la carte @@ -689,6 +691,7 @@ Bloqué Migrer Native staking + Aucun validateur actif n\'est disponible pour le staking pour le moment. Veuillez réessayer plus tard. Récomp. gagnées vous seront envoyées et dispo pour utilisation immédiate Stakez et commencez à gagner vos récompenses Stakez en toute sécurité et commencez à gagner des récompenses quotidiennes @@ -720,7 +723,6 @@ Bloquer Jour Chaque jour - Toute(s) les %1$s seconde(s) Époque Ère Heure diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml index 0d15d6edfc..c1a429ffe6 100644 --- a/core/res/src/main/res/values-ja/strings.xml +++ b/core/res/src/main/res/values-ja/strings.xml @@ -12,19 +12,19 @@ 取引を送信できません 選択したものは%1$sネットワークをサポートしていません。 %1$sブロックチェーンの暗号化を有効にするには、ウォレットを工場出荷時の設定にリセットする必要があります。リセットする前に出金して、資金が失われないようにしてから、リセット処理を完了してください。リセット後は、現在のウォレットにアクセスできなくなります。 - %1$s ネットワークのトークンは、ファームウェアの制限により、このカードではサポートされていません。 - カードのスキャンに問題がありますか? + %1$s ネットワークのトークンは、ファームウェアの制限により、このカードまたはリングではサポートされていません。 + カードまたはリングのスキャンに問題がありますか? このカードはこのアプリでは使用できません。 デフォルト手数料 デフォルト手数料を有効にすると、取引手数料が自動的に設定され、送金時に手数料ページを表示する必要がなくなります。必要に応じて、いつでもこのページに戻ることができます。 設定に移動して、Tangemアプリで生体認証を有効にします。 生体認証を有効にする これにより、保存されているウォレットアクセスコードがすべて削除されます。ウォレットでの今後の操作には、アクセスコードの送信が必要になります。 - 保存したカードを削除すると、保存されているすべてのウォレットとそのアクセスコードがアプリから削除されます。 + 保存したデバイスを削除すると、保存されているすべてのウォレットとそのアクセスコードがアプリから削除されます。 アクセスコードを保存 - カードとのやり取りには、アクセスコードの代わりに生体認証が要求されます。 + カードまたはリングとのやり取りには、アクセスコードの代わりに生体認証が要求されます。 ウォレットをアプリに保存する - すべてのウォレットをTangemアプリにリンクできるようにします。アプリのロックを解除するには、生体認証が必要になります。取引の署名には、Tangemカードをタップする必要があります。 + すべてのウォレットをTangemアプリにリンクできるようにします。アプリのロックを解除するには、生体認証が必要になります。取引の署名には、Tangemカードまたはリングをタップする必要があります。 ダーク ライト システムのデフォルト @@ -34,28 +34,28 @@ 今後表示しない わかりました 残高は非表示 - カードをスキャンしてください - 30秒後に再試行するか、カードをスキャンしてください。 + カードまたはリングをスキャンしてください + 30秒後に再試行するか、カードまたはリングをスキャンしてください 試行回数が多すぎます お使いの携帯電話で生体認証が無効になっているため、アプリにウォレットを保存できません。ウォレットを保存するには、携帯電話の設定で生体認証機能を有効にしてください。 バックアップ処理を開始する 銀行カードまたはその他の支払い方法を使用する - %d カード + %d デバイス - このカードを使用して、このウォレット内の他のカードのアクセスコードをリセットしたくない場合は、このオプションを無効にしてください。これにより、このカードのアクセスコードもリセットできなくなりますのでご注意ください。 + このカードを使用して、このウォレット内の他のカードまたはリングのアクセスコードをリセットしたくない場合は、このオプションを無効にしてください。これにより、このカードのアクセスコードもリセットできなくなりますのでご注意ください。 このカードを使用して、このウォレット内の他のカードのアクセスコードをリセットできます。 アクセスコードの復元 リセット 本当に実行しますか? アクセスコードの変更 アクセスコードは、このカードのみで変更されます - 選択したウォレットのすべてのカードが、工場出荷時の設定にリセットされました。これで、新しいウォレットを作成できます。 + 選択したウォレットのすべてのTangemデバイスが、工場出荷時の設定にリセットされました。これで、新しいウォレットを作成できます。 リセット完了 - このウォレットの次のカードをリセットしますか? - カードのリセット - このウォレットのすべてのカードをリセットすることをお勧めします。 - すべてのカードのリセットが完了していません。 + このウォレットの次のデバイスをリセットしますか? + ウォレットのリセット + このウォレットのすべてのTangemデバイスをリセットすることをお勧めします + すべてのTangemデバイスのリセットが完了していません 工場出荷時の設定にリセット セキュリティ・モード カード設定 @@ -116,12 +116,14 @@ アドレスを取得する プロバイダーへ移動 トークンへ移動 + 時間 インポート 進行中 後で 残り%1$s ロックされています メインネットワーク + ネットワーク手数料 送金額は、選択された手数料レベルをカバーするため、%1$s (%2$s) 減額されます。 @@ -130,6 +132,7 @@ わかりました プライマリーカード + プライマリーリング パスフレーズ ペースト プライバシーポリシー @@ -144,6 +147,7 @@ 変更内容を保存 検索 トークンを検索 + シードフレーズ アクションを選択 売る @@ -169,6 +173,7 @@ エラーが発生しました。もう一度お試しください。 アクセスできません ステーキング解除 + はい コントラクトアドレスをコピーしました! 利用可能なネットワーク @@ -205,7 +210,7 @@ アクセスコード カードをスキャンする前に、正しいアクセスコードを送信する必要があります。 長くタップ - このメカニズムは、カードに対する近接攻撃から保護します。コマンドの受信と実行の間に遅延を強制します。 + このメカニズムは、カードまたはリングに対する近接攻撃から保護します。コマンドの受信と実行の間に遅延を強制します。 パスコード カードの状態の変更を伴うコマンドを実行する前に、パスコードを入力する必要があります。 紹介プログラム @@ -222,7 +227,7 @@ 詳細 インターネット接続を確認するか、別のネットワークに切り替えてください。 利用規約 - 別のウォレットのカードを使用しました。このウォレットに関連付けられているカードをタップしてください。 + 別のウォレットのカードまたはリングを使用しました。このウォレットにリンクしているカードまたはリングをタップしてください。 マイトークン まだトークンを追加していません。マーケットからトークンを追加して交換してください。 %sとの交換はできません @@ -279,14 +284,15 @@ ポートフォリオ内のアセットをこのトークンと交換する 以下の情報はオプションです。共有したくない場合は消去できます。 どのような機能が不足しているかを教えください。解決できるよう尽力致します。 - お持ちのカードについて教えてください + お持ちのカードまたはリングについて教えてください サポートチームの皆さん、こんにちは。 問題について詳しく教えてください。どんな些細なことでも役に立ちます。 私の提案 - カードをスキャンできません + カード / リングをスキャンできません フィードバック Tangemへのフィードバック 取引を送信できません + コインの説明エラー 取引 ネットワークは、あなたがトークンのスワップを承認していることを確認するために、トークン承認手数料を請求します。 選択したトークンの承認制限を指定します @@ -296,17 +302,18 @@ 続行するには、%1sスマートコントラクトに%2sを使用する権限を付与してください 許可を与える 無制限 - カードを注文 - スキャン + Tangemを注文 + Tangemをスキャン この情報はAIによって生成されました - アクセスコードを変更するには、上図のようにカードをタップし、操作が終了するまで取り外さないでください。 + アクセスコードを変更するには、上図のようにカードまたはリングをタップし、操作が終了するまで取り外さないでください。 パスコードを変更するには、上記のようにカードをタップし、操作が終了するまで取り外さないでください。 ウォレットを作成するには、上記のようにカードをタップし、操作が終了するまで取り外さないでください。 + ウォレットを作成するには、上図のようにリングをタップし、操作が終了するまで取り外さないでください。 ウォレットの%s番目のカードをタップします タップしてスキャン タップして署名 - カードをタップ - 生体認証を更新しました。カードをスキャンして入ってください。 + カードまたはリングをタップ + 生体認証を更新しました。カードまたはリングをスキャンして入ってください。 送金するには、残高が手数料額より高くなければなりません。 残高不足 この取引に必要なManaが不足しています。Manaが補充されるまでお待ちください。あなたのMana残高は%1$s/%2$sです。 @@ -318,7 +325,7 @@ 暗号資産および取引の追跡を開始するには、トークンを追加してください トークンの管理 すべてのネットワークにアクセスするには、カードをスキャンする必要があります。 - カードをスキャンする + カードまたはリングをスキャンする 2月%2$s - %3$sの期間、Changelly経由のスワップは%1$sのサービス手数料となります。 Changellyでスワップ、手数料%s トークン @@ -344,14 +351,14 @@ ウォレットを選択 ウォレットは複数のネットワークをサポートしていません。 このアセットを購入・交換・受け取るには、ポートフォリオに追加してください。 - このアセットは利用できません + このアセットは現在ウォレットで利用できません このアセットはこのウォレットでは使用できません。 ポートフォリオに追加 追加 利用可能なネットワーク 私のポートフォリオ マーケット - 選択したネットワークのアドレスを生成するには、Tangemカードをスキャンする必要があります。 + 選択したネットワークのアドレスを生成するには、Tangemウォレットカードまたはリングをスキャンする必要があります。 トークンを追加するには、これをスワイプするか、検索バーをタップしてください。 このセクションのデータは、次のネットワークから取得されています: %s データを読み込めません… @@ -441,11 +448,11 @@ これをドラッグするか、検索窓をタップして、マーケットから直接トークンを追加します トークンを追加 お使いのデバイスではNFCが使用できません - すべてのカードを保護するには、単一のアクセスコードを設定する必要があります + すべてのデバイスを保護するには、単一のアクセスコードを設定してください。 保護する - 後で各カードに個別のアクセスコードを設定できます + 後で各カードおよびリングに個別のアクセスコードを設定できます。 パーソナライズする - アクセスコードはリンクされたカード1枚で復元できます。すべてのカードを1か所に保管しないでください。 + アクセスコードはリンクされたカードおよびリング1つで復元できます。すべてのデバイスを1か所に保管しないでください。 復元する アクセスコードとして任意の単語、フレーズ、または数字を選択してください アクセスコードの作成 @@ -456,24 +463,26 @@ 操作を繰り返してください。カードは工場出荷時の設定にリセットされます。 アクティベーションに失敗しました トークンを追加する - バックアップカードを1枚追加しました。バックアップ処理が終了すると、これ以上バックアップカードを追加することはできません。もう一枚カードがあれば、バックアップに追加してください。バックアップ処理を続けますか? + バックアップカードまたはリングを1つ追加しました。バックアップ処理が終了すると、これ以上バックアップデバイスを追加することはできません。もう一つカードまたはリングがある場合、バックアップに追加してください。バックアップ処理を続けますか? バックアップ手続きは部分的に完了しています。現在終了することはできません。 パスフレーズは、暗号資産ウォレットが使用する高度なセキュリティ機能です。既存のリカバリフレーズに、自分で選択した単語またはフレーズを追加することで、まったく新しいアドレスのロック解除が可能になります。 - バックアップカードを追加する + バックアップ用のカードまたはリングを追加 + カードをスキャン カード #%d をスキャン 今すぐバックアップ - プライマリカードをスキャン + カードをスキャン + リングをスキャン ウォレットへ進む バックアップを完了する 暗号資産を受け取る プライマリカードをスキャン スキップする どのように機能しますか? - すべての秘密鍵をカード内で生成し、安全なウォレットを作成しましょう + すべての秘密鍵をカードまたはリング内で生成し、安全なウォレットを作成しましょう ウォレットを作成する ウォレットを作成する その他のオプション - 秘密鍵はカード内で安全に生成されます。シードフレーズは存在しないので、誰もエクスポートしたり盗んだりすることはできません。 + 秘密鍵はカードまたはリング内で安全に生成されます。シードフレーズは存在しないので、誰もエクスポートしたり盗んだりすることはできません。 秘密鍵を非公開で生成する カードは有効化され、使用可能になりました 成功! @@ -493,27 +502,27 @@ ウォレットをインポートするには、下のフィールドにシードフレーズを入力してください。 シードフレーズを生成する ウォレットをインポート - シードフレーズは、ウォレットを復元できる一連の単語です。カードによって生成される秘密鍵とは異なり、シードフレーズは保護されていないため、コピーされて盗まれる可能性があります。このオプションは自己責任で使用してください。 + シードフレーズは、ウォレットを復元できる一連の単語です。カードやリングによって生成される秘密鍵とは異なり、シードフレーズは保護されていないため、コピーされて盗まれる可能性があります。このオプションは自己責任で使用してください。 シードフレーズを使用する 無効なシードフレーズです。語順を確認してください。 無効なシードフレーズです。スペルを確認してください。 レガシー シードフレーズを正しく書き込んだかどうかを確認するために、2番目、7番目、11番目の単語を入力してください。 では、確認してみましょう - バックアップ処理を開始するには、最大2枚のバックアップカードを追加します。 - カードをもう1枚追加するか、バックアップ処理を完了します + バックアップ処理を開始するには、最大2つのバックアップカードまたはリングを追加します。 + カードまたはリングをもう1つ追加するか、バックアップ処理を完了します 番号%sのバックアップカードを準備してください - プライマリカードをスキャンして、バックアップ処理を開始します。 + プライマリカードまたはリングをスキャンして、バックアップ処理を開始します。 番号%sのプライマリカードを準備してください - ウォレットカードが設定され、使用できるようになりました。 - 最大枚数のカードが追加されました。バックアップ処理を完了します。 - カードの有効化 + ウォレットが設定され、使用できるようになりました。 + 最大数のデバイスが追加されました。バックアップ処理を完了します。 + ウォレットの有効化 バックアップカード # %d - バックアップカードなし + バックアップカードまたはリングなし 通知 - バックアップカードが1枚追加されました + バックアップデバイスが1つ追加されました カードを準備してください - バックアップカード2枚が追加されました + バックアップデバイス2つが追加されました 始めるには、ウォレットに任意の金額を入金するだけです 始めるには、ウォレットに%1$s %2$s以上入金するだけです 暗号資産を購入する @@ -521,7 +530,7 @@ ウォレットを有効化する ツイン化プロセスは部分的に完了しています。現在終了することはできません。 ウォレットの作成プロセスが何らかの理由で中断された場合は、最初からやり直す必要があります。 - 秘密鍵のバックアップは、未使用のTangemウォレットカード2枚まで可能です。 + 秘密鍵のバックアップは、未使用のTangemウォレットカードまたはリング2つまで可能です。 アクセスコードは、バックアップカード1枚を使用して復元できます。 すべてのバックアップカードは同一の秘密鍵を使用しており、すべての機能を使用できます。 ウォレットを保護するためにアクセスコードを設定できます。 @@ -570,11 +579,13 @@ カードをリセットする この操作を実行すると、現在のウォレットにアクセスできなくなることを理解しています。 このカードを使用して、現在のウォレットの他のカードのアクセスコードを回復させられないことを認識しています。 - 工場出荷時設定にリセットすると、選択したカードからウォレットが完全に削除されます。現在のウォレットを復元したり、カードを使用してアクセスコードを回復したりすることはできません。 - 工場出荷時の状態にリセットすると、選択したカードからウォレットが完全に削除され、アプリから削除されます。現在のウォレットを復元することはできません。 + 工場出荷時設定にリセットすると、選択したカードやリングからウォレットが完全に削除されます。現在のウォレットを復元したり、カードやリングを使用してアクセスコードを復元することはできません。 + 工場出荷時の状態にリセットすると、選択したカードやリングからウォレットが完全に削除され、アプリから削除されます。現在のウォレットを復元することはできません。 + Tangem Ringユーザーは、11/15日までChangelly経由でスワップを3回手数料ゼロで行えます! + 今すぐ手数料0%でスワップしましょう! 他の国の銀行カードと、ロシア連邦外での居住許可証または登録をお持ちですか? ロシアの銀行カードは現在ご利用いただけません - アプリにログインして、カードをスキャンせずに残高を確認できます + アプリにログインして、カードまたはリングをスキャンせずに残高を確認できます アプリにアクセスする 生体認証の使用を許可する ウォレットの操作には、アクセスコードの代わりに生体認証が要求されます。 @@ -582,10 +593,10 @@ 生体認証が無効になっているようです。ウォレットを保存する必要があります。 生体認証を有効にする 生体認証を使用しますか? - 資金を使って取引を行うには、カードが必要になりますのでご注意ください。 - カードをスキャン - カードをスキャンして設定を変更します。変更はスキャンしたカードにのみ影響し、ウォレットに関連付けられている他のカードには影響しません。 - カードを準備してください! + 資金を使って取引を行うには、カードまたはリングが必要になりますのでご注意ください + カードまたはリングをスキャン + カードまたはリングをスキャンして設定を変更します。変更はスキャンしたカードまたはリングにのみ影響し、ウォレットに関連付けられている他のデバイスには影響しません。 + Tangemを準備してください! 入力したアドレスにすでに含まれています 手数料額が推奨額の%s倍となっています。設定を再度確認し、調整してください。 推奨手数料を下回る手数料が指定されたため、取引に遅れが生じる可能性があります。続行しますか? @@ -661,7 +672,7 @@ 取引は正常に署名され、ブロックチェーンノードに送信されました。ウォレットの残高はしばらくして更新されます。 %1$sはTronネットワークのアセットです。手数料を計算して取引を行うには、アカウントにTron(TRX)を入金する必要があります。 取引が送信されました - セットアップしたいカードをスキャンするために準備してください。 + 設定したいカードまたはリングをスキャンするために準備してください。 ウォレット削除 これにより、ウォレットがアプリから削除されます。ウォレットは再度追加できます。 名前 @@ -685,7 +696,7 @@ 請求中の報酬 ステーキング報酬の受け取り方法。\n自動であなたのアドレスに報酬が入金される方法と、手動で取引を生成して報酬を引き出す方法があります。 報酬スケジュール - これは、ステーキングの参加者がいつ報酬を受け取るかを決定するスケジュールです。 + これは、ステーキングの参加者が報酬を受け取る時期を決定するスケジュールです。報酬の配布時間は、バリデーターとネットワークの負荷に応じて若干異なる場合があります。 報酬: %s ステーキング%s 解約完了までの期間 @@ -696,6 +707,7 @@ ロック中 移行 ネイティブステーキング + 現在、ステーキングに使用できるアクティブなバリデータは見つかりません。しばらくしてからもう一度お試しください。 獲得した報酬はあなたのアドレスに直接送られ、すぐ使用可能です。 安全にステーキングして報酬を獲得しましょう 安全にステーキングして、報酬を毎日獲得しましょう @@ -728,8 +740,6 @@ 毎日 %sごと 毎日 - %1$s日ごとに - %1$s秒毎 エポック毎 時代毎 毎時間 @@ -738,7 +748,7 @@ 報酬 ステーキングはロックされています もっとステーキングする - %1$sをステーキングし、年間 %2$sを受け取ります + %1$sをステーキングし、毎%2$s報酬を受け取ります タップしてロック解除 タップしてロック解除もしくは投票 タップして引き出す @@ -758,13 +768,13 @@ 投票する 引き出す あなたのステーキング - カード内に秘密鍵を保管しながら暗号資産を安全に保管します + カードまたはリング内に秘密鍵を保管しながら、暗号資産を安全に保管します 革新的なハードウェアウォレット - 1つのウォレットに最大3枚のカード + 1つのウォレットに最大3枚のカードまたは3本のリング 安全なバックアップ - ビットコイン、イーサリアム、その他多くの暗号資産を同時に管理できるハードウェアウォレット – オールインワンカード + ビットコイン、イーサリアム、その他多くの暗号資産を同時に管理できるハードウェアウォレット – オール・イン・ワンのカード / リング 数千の通貨 - 外出先でも、いつでもどこでも使用できます。コードや電池は不要です。暗号資産が必要なときに、カードをスマートフォンにタップするだけです。 + 外出先でも、いつでもどこでも使用できます。コードや電池は不要です。暗号資産が必要なときに、カードまたはリングをスマートフォンにタップするだけです。 すべての人のためのウォレット Tangemのご紹介 100種類以上の分散型サービスで、NFTの交換・購入や、借入・預金を行うことができます。 @@ -832,13 +842,13 @@ Tangemツイン この操作は元に戻せません。古いウォレットにはアクセスできなくなります。 番号%sのツインカードをタップし、操作が終了するまで取り外さないでください。 - %sを使用するか、カードをスキャンしてウォレットにアクセスしてください + %sを使用するか、カード / リングをスキャンしてウォレットにアクセスしてください 最新の機能とニュースをお届けします 新しいプロモーション情報をいち早く入手しましょう プッシュ通知を使用しますか? 新しいウォレットを追加 本当にこのウォレットを削除してもよろしいですか? - エラーが発生しました。カードをスキャンしてログインしてください。 + エラーが発生しました。カードまたはリングをスキャンしてログインしてください。 このウォレットはすでに保存されています。別のウォレットを追加できます。 %sという名前のウォレットはすでに存在します。 ウォレット名 @@ -846,7 +856,7 @@ すべてのロックを解除 %sですべてをロック解除 ブロックチェーンにアクセスできません。後でもう一度お試しください。 - カードをスキャン + カードまたはリングをスキャン メッセージに署名することを要求しています。 \n\n %s Dapp%1$s 、BNB取引の署名を要求しています\n\n%2$s %1$sの取引注文\n価格: %2$s\n受取金額: %3$s\n支払金額: %4$s @@ -860,7 +870,7 @@ セッションリクエストには、WalletConnect接続でサポートされていないブロックチェーンが含まれています。サポートされていないブロックチェーン: \n 技術的な実装のため、このDappとの接続は確立できません。 不明なエラーが発生しました。エラーメッセージ: %s 。問題が解決しない場合は、お気軽にサポートにお問い合わせください。 - Tangemアプリで間違ったカードが選択されました + Tangemアプリで誤ったカードまたはリングが選択されました Dappデータから取引を作成できませんでした。コード: %s 不明なエラーが発生しました。エラーコード: %d 。問題が解決しない場合は、お気軽にサポートにお問い合わせください。 WalletConnectセッションが開かれていません @@ -885,10 +895,10 @@ インターネット接続がありません ウォレット設定 Tangem - %sを使用するか、カードをスキャンしてウォレットにアクセスしてください + %sを使用するか、カード / リングをスキャンしてウォレットにアクセスしてください 許可付与のプロセスは現在進行中であり、まもなく完了する予定です。 承認中 - カードのアクティベーションが正しく完了しませんでした。デバイスの NFCモジュールに問題があるか、カードをデバイスに正しくタップしていないことが原因かもしれません。サポートチームにお問い合わせください。 + カードまたはリングのアクティベーションが正しく完了しませんでした。デバイスのNFCモジュールに問題があるか、カードまたはリングをデバイスに正しくタップしていないことが原因かもしれません。サポートチームにお問い合わせください。 アクティベーションに失敗しました BNBネットワーク開発者によると、BEP-2規格のサポートは2024年6月に終了します。この規格の資産を失わないために、BEP-20規格に変換してください。BNBスマートチェーンネットワークへ移行するには、Tangemのスワップサービスをご利用ください。 BNBビーコンチェーンは閉鎖されます。 @@ -934,7 +944,7 @@ MATICはPOLに移行中です。ただし、期限は設定されておらず、MATICはまだ廃止されていません。MATICトークンを引き続き安全に使用することも、取引所でPOLに交換することもできます。 MATICからPOLへの移行 - %d ネットワークのアドレスを取得するために、カードを利用してください + %d ネットワークのアドレスを取得するために、カードまたはリングを利用してください 一部のアドレスが見つかりません 現在、ネットワークにアクセスできません。しばらくしてからもう一度お試しください。 @@ -961,11 +971,11 @@ バックアップが中断されました。再開しますか? はい、再開します 破棄 - バックアップを破棄する場合は、カードを工場出荷時の設定にリセットしてやり直す必要があります。 + バックアップを破棄する場合は、デバイスを工場出荷時の設定にリセットしてやり直す必要があります。 バックアップを再開する これはやり直しができないアクションです %sでログイン - カードをスキャン - アプリにアクセスするには、 %sを使用するか、カードをスキャンしてください + カードまたはリングをスキャン + アプリにアクセスするには、 %sを使用するか、カードまたはリングをスキャンします お帰りなさい! diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 57db1a7c37..3ac533d36e 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -12,8 +12,8 @@ Не могу отправить транзакцию Выбранный кошелёк не поддерживает сеть %1$s Для активации криптографии сети %1$s необходимо сбросить кошелек до заводских настроек. Пожалуйста, выведите свои средства, чтобы не потерять их, после сброса доступ к текущему кошельку будет невозможен. - Токены в сети %1$s не поддерживаются этой картой из-за ограничений прошивки. - У вас возникли трудности со сканированием карты? + Токены в сети %1$s не поддерживаются этой картой или кольцом из-за ограничений прошивки. + У вас возникли трудности со сканированием карты или кольца? Эта карта не предназначена для работы с этим приложением Комиссия по-умолчанию Подключите функцию комиссии по умолчанию и при формировании транзакции на отправку средств комиссия будет выставлена автоматически, а экран комиссии пропущен. Вы всегда сможете на него вернуться. @@ -22,9 +22,9 @@ Все сохраненные коды доступа будут удалены. Вам потребуется вводить код доступа при работе с кошельком. При отключении функции сохранения кошелька все ранее сохраненные кошельки будут удалены из приложения. Сохранение кода доступа - Подключите функцию хранения кодов доступа от карт на телефоне в зашифрованном виде, и при работе с картой вместо кода доступа будет запрашиваться биометрическая аутентификация. + Подключите функцию хранения кодов доступа от карт на телефоне в зашифрованном виде, и при работе с картой или кольцом вместо кода доступа будет запрашиваться биометрическая аутентификация. Cохранение кошелька - Подключите функцию привязки карты в приложении, а также возможность биометрической аутентификации. Подпись транзакции все так же потребует карту. + Подключите функцию привязки карты в приложении, а также возможность биометрической аутентификации. Подпись транзакции все так же потребует карту или кольцо. Тёмная Светлая Как в системе @@ -34,17 +34,17 @@ Больше не показывать Понятно Балансы скрыты - Пожалуйста, отсканируйте карту - Пожалуйста, попробуйте снова через 30 секунд или отсканируйте карту + Пожалуйста, отсканируйте карту или кольцо + Пожалуйста, попробуйте снова через 30 секунд или отсканируйте карту или кольцо Слишком много попыток Вы отключили биометрическую аутентификацию на вашем телефоне и не сможете сохранять кошельки в приложении. Для сохранения кошельков, пожалуйста, включите функцию биометрической аутентификации в настройках телефона. Начать резервное копирование Используйте банковскую карту или другие методы оплаты - %d карта - %d карты - %d карт - %d карт + %d устройство + %d устройства + %d устройств + %d устройств Отключите эту опцию, если не хотите, чтобы эта карта использовалась для сброса кодов доступа на другие карты этого кошелька. Обратите внимание, сброс кода также не будет доступен на этой карте. Использовать эту карту для сброса кода доступа на других картах в этом кошельке @@ -52,16 +52,16 @@ Сбросить Вы уверены, что хотите это сделать? Смена кода доступа - Код доступа будет изменен только на данной карте - Все карты выбранного кошелька сброшены до заводских настроек, вы можете создать новый кошелек + Код доступа будет изменен только на данной карте или кольце + Все устройства Tangem выбранного кошелька сброшены до заводских настроек, вы можете создать новый кошелек Сброс завершён - Хотите сбросить следующую карту от этого кошелька? - Сброс карты - Рекомендуем завершить процесс сброса всех карт кошелька - Вы сбросили не все карты + Хотите сбросить следующее устройство от этого кошелька? + Сброс кошелька + Рекомендуем завершить процесс сброса всех устройств Tangem кошелька + Вы сбросили не все Tangem устройства Заводские настройки Тип безопасности - Настройки карты + Настройки устройства Помимо сетевой комиссий, сеть Cardano взимает %1$s ADA при транзакции с токеном %2$s Требования к транзакции Cardano Чтобы совершить транзакцию %1$s, внесите некоторую сумму ADA для покрытия сетевой комиссии и минимального значения ADA (рекомендуется 5 ADA) @@ -122,7 +122,7 @@ По рынку Медленно Скорость и комиссия - Получить адреса + Получить ваши адреса К провайдеру Перейти в токен Импортировать @@ -131,6 +131,7 @@ Осталось %1$s Заблокирован Основная сеть + месяц Сетевая комиссия Сумма отправки будет уменьшена на %1$s (%2$s) для покрытия выбранного уровня комиссии Далее @@ -139,6 +140,7 @@ Сейчас OK Основная карта + Основное кольцо Парольная фраза Вставить Политикой конфиденциальности @@ -153,6 +155,7 @@ Сохранить изменения Поиск Поиск токенов + сек Seed-фраза Выберите действие Продать @@ -214,13 +217,13 @@ Код доступа Перед сканированием карты вам нужно будет ввести правильный код доступа. Задержка сканирования - Этот механизм защищает карту от бесконтактных атак. Между сканированием карты и выполнением команды будет добавлена задержка. + Этот механизм защищает карту или кольцо от бесконтактных атак. Между сканированием и выполнением команды будет добавлена задержка. Пароль Перед выполнением любой команды, влекущей за собой изменение состояния карты, вам необходимо будет ввести пароль. Реферальная программа Переверните экран вашего устройства вниз, чтобы быстро скрыть и отобразить балансы %s хэшей - Номер карты + Номер устройства Обратиться в поддержку Добавить еще карты Валюта приложения @@ -231,7 +234,7 @@ Подробности Проверьте подключение с интернетом или переключитесь на другую сеть Условия использования - Вы использовали карту от другого кошелька. Приложите карту, связанную с этим кошельком. + Вы использовали карту или кольцо от другого кошелька. Приложите карту или кольцо, связанную с этим кошельком. Мои токены У вас нет добавленных токенов. Добавьте токены для обмена Недоступен для обмена с %s @@ -288,14 +291,15 @@ Обменяйте любой актив в вашем портфеле на этот токен Информация ниже не является обязательной. Вы можете стереть её, если хотите. Расскажите, каких функций вам не хватает, и мы постараемся вам помочь. - Скажите, пожалуйста, какая у вас карта? + Скажите, пожалуйста, какая у вас карта или кольцо? Привет, команда поддержки, Пожалуйста, расскажите нам больше о вашей проблеме. Каждая маленькая деталь может помочь. Мои предложения - Не могу отсканировать карту + Не могу отсканировать карту/кольцо Обращение в поддержку Обращение в поддержку Tangem Не могу отправить транзакцию + Ошибка в описании монеты Транзакция Комиссия сети за одобрение токена будет взиматься за подтверждение того, что именно вы разрешаете использовать ваш токен для обмена. Укажите лимит доступа к выбранному токену @@ -308,14 +312,15 @@ Купить Сканировать Информация сгенерирована ИИ - Чтобы изменить код доступа, приложите карту как показано выше и не убирайте до окончания операции + Чтобы изменить код доступа, приложите карту или кольцо как показано выше и не убирайте до окончания операции Чтобы изменить пароль, приложите карту как показано выше и не убирайте до окончания операции Чтобы создать кошелек, приложите карту как показано выше и не убирайте до окончания операции + Чтобы создать кошелек, приложите кольцо как показано выше и не убирайте до окончания операции Приложите карту #%s для сброса Приложите, чтобы отсканировать Приложите, чтобы подписать - Приложите карту - Вы обновили данные биометрии, отсканируйте свою карту для входа + Приложите карту или кольцо + Вы обновили данные биометрии, отсканируйте свою карту или кольцо для входа Ваш баланс должен быть выше суммы комиссии для осуществления перевода Недостаточно средств У вас недостаточно Маны для этой транзакции. Пожалуйста, подождите, пока Мана восполнится. Ваш баланс маны равен %1$s/%2$s @@ -327,13 +332,14 @@ Чтобы начать отслеживать свои криптоактивы и транзакции, добавьте токены Управление токенами Чтобы получить доступ ко всем сетям, вам необходимо отсканировать карту - Отсканируйте карту + Отсканируйте карту или кольцо Обменивайте свои токены с %1$s комиссии провайдера через Changelly с %2$s по %3$s февраля. Обмен с Changelly, %s комиссии Токены Добавить Изменить Рыночная капитализация + Блокчейн, в котором криптовалюта изначально была создана Основная сеть Не основной или основной блокчейн, на котором размещен токен Не основные сети @@ -354,14 +360,14 @@ Выберите кошелек Кошелёк не поддерживает более одной сети Чтобы купить, обменять или получить данный токен, вам нужно добавить его к себе в портфель - Этот актив недоступен + Этот актив в настоящее время не поддерживается в кошельке Этот токен не доступен для данного кошелька Добавить в портфель Добавить Доступные сети Мой портфель Рынок - Чтобы создать адреса для выбранных сетей, отсканируйте вашу карту Tangem кошелька + Чтобы создать адреса для выбранных сетей, отсканируйте вашу карту или кольцо Tangem кошелька Потяните вверх или коснитесь поисковой строки, чтобы добавить токен Данные раздела получены из следующих сетей: %s Невозможно загрузить данные @@ -457,11 +463,11 @@ Потяните вверх или коснитесь поисковой строки, чтобы добавить токены напрямую из рынка Добавить токены Функция NFC недоступна на вашем устройстве - Вам необходимо установить единый код доступа для защиты всех ваших карт + Установите единый код доступа для защиты всех ваших карт или колец Защита - Позже вы сможете установить индивидуальный код доступа для каждой карты + Установите индивидуальный код доступа для каждой карты или кольца позже. Персонализация - Код доступа можно восстановить с помощью привязанной карты. Не храните все карты в одном месте. + Восстановите код доступа с помощью связанной карты или кольца. Не храните все устройства в одном месте. Восстановление Выберите любое слово, фразу или число в качестве кода доступа Создайте код доступа @@ -472,24 +478,26 @@ Необходимо повторить операцию, при этом карта будет сброшена к заводским настройкам Ошибка активации Добавление токенов - Вы добавили одну резервную карту. После того, как процесс будет завершен, Вы больше не сможете добавить карт. Если у Вас есть еще одна карта, добавьте ее в резервную копию. Хотите продолжить? + Вы добавили одну резервную карту или кольцо. После того, как процесс будет завершен, Вы больше не сможете добавить еще. Если у Вас есть еще одна карта или кольцо, добавьте ее в резервную копию. Хотите продолжить? Процесс резервного копирования почти завершен. Вы не можете выйти из него сейчас. Парольная фраза — это расширенная функция безопасности, которую используют криптокошельки. Она добавляет дополнительное слово или фразу по вашему выбору к уже существующей seed - фразе, чтобы разблокировать совершенно новый набор адресов. - Добавить резервную карту + Добавить карту или кольцо + Сканировать карту Сканировать карту #%d Создать резервную копию - Сканировать основную карту + Сканировать карту + Сканировать кольцо Перейти к моему кошельку Завершение бэкапа Получить криптовалюту Сканировать основную карту Пропустить Как это работает? - Давайте сгенерируем все ключи на вашей карте и создадим безопасный кошелек + Давайте сгенерируем все ключи на вашей карте или кольце и создадим безопасный кошелек Создать кошелек Создать кошелек Другие опции - Ваши ключи будут надежно сгенерированы внутри карты. Никакой seed-фразы, а это значит, что никто не может экспортировать или украсть ее. + Ваши ключи будут надежно сгенерированы внутри чипа. Никакой seed-фразы, а это значит, что никто не может экспортировать или украсть ее. Cоздавайте ключи приватно Ваша карта активирована и готова к использованию Успешно! @@ -515,27 +523,30 @@ Чтобы импортировать кошелек, введите seed-фразу в поле ниже Создать seed-фразу Импорт кошелька - Seed-фраза — это набор слов, который дает возможность восстановить кошелек. В отличие от ключей, сгенерированных картой, seed-фраза не защищена и может быть скопирована и украдена. Используйте этот вариант на свой страх и риск. + Seed-фраза — это набор слов, который дает возможность восстановить кошелек. В отличие от ключей, сгенерированных картой или кольцом, seed-фраза не защищена и может быть скопирована и украдена. Используйте этот вариант на свой страх и риск. Использовать seed-фразу Неверная seed-фраза. Пожалуйста, проверьте порядок слов. Неверная seed-фраза. Пожалуйста, проверьте орфографию. Устаревший Чтобы проверить, правильно ли вы записали seed-фразу, введите 2-е, 7-е и 11-е слова Итак, проверим - Чтобы начать процесс резервного копирования, добавьте одну или две резервные карты. - Вы можете добавить еще одну карту или завершить процесс резервного копирования + Чтобы начать процесс резервного копирования, добавьте одну или две резервные карты или кольца. + Вы можете добавить еще одну карту или кольцо или завершить процесс резервного копирования Подготовьте резервную карту с номером %s - Отсканируйте основную карту, чтобы начать процесс резервного копирования. + Отсканируйте основную карту или кольцо, чтобы начать процесс резервного копирования. Подготовьте основную карту с номером %s - Ваша карта настроена и готова к использованию. - Добавлено максимальное количество карт. Завершите процесс резервного копирования. + Приготовьте кольцо и нажмите кнопку сканирования кольца внизу + Ваш кошелек настроен и готов к использованию. + Добавлено максимальное количество устройств. Завершите процесс резервного копирования. Активация кошелька + Резервная карта Резервная карта #%d - Нет резервных карт + Резервное кольцо + Нет резервных устройств Уведомления - Добавлена ​​одна резервная карта + Добавлено одно резервное устройство Подготовьте свою карту - Добавлены две резервные карты + Добавлены два резервных девайса Пополните кошелек на любую сумму, чтобы начать пользоваться картой Пополните кошелек более чем на %1$s %2$s, чтобы начать пользоваться картой Купить криптовалюту @@ -543,7 +554,7 @@ Активация кошелька Процесс связывания карт частично завершен. Вы не можете выйти из него сейчас. Если процесc создания кошелька каким-либо образом прервется, вам придется начинать сначала - Вы можете сделать резервную копию своих ключей на одной или двух других пустых картах Wallet. + Вы можете сделать резервную копию своих ключей на одной или двух других пустых картах или кольцах Tangem Wallet. Код доступа можно восстановить с помощью одной из резервных карт. Все резервные карты являются полнофункциональными и содержат одинаковые ключи. Вы сможете установить код доступа для защиты своих кошельков. @@ -598,11 +609,13 @@ Сбросить карту Я понимаю, что после выполнения этого действия у меня больше не будет доступа к текущему кошельку Я понимаю, что не смогу этой картой восстановить пароль на остальных картах этого кошелька, если я его забуду - Сброс к заводским настройкам приведет к полному удалению кошелька с выбранной карты. Вы не сможете восстановить текущий кошелек или использовать данную карту для восстановления кода доступа. - Сброс к заводским настройкам приведет к полному удалению кошелька с выбранной карты. Вы не сможете восстановить текущий кошелек. + Сброс к заводским настройкам приведет к полному удалению кошелька с выбранной карты или кольца. Вы не сможете восстановить текущий кошелек или использовать данную карту или кольцо для восстановления кода доступа. + Сброс к заводским настройкам приведет к полному удалению кошелька с выбранной карты или кольца. Вы не сможете восстановить текущий кошелек. + Владельцам колец — 3 обмена без комиссии на Changelly до 15.11! + Обмен с 0%% комиссией! У вас есть карта банка другой страны, а также вид на жительство или регистрация вне РФ? Карты банков РФ в данный момент не принимаются - Войдите в приложение и следите за своим балансом без сканирования карты + Войдите в приложение и следите за своим балансом без сканирования карты или кольца Доступ в приложение Использовать биометрию Для операций с вашим кошельком будет запрашиваться биометрия вместо кода доступа карты @@ -610,10 +623,10 @@ Похоже, что у вас отключена биометрическая аутентификация, она необходима для сохранения кошельков Включите биометрическую аутентификацию Вы хотите использовать биометрию? - Обратите внимание, что для совершения транзакции с вашими средствами по-прежнему потребуется ваша карта + Обратите внимание, что для совершения транзакции с вашими средствами по-прежнему потребуется ваша карта или кольцо Сканировать - Отсканируйте карту, чтобы изменить ее настройки. Изменения затронут только ту карту, которую вы отсканировали, и не повлияют на другие карты, привязанные к вашему кошельку. - Приготовьте свою карту + Отсканируйте карту или кольцо, чтобы изменить ее настройки. Изменения затронут только ту карту или кольцо, которые вы отсканировали, и не повлияют на другие устройства, привязанные к вашему кошельку. + Приготовьте свой Tangem Уже содержится во введенном адресе Сумма комиссии в %s раз превышает рекомендованную. Убедитесь, что указанная комиссия верна. Вы указали комиссию ниже рекомендуемой, это может привести к задержке исполнения вашей транзакции. Продолжить? @@ -689,7 +702,7 @@ Транзакция успешно подписана и отправлена в блокчейн. Баланс будет обновлен через некоторое время %1$s — это монета в сети Tron. Чтобы рассчитать комиссию и совершить транзакцию, вам необходимо внести немного Tron (TRX) на свой адрес. Транзакция отправлена - Подготовьтесь к сканированию карты, которую вы хотите настроить. + Подготовьтесь к сканированию кольцо или карту, которую вы хотите настроить. Забыть кошелек Это приведет к удалению кошелька из приложения. Сам кошелек можно добавить снова. Имя @@ -724,6 +737,7 @@ Заблокировано Переместить Нативный стейкинг + В данный момент нет доступных активных валидаторов для стейкинга. Пожалуйста, попробуйте позже. Полученная награда отправится на ваш кошелек и сразу станет доступна для использования Стейкайте безопасно и начинайте получать ваши награды. Стейкайте безопасно и начинайте получать ежедневные награды. @@ -754,8 +768,6 @@ За блок Ежедневно Каждый день - Каждые %1$s дня - Каждые %1$s сек За эпоху За эру Ежечасно @@ -784,20 +796,20 @@ Проголосовать Вывод Застейканные средства - Держите свои криптосбережения в безопасности. Приватные ключи надежно хранятся на карте. + Держите свои криптосбережения в безопасности. Приватные ключи надежно хранятся на карте или кольце. Революционный аппаратный кошелек - До трех карт с одним кошельком + До трех карт или колец с одним кошельком Все ключи в безопасности - Аппаратный кошелек для ваших биткоинов, эфира и многих других валют одновременно — все в одной карте + Аппаратный кошелек для ваших биткоинов, эфира и многих других валют одновременно — все в одной карте или кольце Тысячи криптовалют - Используйте его на ходу, в любом месте, в любое время. Без проводов и батареек. Как только понадобится крипта, просто приложите карту к телефону. + Используйте его на ходу, в любом месте, в любое время. Без проводов и батареек. Как только понадобится крипта, просто приложите карту или кольцо к телефону. Кошелек для каждого Встречайте Tangem Обменивайте, покупайте NFT, получайте займы и делайте вклады в более чем 100 различных децентрализованных сервисах Поддержка Web 3.0 Обменивайте больше токенов по лучшим курсам прямо в вашем кошельке. Новый провайдер обмена! - В сумму включено: \n• комиссия провайдера сервиса\n• комиссия сети за отправку %s от биржи обратно на адрес пользователя + В сумму включено: \n• комиссия провайдера сервиса\n• комиссия сети за отправку %s от биржи обратно на адрес пользователя. В сумму включена комиссия провайдера сервиса. Комиссии Подтверждения считаются отраслевым стандартом для всех децентрализованных бирж и защищают ваш кошелек от доступа со стороны смарт-контракта без вашего разрешения. По замыслу смарт-контракты не могут получить доступ к вашим токенам, если вы не одобрите доступ со своей стороны. «Разблокируя» свои токены, вы даете смарт-контракту 1inch разрешение тратить ваши активы. Майнеры сети получают компенсацию за газ (оплачиваемый вами) за запись этого действия в блокчейне. Как только разрешение будет предоставлено, вы сможете обменять свой токен. @@ -858,13 +870,13 @@ Tangem Twin Это действие необратимо. У вас не будет доступа к старому кошельку. Приложите twin-карту с номером %s и не убирайте до окончания операции - Используйте %s или отсканируйте карту, чтобы получить доступ к своему кошельку + Используйте %s или отсканируйте карту/кольцо, чтобы получить доступ к своему кошельку Будьте в курсе новых функций и новостей Узнавайте первым о новых акциях Хотите использовать Push-уведомления? Добавить новый кошелек Вы уверены, что хотите удалить этот кошелек? - Произошла ошибка, пожалуйста, отсканируйте свою карту для входа + Произошла ошибка, пожалуйста, отсканируйте свою карту или кольцо для входа Этот кошелек уже был сохранен, вы можете добавить другой Кошелек с именем %s уже существует Имя кошелька @@ -872,7 +884,7 @@ Разблокировать все Разблокировать все с %s Блокчейн недоступен. Попробуйте позже. - Отсканируйте карту + Отсканируйте карту или кольцо Запрос на подпись сообщения.\n\n%s Dapp %1$s, запрос на\nподпись транзакции с BNB.\n\n%2$s Торговый ордер на %1$s\nЦена: %2$s\nСумма к получению: %3$s\nСумма к оплате: %4$s @@ -887,7 +899,7 @@ Запрос на подключение через WalletConnect содержит неподдерживаемые блокчеины. Неподдерживаемые блокчеины:\n Cоединение с этим Dapp сервисом не может быть установлено из-за его технической реализации. Произошла непредвиденная ошибка. Сообщение ошибки: %s Попробуйте, пожалуйста, позже. Если проблема будет продолжать возникать - обратитесь в службу поддержки. - Неверная карта выбрана в приложении Tangem + Неверная карта или кольцо выбрана в приложении Tangem Не удалось создать транзакцию из данных Dapp. Код: %s Произошла непредвиденная ошибка. Код ошибки: %d. Попробуйте, пожалуйста, позже. Если проблема будет продолжать возникать - обратитесь в службу поддержки. Нет открытых сессий WalletConnect @@ -912,10 +924,10 @@ Нет соединения с интернетом Настройки кошелька Tangem - Используйте %s или отсканируйте карту, чтобы разблокировать доступ к вашему кошельку + Используйте %s или отсканируйте карту/кольцо, чтобы разблокировать доступ к вашему кошельку Процесс выдачи разрешения уже в работе и скоро будет завершен Выдача разрешения - Похоже, что процесс активации карт не был завершен корректно. Это могло быть вызвано проблемой взаимодействия с модулем NFC либо некорректным прикладыванием карты к телефону. Пожалуйста, обратитесь в нашу службу поддержки для уточнения деталей. + Похоже, что процесс активации карт или кольца не был завершен корректно. Это могло быть вызвано проблемой взаимодействия с модулем NFC либо некорректным прикладыванием карты или кольца к телефону. Пожалуйста, обратитесь в нашу службу поддержки для уточнения деталей. Ошибка активации По решению разработчиков сети BNB стандарт BEP-2 перестанет поддерживаться в июне 2024 года. Чтобы не потерять активы, их необходимо преобразовать в стандарт BEP-20. Используйте функцию обмена в приложении или сторонние сервисы, чтобы перевести средства в cеть BNB Smart Chain. Отключение сети BNB Beacon Chain @@ -961,10 +973,10 @@ Токен MATIC мигрирует на POL. Однако крайний срок не установлен, и MATIC пока не устарел. Вы можете спокойно продолжать использовать токен MATIC или воспользоваться биржами, чтобы обменять его на POL. Миграция MATIC в POL - Используйте вашу карту, чтобы получить адрес для %d сети - Используйте вашу карту, чтобы получить адреса для %d сетей - Используйте вашу карту, чтобы получить адреса для %d сетей - Используйте вашу карту, чтобы получить адреса для %d сетей + Используйте вашу карту или кольцо, чтобы получить адрес для %d сети + Используйте вашу карту или кольцо, чтобы получить адреса для %d сетей + Используйте вашу карту или кольцо, чтобы получить адреса для %d сетей + Используйте вашу карту или кольцо, чтобы получить адреса для %d сетей Некоторые адреса отсутствуют В данный момент сеть недоступна. Пожалуйста, попробуйте позже. @@ -991,11 +1003,11 @@ Вы не закончили резервное копирование. Хотите продолжить? Да, возобновить Отказаться - Если сейчас отказаться, то придётся сбрасывать карты до заводских настроек, чтобы начать заново + Если сейчас отказаться, то придётся сбрасывать устройства до заводских настроек, чтобы начать заново Возобновить резервное копирование Это необратимое действие Войти с %s - Сканировать карту - Используйте %s или отсканируйте карту для входа в приложение + Сканировать карту или кольцо + Используйте %s или отсканируйте карту либо кольцо для входа в приложение C возвращением! diff --git a/core/res/src/main/res/values-uk-rUA/strings.xml b/core/res/src/main/res/values-uk-rUA/strings.xml index 5a9fb13979..c24d3cce76 100644 --- a/core/res/src/main/res/values-uk-rUA/strings.xml +++ b/core/res/src/main/res/values-uk-rUA/strings.xml @@ -101,6 +101,12 @@ %d днів %d днів + + день + дні + днів + днів + Видалити Вимкнуто Готово @@ -119,12 +125,14 @@ Отримати адреси Перейти до провайдера Перейти до токену + година Імпортувати В процесі Пізніше Залишилося %1$s Заблокований Основна мережа + місяць Комісія мережі Сума відправлення буде зменшена на %1$s (%2$s) для покриття обраного рівня комісії Далі @@ -147,6 +155,7 @@ Зберегти зміни Пошук Пошук токенів + сек Seed-фраза Оберіть дію Продати @@ -172,6 +181,7 @@ Виникла помилка. Будь ласка, спробуйте ще раз. Недоступно Скасувати стейкінг + тиждень Так Адреса контракту скопійована! Доступні мережі @@ -290,6 +300,7 @@ Звернення в підтримку Звернення в підтримку Tangem Не вдається відправити транзакцію + Помилка в описі монети Транзакція Мережа стягує комісію за схвалення токену за підтвердження, що саме ви дозволяєте використовувати ваш токен для обміну. Вкажіть ліміт доступу для обраного токена @@ -349,8 +360,8 @@ Проголосувати Оберіть гаманець Гаманець не підтримує більше однієї мережі - Щоб почати купувати, обмінювати або отримувати цей актив, додайте цей токен принаймні в 1 мережу - Цей актив недоступний + Щоб купити, обміняти або отримати цей актив, додайте його до свого портфоліо + Цей актив наразі не підтримується в гаманці Цей токен недоступний для цього гаманця Додати в портфоліо Додати @@ -384,6 +395,12 @@ Лідери падіння В тренді Про %s + + біржа + біржі + бірж + бірж + На основі %d оцінки На основі %d оцінок @@ -397,6 +414,12 @@ Циркуляційний запас Загальна кількість монет, які доступні для торгівлі та перебувають в обігу на ринку Циркуляційний запас + Бірж не знайдено + Біржа + Обережно + Ризиковано + Надійно + Біржі Досвідчені покупці Мережеві покупці з додатковою вимогою мати не менше 100 вихідних транзакцій Досвідчені покупці @@ -414,6 +437,7 @@ Зміна того, скільки ліквідності доступно для токена протягом зазначеного періоду часу Ліквідність Індекс ліквідності + Лістинг на Низький Рин. кап. Загальна ринкова вартість криптовалюти, що розраховується шляхом множення поточної ціни монети на загальну кількість монет в обігу @@ -436,6 +460,7 @@ Обсяг торгів (24г) Загальна сума криптовалюти, якою торгували протягом останніх 24 годин, що вказує на рівень активності та ліквідності на ринку Обсяг торгів (24г) + Обсяг Потягніть вгору або торкніться панелі пошуку, щоб додати токени безпосередньо з маркету Додати токени Функція NFC недоступна на вашому пристрої @@ -582,6 +607,8 @@ Я розумію, що не можу використати цю картку для відновлення свого коду доступу на інших картках поточного гаманця Скидання до заводських налаштувань призведе до повного видалення гаманця з обраної картки. Ви не зможете відновити поточний гаманець або використати картку для відновлення коду доступу. Скидання до заводських налаштувань призведе до повного видалення гаманця з обраної картки. Ви не зможете відновити поточний гаманець. + Власникам кілець - 3 обміни без комісії на Changelly до 15.11! + Обмін з 0%% комісією! У вас є банківська картка іншої країни та посвідка на проживання або реєстрація за межами Російської Федерації? Російські банківські картки наразі не приймаються Увійдіть у додаток та слідкуйте за своїм балансом без сканування картки @@ -706,6 +733,7 @@ Заблоковано Перемістити Нативний стейкінг + На даний момент немає доступних активних валідаторів для стейкінгу. Будь ласка, спробуйте пізніше. Зароблені винагороди будуть надіслані на ваш гаманець і доступні для використання відразу Стейкайте безпечно та почніть отримувати ваші винагороди Стейкайте безпечно та почніть отримувати винагороди щоденно @@ -736,8 +764,8 @@ Вручну Блок День + Кожен %s Щодня - Кожні %1$s сек Епоха Ера Година @@ -748,6 +776,7 @@ Застейкати більше Ви стейкаєте %1$s і будете отримувати %2$s щороку Натисніть, щоб розблокувати + Натисніть, щоб розблокувати або проголосувати Натисніть, щоб вивести кошти Застейкати %s Зняти зі стейкінгу %s @@ -762,7 +791,7 @@ Валідатори Стратегічний партнер Проголосувати - Голос заблоковано + Проголосувати Зняти Ваші стейкінги Тримайте свою криптовалюту в безпеці. Приватні ключі надійно зберігаються на картці. diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 7d4c202a25..513cd8eba2 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -12,19 +12,19 @@ Can\'t send a transaction The selected does not support the %1$s network To activate the %1$s blockchain\'s cryptographic encryption, you\'ll need to reset the wallet to factory settings. Please withdraw your funds before doing so to ensure that you don\'t lose them, and then complete the reset process. Access to the current wallet will not be possible after the reset. - Tokens in %1$s network are not supported by this card due to firmware limitation. - Are you having difficulty scanning your card? + Tokens in %1$s network are not supported by this card or ring due to firmware limitation. + Are you having difficulty scanning your card or ring? This card is not designed to work with this app Default Fee Enable Default Fee to set transaction fees automatically and skip the Fee page when sending funds. You can always go back to this page if necessary. Go to settings to enable biometric authentication in the Tangem App Enable biometric authentication This will delete all the saved wallet access codes. Any further operation with the wallet will require submitting the access code. - Removing the saved card deletes all the saved wallets and their access codes from the app. + Removing the saved devices deletes all the saved wallets and their access codes from the app. Save Access Code - Biometric authentication will be requested instead of the access code for interactions with your card. + Biometric authentication will be requested instead of the access code for interactions with your card or ring. Keep the wallet in the app - Enable to link all the wallets to Tangem app. Biometric authentication will be required for unlocking the app. Transaction signing requires tapping your Tangem card. + Enable to link all the wallets to Tangem app. Biometric authentication will be required for unlocking the app. Transaction signing requires tapping your Tangem card or ring. Dark Light System default @@ -34,32 +34,32 @@ Don\'t show again Got it Balances are hidden - Please scan the card - Please try again in 30 seconds or scan the card + Please scan the card or ring + Please try again in 30 seconds or scan the card or ring Too many attempts You have disabled biometric authentication on your phone and will not be able to save wallets in the app. To save wallets, please enable the biometric authentication function in your phone settings. Start backup process Use a bank card or other payment methods - %d card - %d cards + %d device + %d devices - Disable this option if you don\'t want this card to be used to reset access codes on other cards in this wallet. Please note that this will also prevent you from resetting the access code on this card. + Disable this option if you don\'t want this card to be used to reset access codes on other cards or rings in this wallet. Please note that this will also prevent you from resetting the access code on this card. Allows you to use this card to reset access code on other cards in this wallet Access code recovery Reset Are you sure you want to do this? Change Access Code - Access code will be changed on this card only - All cards in the selected wallet have been reset to factory settings. You can now create a new wallet. + Access code will be changed on this card or ring only + All Tangem devices in the selected wallet have been reset to factory settings. You can now create a new wallet. Reset complete - Do you want to reset the next card in this wallet? - Card reset - We recommend completing the reset process for all cards in this wallet - You haven\'t reset all your cards + Do you want to reset the next device in this wallet? + Wallet reset + We recommend completing the reset process for all Tangem devices in this wallet + You haven\'t reset all your Tangem devices Reset to Factory Settings Security Mode - Card settings + Device settings In addition to network fee, the Cardano network charges %1$s ADA when transacting with the %2$s token Cardano transaction requirements To make a %1$s transaction, you must deposit some ADA to cover the network fee and minimum ADA value (5 ADA recommended) @@ -116,15 +116,17 @@ Market Slow Speed and fee - Get addresses + Get your addresses Go to provider Go to token + hour Import In progress Later %1$s left Locked Main network + month Network fee Amount sent will be reduced by %1$s (%2$s) to cover the selected fee level Next @@ -133,6 +135,7 @@ Now OK Primary card + Primary ring Passphrase Paste Privacy Policy @@ -147,6 +150,7 @@ Save changes Search Search tokens + sec Seed phrase Select action Sell @@ -172,6 +176,7 @@ There was an error. Please try again. Unreachable Unstake + week Yes Contract address copied! Available networks @@ -208,13 +213,13 @@ Access code You will have to submit the correct access code before scanning the card Long Tap - This mechanism protects against proximity attacks on a card. It will enforce a delay between reception and execution of a command. + This mechanism protects against proximity attacks on a card or ring. It will enforce a delay between reception and execution of a command. Passcode Before executing any command entailing a change of the card state, you will have to enter the passcode. Referral program Flip your device screen down to quickly hide and show balances %s hashes - Card ID + Device ID Contact support Link More Cards App Currency @@ -225,7 +230,7 @@ Details Check your internet connection or switch to a different network Terms of service - You have used a card from another wallet. Tap the card associated with this wallet + You have used a card or ring from another wallet. Tap the card or ring associated with this wallet My tokens You haven\'t added any tokens yet. Add tokens via Market to swap Cannot be swapped for %s @@ -282,14 +287,15 @@ Swap any asset in your portfolio for this token The following information is optional. You can erase it if you don\'t want to share it. Tell us what functions you are missing, and we will try to help you. - Please tell us what card do you have + Please tell us what card or ring do you have Hi support team, Please tell us more about your issue. Every small detail can help. My suggestions - Can\'t scan a card + Can\'t scan a card/ring Feedback Tangem feedback Can\'t send a transaction + Coin description error Transaction The network will charge a token approval fee to verify that you are authorizing the use of your token for the swap. Specify the approve limit for the selected token @@ -299,17 +305,18 @@ To continue, grant %1s smart contracts permission to use your %2s Give Permission Unlimited - Order card - Scan card + Order Tangem + Scan Tangem This information was generated with AI - To change the access code tap the card as shown above and do not remove until the end of the operation + To change the access code tap the card or ring as shown above and do not remove until the end of the operation To change the passcode tap the card as shown above and do not remove until the end of the operation To create the wallet tap the card as shown above and do not remove until the end of the operation + To create the wallet tap the ring as shown above and do not remove until the end of the operation Tap the card #%s of the wallet Tap to scan Tap to sign - Tap the card - You have updated biometrics, scan your card to enter + Tap the card or ring + You have updated biometrics, scan your card or ring to enter Your balance should be higher than the fee value to make a transfer Not enough balance You don\'t have enough Mana for this transaction. Please wait until the Mana is refilled. Your Mana balance is %1$s/%2$s @@ -321,7 +328,7 @@ To begin tracking your crypto assets and transactions, add tokens Manage tokens To access all the networks you need to scan the card - Scan your card + Scan your card or ring Enjoy %1$s service fees on swaps via Changelly from February %2$s-%3$s Swap with Changelly, %s fees Tokens @@ -348,14 +355,14 @@ Choose wallet The wallet doesn\'t support more than one network To buy, exchange, or receive this asset, add it to your portfolio - This asset is not available + This asset is currently not supported in the wallet This asset is not available for this wallet Add to portfolio Add Available networks My portfolio Market - To generate addresses for selected networks, you must scan your Tangem Wallet card + To generate addresses for selected networks, you must scan your Tangem Wallet card or ring To add tokens pull this up or tap the search bar This section’s data is sourced from the following networks: %s Unable to load the data… @@ -447,11 +454,11 @@ Pull this up or tap the search bar to add tokens directly from the market Add tokens NFC is not available on your device - You have to set up a single access code to protect all your cards + Set up a single access code to protect all your devices. Protect - You can set up an individual access code on each card later + Set an individual access code for each card or ring later. Personalize - The access code can be restored with a linked card. Don’t keep all cards at one place. + Restore the access code with a linked card or ring. Don`t keep all your devices in one place. Restore Choose any word, phrase, or number you want as your access code Create Access Code @@ -462,24 +469,26 @@ Please repeat the operation. The card will be reset to factory settings. Activation error Add tokens - You\'ve added one backup card. When backup process is finished you can\'t add more backup cards. If you have one more card, add it to backup. Do you like to continue the backup process? + You\'ve added one backup card or ring. When backup process is finished you can\'t add more backup devices. If you have one more card or ring, add it to backup. Do you like to continue the backup process? The backup process is partly complete. You can\'t exit it now. The passphrase is an advanced security feature that crypto wallets use. It adds an extra word or phrase of your own choosing to your already existing recovery phrase to unlock a brand-new set of addresses. - Add a backup card - Scan the card #%d + Add a card or ring + Scan card + Scan card #%d Backup now - Scan the primary card + Scan card + Scan ring Continue to my wallet - Finalize the backup + Finalize backup Receive crypto Scan primary card Skip for later How does it work? - Let\'s generate all the keys on your card and create a secure wallet + Let\'s generate all the keys on your card or ring and create a secure wallet Create wallet Create a wallet Other options - Your keys will be securely generated inside the card. There is no seed phrase, which means nobody can export or steal it. + Your keys will be securely generated inside the chip. There is no seed phrase, which means nobody can export or steal it. Generate keys privately Your card is activated and ready to be used Success! @@ -501,27 +510,30 @@ To import your wallet, enter your seed phrase in the field below Generate seed phrase Import wallet - A seed phrase is a series of words that allows you to recover your wallet. Unlike the keys generated by the card, seed phrases are unprotected and can be copied and stolen. Use this option at your own risk. + A seed phrase is a series of words that allows you to recover your wallet. Unlike the keys generated by the card or ring, seed phrases are unprotected and can be copied and stolen. Use this option at your own risk. Use seed phrase Invalid seed phrase. Please check the word order. Invalid seed phrase. Please check your spelling. Legacy To check whether you’ve written down your seed phrase correctly, please enter the 2nd, 7th and 11th words So, let’s check - To start the backup process add up to two backup cards. - You can add one more card or finalize the backup process + To start the backup process add up to two backup cards or rings. + You can add one more card or ring or finalize the backup process Prepare the backup card with number %s - Scan the primary card to start the backup process. + Scan the primary card or ring to start the backup process. Prepare the primary card with number %s - Your wallet card is configured and ready for use. - Max number of cards added. Finalize the backup process. + Prepare the ring and press the scan button below + Your wallet is configured and ready for use. + Max number of devices added. Finalize the backup process. Activating wallet + Backup card Backup card #%d - No backup cards + Backup ring + No backup devices Notifications - One backup card added + One backup device added Prepare your card - Two backup cards added + Two backup devices added To get started, simply top up the wallet with any amount To get started, simply top up the wallet with more than %1$s %2$s Buy crypto @@ -529,7 +541,7 @@ Activate a wallet The twinning process is partly complete. You can\'t exit it now. If the process of creating the wallet gets interrupted in any way, you\'ll have to start over - You can backup your keys up to two other blank Tangem Wallet cards. + You can backup your keys up to two other blank Tangem Wallet cards or rings. Access code can be restored with one of backup cards. All the backup cards can be used as full-functional with the identical keys. You will be able to set an access code to protect your wallets. @@ -580,11 +592,13 @@ Reset the card I understand that after performing this action, I will no longer have access to the current wallet I realize that I can\'t use this card to recover my access code on the other cards of the current wallet - Factory Reset will completely delete the wallet from the selected card. You will not be able to restore the current wallet or use the card to recover the access code. - Factory Reset will completely delete the wallet from the selected card and remove it from the app. You will not be able to restore the current wallet. + Factory Reset will completely delete the wallet from the selected card or ring. You will not be able to restore the current wallet or use the card or ring to recover the access code. + Factory Reset will completely delete the wallet from the selected card or ring and remove it from the app. You will not be able to restore the current wallet. + Ring owners get 3 commission-free swaps on Changelly until 15.11! + Swap With 0%% Fees Now! Do you have a bank card from another country and a residence permit or registration outside the Russian Federation? Russian bank cards are not currently accepted - Log into the app and check your balance without scanning the card + Log into the app and check your balance without scanning the card or ring Access the app Allow to use biometrics Biometrics will be requested instead of the access code for interactions with your wallet @@ -592,10 +606,10 @@ It looks like you have biometric authentication disabled, it is necessary to save wallets Enable biometric authorization Would you like to use biometrics? - Note that making a transaction with your funds will still require your card - Scan card - Scan the card to change its settings. The changes will impact only the card you\'ve scanned and will not affect other cards tied to your wallet. - Get your card ready! + Note that making a transaction with your funds will still require your card or ring + Scan card or ring + Scan the card or ring to change its settings. The changes will impact only the card or ring you\'ve scanned and will not affect other devices tied to your wallet. + Get your Tangem ready! Already included in the entered address Your commission amount is %s times higher than the recommended amount. Please review and adjust your custom settings. You specified a commission below the recommended amount, which could cause a delay in your transaction. Continue? @@ -671,7 +685,7 @@ Transaction has been successfully signed and sent to the blockchain node. Wallet balance will be updated in a while %1$s is an asset in the Tron network. To calculate the fee and make a transaction you need to deposit some Tron (TRX) in your account. Transaction sent - Prepare to scan card you want to setup. + Prepare to scan card or ring you want to set up. Forget wallet This will remove the wallet from the application. The wallet itself can be added again. Name @@ -706,6 +720,7 @@ Locked Migrate Native staking + No active validators available for staking at the moment. Please try again later. Earned rewards will be sent to your wallet and available for use immediately Stake securely and start earning your rewards Stake securely and start earning daily rewards @@ -738,8 +753,6 @@ Daily Each %s Each day - Each %1$s days - Each %1$s sec Per Epoch Per Era Hourly @@ -748,7 +761,7 @@ Rewards Stake locked Stake more - You stake %1$s and will be receiving %2$s yearly + You stake %1$s and will be receiving your reward each %2$s Tap to unlock Tap to unlock or vote Tap to withdraw @@ -768,13 +781,13 @@ Vote Withdraw Your stakes - Store your crypto assets secure while keeping private keys contained in your card + Store your crypto assets secure while keeping private keys contained in your card or ring Revolutionary Hardware Wallet - Up to 3 physical cards to one wallet + Up to 3 physical cards or rings to one wallet Ultra Secure Backup - A hardware wallet for your Bitcoin, Ethereum and many more currencies simultaneously – all in one card + A hardware wallet for your Bitcoin, Ethereum and many more currencies simultaneously – all in one card or ring Thousands of Currencies - Use it on the go, anywhere, anytime. No wires or batteries. Just tap the card to your phone when you need your crypto. + Use it on the go, anywhere, anytime. No wires or batteries. Just tap the card or ring to your phone when you need your crypto. The Wallet for Everyone Meet Tangem Exchange, buy NFT\'s, make loans and deposits in more than 100 different decentralized services @@ -835,20 +848,20 @@ You\'ve scanned wrong twin card. Please try another one This one that you are holding in your hands and the other one with number %s.\n\nBoth cards can be used to extract funds from this wallet. One wallet. Two cards. - Scan the card #%s + Scan card #%s Creating wallet Scan the #%s twin card Preparing card Tangem Twin This action is irreversible. You will not have access to the old wallet. Tap the twin card with number %s and do not remove until the end of the operation - Use %s or scan a card to have an access to your wallet + Use %s or scan a card/ring to have access to your wallet Stay up to date with the latest features and news Be the first to know about new promotions Would you like to use\nPush-notifications? Add new wallet Are you sure you want to delete this wallet? - An error has occurred, please scan your card to log in + An error has occurred, please scan your card or ring to log in This wallet has already been saved, you can add another one The wallet with name %s already exists Wallet name @@ -856,7 +869,7 @@ Unlock all Unlock all with %s Blockchain is unreachable. Try later - Scan the card + Scan card or ring Requesting to sign a message.\n\n%s Dapp %1$s, requesting to\nsign BNB transaction.\n\n%2$s Trade order for %1$s\nPrice: %2$s\nAmount to receive: %3$s\nAmount to pay: %4$s @@ -871,7 +884,7 @@ Session request contains unsupported blockchains for WalletConnect connection. Unsupported blockchains:\n Connection with this Dapp cannot be established due to its technical implementation. We\'ve encountered unknown error. Error message: %s. If the problem persists — feel free to contact our support - Wrong card selected in Tangem App + Wrong card or ring selected in Tangem App Failed to create transaction from Dapp data. Code: %s We\'ve encountered unknown error. Error code: %d. If the problem persists — feel free to contact our support No opened WalletConnect sessions @@ -896,10 +909,10 @@ No internet connection Wallet settings Tangem - Use %s or scan a card to unlock access to your wallet + Use %s or scan a card/ring to unlock access to your wallet The permission-granting process is currently underway and will be completed shortly Approval in Progress - It seems that the card activation was not completed correctly. This could be due to an issue with your device\'s NFC module or incorrect tapping of the card to your device. Please contact our Support team for assistance. + It seems that the card or ring activation was not completed correctly. This could be due to an issue with your device\'s NFC module or incorrect tapping of the card or ring to your device. Please contact our Support team for assistance. Activation error According to BNB network developers, support for the BEP-2 standard will end in June 2024. To avoid losing assets with this standard, please convert them to the BEP-20 standard. Use our swap service or third-party services to transfer funds to the BNB Smart Chain network. BNB Beacon Chain will shut down @@ -945,8 +958,8 @@ MATIC is being migrated to POL. However there is no deadline set and MATIC isn\'t being deprecated yet. You can safely continue using MATIC token or use exchanges to swap it for POL. MATIC to POL Migration - Use your card to get an address for %d network - Use your card to get an addresses for %d networks + Use your card or ring to get an address for %d network + Use your card or ring to get an addresses for %d networks Some addresses are missing The network is currently unreachable. Please try again later. @@ -973,11 +986,11 @@ You have an interrupted backup. Do you want to resume? Yes, resume Discard - If you will discard the backup now, then you will have to reset the cards to factory settings to start over again + If you discard the backup now, then you will have to reset the devices to factory settings to start over again Resume backup This is an irreversible action Log in with %s - Scan card - Use %s or scan a card to access the app + Scan card or ring + Use %s or scan a card or ring to access the app Welcome back! diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt index 7a77b4cb67..a4c6913d1f 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt @@ -179,14 +179,20 @@ private fun Icon(@DrawableRes iconResId: Int, tint: Color?, modifier: Modifier = } @Composable -private fun TextsBlock(title: TextReference?, subtitle: TextReference, subtitleColor: Color) { - Column { +internal fun TextsBlock( + title: TextReference?, + subtitle: TextReference, + subtitleColor: Color, + modifier: Modifier = Modifier, + titleColor: Color = TangemTheme.colors.text.primary1, +) { + Column(modifier = modifier) { val titleText = title?.resolveReference() if (titleText != null) { Text( text = titleText, - color = TangemTheme.colors.text.primary1, + color = titleColor, style = TangemTheme.typography.button, ) @@ -269,7 +275,12 @@ private fun PairButtons(config: NotificationButtonsState.PairButtonsConfig, isEn } @Composable -private fun CloseableIconButton(onClick: (() -> Unit)?, modifier: Modifier = Modifier, isEnabled: Boolean = true) { +internal fun CloseableIconButton( + onClick: (() -> Unit)?, + modifier: Modifier = Modifier, + isEnabled: Boolean = true, + iconTint: Color = TangemTheme.colors.icon.inactive, +) { AnimatedVisibility(visible = onClick != null, modifier = modifier) { onClick ?: return@AnimatedVisibility @@ -296,7 +307,7 @@ private fun CloseableIconButton(onClick: (() -> Unit)?, modifier: Modifier = Mod modifier = Modifier .size(size = TangemTheme.dimens.size16) .align(alignment = Alignment.Center), - tint = TangemTheme.colors.icon.inactive, + tint = iconTint, ) } } diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/RingPromoNotification.kt b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/RingPromoNotification.kt new file mode 100644 index 0000000000..c40947749d --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/RingPromoNotification.kt @@ -0,0 +1,131 @@ +package com.tangem.core.ui.components.notifications + +import android.content.res.Configuration +import androidx.annotation.DrawableRes +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.layout.FixedScale +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.constraintlayout.compose.ConstraintLayout +import com.tangem.core.ui.R +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.res.TangemColorPalette +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview + +@Composable +fun RingPromoNotification(config: NotificationConfig, modifier: Modifier = Modifier) { + var textHeightDp by remember { mutableStateOf(0.dp) } + + Box( + modifier = modifier + .fillMaxWidth() + .clip(shape = TangemTheme.shapes.roundedCornersXMedium) + .background(TangemColorPalette.Dark6), + ) { + PromoImage( + backgroundRes = config.backgroundResId, + iconRes = config.iconResId, + modifier = Modifier.height(textHeightDp), + ) + PromoText( + title = config.title, + subtitle = config.subtitle, + onSizeChange = { textHeightDp = it }, + ) + CloseableIconButton( + onClick = config.onCloseClick, + modifier = Modifier.align(alignment = Alignment.TopEnd), + iconTint = TangemTheme.colors.icon.informative, + ) + } +} + +@Composable +private fun PromoImage(@DrawableRes backgroundRes: Int?, @DrawableRes iconRes: Int, modifier: Modifier = Modifier) { + if (backgroundRes == null) return + ConstraintLayout( + modifier = modifier, + ) { + val (background, image) = createRefs() + + Image( + painter = painterResource(backgroundRes), + contentDescription = null, + contentScale = FixedScale(2f), + modifier = Modifier + .constrainAs(background) { + top.linkTo(parent.top) + start.linkTo(image.start) + end.linkTo(image.end) + }, + ) + Image( + painter = painterResource(id = iconRes), + contentDescription = null, + contentScale = ContentScale.FillWidth, + modifier = Modifier + .requiredWidth(width = 60.dp) + .wrapContentHeight(Alignment.Top, unbounded = true) + .constrainAs(image) { + top.linkTo(parent.top, 11.dp) + start.linkTo(parent.start, 9.dp) + }, + ) + } +} + +@Composable +private fun PromoText(title: TextReference?, subtitle: TextReference, onSizeChange: (Dp) -> Unit) { + val density = LocalDensity.current + + Box( + modifier = Modifier.onSizeChanged { + with(density) { onSizeChange(it.height.toDp()) } + }, + ) { + TextsBlock( + title = title, + subtitle = subtitle, + subtitleColor = TangemTheme.colors.text.tertiary, + titleColor = TangemTheme.colors.text.constantWhite, + modifier = Modifier + .wrapContentHeight() + .align(Alignment.CenterStart) + .padding(start = 80.dp, top = 12.dp, end = 12.dp, bottom = 12.dp), + ) + } +} + +// region Preview +@Preview(showBackground = true, widthDp = 360) +@Preview(showBackground = true, widthDp = 360, locale = "fr") +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun RingPromoNotification_Preview() { + TangemThemePreview { + RingPromoNotification( + NotificationConfig( + title = resourceReference(id = R.string.ring_promo_title), + subtitle = resourceReference(id = R.string.ring_promo_text), + iconResId = R.drawable.img_card_with_ring, + backgroundResId = R.drawable.img_ring_promo_background, + onCloseClick = { }, + ), + ) + } +} + +// endregion \ No newline at end of file diff --git a/core/ui/src/main/res/drawable/img_card_with_ring.webp b/core/ui/src/main/res/drawable/img_card_with_ring.webp new file mode 100644 index 0000000000..c4c8010355 Binary files /dev/null and b/core/ui/src/main/res/drawable/img_card_with_ring.webp differ diff --git a/core/ui/src/main/res/drawable/img_ring_promo_background.webp b/core/ui/src/main/res/drawable/img_ring_promo_background.webp new file mode 100644 index 0000000000..aa5ae8be39 Binary files /dev/null and b/core/ui/src/main/res/drawable/img_ring_promo_background.webp differ diff --git a/data/card/build.gradle.kts b/data/card/build.gradle.kts index eb8abfb89e..eb80b3d8cf 100644 --- a/data/card/build.gradle.kts +++ b/data/card/build.gradle.kts @@ -26,6 +26,8 @@ dependencies { implementation(projects.core.datasource) implementation(projects.core.utils) + implementation(projects.libs.blockchainSdk) + implementation(projects.domain.card) implementation(projects.domain.models) } \ No newline at end of file diff --git a/data/card/src/main/java/com/tangem/data/card/DefaultCardSdkConfigRepository.kt b/data/card/src/main/java/com/tangem/data/card/DefaultCardSdkConfigRepository.kt index 7e629bb338..1dd6e7c245 100644 --- a/data/card/src/main/java/com/tangem/data/card/DefaultCardSdkConfigRepository.kt +++ b/data/card/src/main/java/com/tangem/data/card/DefaultCardSdkConfigRepository.kt @@ -1,7 +1,8 @@ package com.tangem.data.card import com.tangem.TangemSdk -import com.tangem.blockchain.common.CommonSigner +import com.tangem.blockchain.common.TransactionSigner +import com.tangem.blockchainsdk.signer.TransactionSignerFactory import com.tangem.common.UserCodeType import com.tangem.common.core.CardIdDisplayFormat import com.tangem.common.core.UserCodeRequestPolicy @@ -16,7 +17,10 @@ import com.tangem.domain.models.scan.ProductType * [REDACTED_AUTHOR] */ -internal class DefaultCardSdkConfigRepository(private val cardSdkProvider: CardSdkProvider) : CardSdkConfigRepository { +internal class DefaultCardSdkConfigRepository( + private val cardSdkProvider: CardSdkProvider, + private val transactionSignerFactory: TransactionSignerFactory, +) : CardSdkConfigRepository { @Deprecated("Use CardSdkConfigRepository's methods instead of this property") override val sdk: TangemSdk @@ -53,11 +57,9 @@ internal class DefaultCardSdkConfigRepository(private val cardSdkProvider: CardS } } - override fun getCommonSigner(cardId: String?) = CommonSigner( - tangemSdk = sdk, - cardId = cardId, - initialMessage = null, - ) + override fun getCommonSigner(cardId: String?): TransactionSigner { + return transactionSignerFactory.createTransactionSigner(cardId = cardId, sdk = sdk) + } override fun isLinkedTerminal() = sdk.config.linkedTerminal diff --git a/data/card/src/main/java/com/tangem/data/card/di/CardDataModule.kt b/data/card/src/main/java/com/tangem/data/card/di/CardDataModule.kt index eb53f5dea1..cfdd7984b5 100644 --- a/data/card/src/main/java/com/tangem/data/card/di/CardDataModule.kt +++ b/data/card/src/main/java/com/tangem/data/card/di/CardDataModule.kt @@ -1,5 +1,6 @@ package com.tangem.data.card.di +import com.tangem.blockchainsdk.signer.TransactionSignerFactory import com.tangem.data.card.DefaultCardRepository import com.tangem.data.card.DefaultCardSdkConfigRepository import com.tangem.data.card.sdk.CardSdkProvider @@ -18,8 +19,14 @@ internal object CardDataModule { @Provides @Singleton - fun provideCardSdkConfigRepository(cardSdkProvider: CardSdkProvider): CardSdkConfigRepository { - return DefaultCardSdkConfigRepository(cardSdkProvider = cardSdkProvider) + fun provideCardSdkConfigRepository( + cardSdkProvider: CardSdkProvider, + transactionSignerFactory: TransactionSignerFactory, + ): CardSdkConfigRepository { + return DefaultCardSdkConfigRepository( + cardSdkProvider = cardSdkProvider, + transactionSignerFactory = transactionSignerFactory, + ) } @Provides diff --git a/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt b/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt index 94a184fb73..fbd89738aa 100644 --- a/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt +++ b/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt @@ -41,9 +41,19 @@ internal class DefaultPromoRepository( }.getOrNull() } + override suspend fun getRingPromoBanner(): PromoBanner? { + return runCatching(dispatchers.io) { + promoResponseConverter.convert( + tangemApi.getPromotionInfo(RING) + .getOrThrow(), + ) + }.getOrNull() + } + private companion object { private const val CHANGELLY_NAME = "changelly" private const val TRAVALA = "travala" private const val OKX = "okx" + private const val RING = "ring" } } \ No newline at end of file diff --git a/data/settings/build.gradle.kts b/data/settings/build.gradle.kts index 96f4640242..4b86273708 100644 --- a/data/settings/build.gradle.kts +++ b/data/settings/build.gradle.kts @@ -17,6 +17,7 @@ dependencies { implementation(projects.domain.balanceHiding.models) implementation(projects.domain.settings) + implementation(projects.domain.wallets.models) implementation(deps.androidx.datastore) diff --git a/data/settings/src/main/java/com/tangem/data/settings/DefaultPromoSettingsRepository.kt b/data/settings/src/main/java/com/tangem/data/settings/DefaultPromoSettingsRepository.kt index ce6d3fa236..ecd647ae73 100644 --- a/data/settings/src/main/java/com/tangem/data/settings/DefaultPromoSettingsRepository.kt +++ b/data/settings/src/main/java/com/tangem/data/settings/DefaultPromoSettingsRepository.kt @@ -1,13 +1,18 @@ package com.tangem.data.settings import com.tangem.datasource.local.preferences.AppPreferencesStore +import com.tangem.datasource.local.preferences.PreferencesKeys.ADDED_WALLETS_WITH_RING_KEY import com.tangem.datasource.local.preferences.PreferencesKeys.IS_TOKEN_SWAP_PROMO_OKX_SHOW_KEY import com.tangem.datasource.local.preferences.PreferencesKeys.IS_WALLET_SWAP_PROMO_OKX_SHOW_KEY import com.tangem.datasource.local.preferences.PreferencesKeys.IS_WALLET_TRAVALA_PROMO_SHOWN_KEY +import com.tangem.datasource.local.preferences.PreferencesKeys.SHOULD_SHOW_RING_PROMO_KEY import com.tangem.datasource.local.preferences.utils.get import com.tangem.datasource.local.preferences.utils.store import com.tangem.domain.settings.repositories.PromoSettingsRepository +import com.tangem.domain.wallets.models.UserWalletId import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.map /** * Repository for showing swap promo notification. @@ -47,4 +52,18 @@ class DefaultPromoSettingsRepository( value = false, ) } + + override fun isReadyToShowRingPromo(userWalletId: UserWalletId): Flow { + return combine( + flow = appPreferencesStore + .get(key = ADDED_WALLETS_WITH_RING_KEY, default = emptySet()) + .map { userWalletId.stringValue in it }, + flow2 = appPreferencesStore.get(key = SHOULD_SHOW_RING_PROMO_KEY, default = true), + transform = { isRingAdded, shouldShowRingPromo -> isRingAdded && shouldShowRingPromo }, + ) + } + + override suspend fun setNeverToShowRingPromo() { + appPreferencesStore.store(key = SHOULD_SHOW_RING_PROMO_KEY, value = false) + } } \ No newline at end of file diff --git a/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt b/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt index b18a5b0fa4..8a4472cf71 100644 --- a/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt +++ b/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt @@ -146,7 +146,7 @@ internal class DefaultTransactionRepository( override suspend fun sendTransaction( txData: TransactionData, - signer: CommonSigner, + signer: TransactionSigner, userWalletId: UserWalletId, network: Network, ) = withContext(coroutineDispatcherProvider.io) { @@ -161,7 +161,7 @@ internal class DefaultTransactionRepository( override suspend fun sendMultipleTransactions( txsData: List, - signer: CommonSigner, + signer: TransactionSigner, userWalletId: UserWalletId, network: Network, ) = withContext(coroutineDispatcherProvider.io) { diff --git a/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt b/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt index 2cc72cd098..b7102a6b52 100644 --- a/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt +++ b/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt @@ -5,6 +5,7 @@ import com.tangem.datasource.local.preferences.PreferencesKeys import com.tangem.datasource.local.preferences.utils.get import com.tangem.datasource.local.preferences.utils.getSyncOrDefault import com.tangem.datasource.local.preferences.utils.store +import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.repository.WalletsRepository import kotlinx.coroutines.flow.Flow @@ -23,4 +24,18 @@ internal class DefaultWalletsRepository( override suspend fun saveShouldSaveUserWallets(item: Boolean) { appPreferencesStore.store(key = PreferencesKeys.SAVE_USER_WALLETS_KEY, value = item) } + + override suspend fun isWalletWithRing(userWalletId: UserWalletId): Boolean { + return appPreferencesStore + .getSyncOrDefault(key = PreferencesKeys.ADDED_WALLETS_WITH_RING_KEY, default = emptySet()) + .contains(element = userWalletId.stringValue) + } + + override suspend fun setHasWalletsWithRing(userWalletId: UserWalletId) { + appPreferencesStore.editData { + val added = it[PreferencesKeys.ADDED_WALLETS_WITH_RING_KEY].orEmpty() + + it[PreferencesKeys.ADDED_WALLETS_WITH_RING_KEY] = added + userWalletId.stringValue + } + } } \ No newline at end of file diff --git a/domain/card/src/main/kotlin/com/tangem/domain/card/repository/CardSdkConfigRepository.kt b/domain/card/src/main/kotlin/com/tangem/domain/card/repository/CardSdkConfigRepository.kt index 98b2739bec..4803d9bff3 100644 --- a/domain/card/src/main/kotlin/com/tangem/domain/card/repository/CardSdkConfigRepository.kt +++ b/domain/card/src/main/kotlin/com/tangem/domain/card/repository/CardSdkConfigRepository.kt @@ -1,7 +1,7 @@ package com.tangem.domain.card.repository import com.tangem.TangemSdk -import com.tangem.blockchain.common.CommonSigner +import com.tangem.blockchain.common.TransactionSigner import com.tangem.domain.models.scan.ProductType /** @@ -28,7 +28,7 @@ interface CardSdkConfigRepository { fun updateCardIdDisplayFormat(productType: ProductType) /** Get common signer by [cardId] */ - fun getCommonSigner(cardId: String?): CommonSigner + fun getCommonSigner(cardId: String?): TransactionSigner /** Check if linked terminal is enabled */ fun isLinkedTerminal(): Boolean? diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt index 6d6c0b3a4b..5a531976c7 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt @@ -614,7 +614,7 @@ class DefaultWalletManagersFacade( override suspend fun associateAsset( userWalletId: UserWalletId, currency: CryptoCurrency, - signer: CommonSigner, + signer: TransactionSigner, ): SimpleResult { return withContext(dispatchers.io) { val walletManager = getOrCreateWalletManager(userWalletId = userWalletId, network = currency.network) diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt index 5104db3554..2f6317a316 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt @@ -236,7 +236,7 @@ interface WalletManagersFacade { suspend fun associateAsset( userWalletId: UserWalletId, currency: CryptoCurrency, - signer: CommonSigner, + signer: TransactionSigner, ): SimpleResult /** diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/scan/CardDTO.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/scan/CardDTO.kt index fcce746f21..706e05a1e5 100644 --- a/domain/models/src/main/kotlin/com/tangem/domain/models/scan/CardDTO.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/scan/CardDTO.kt @@ -323,5 +323,6 @@ data class CardDTO( companion object { val RING_BATCH_IDS = listOf("AC17", "BA01") + const val RING_BATCH_PREFIX = "BA" } } \ No newline at end of file diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/scan/RingExt.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/scan/RingExt.kt new file mode 100644 index 0000000000..d2ce46707e --- /dev/null +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/scan/RingExt.kt @@ -0,0 +1,8 @@ +package com.tangem.domain.models.scan + +import com.tangem.domain.models.scan.CardDTO.Companion.RING_BATCH_IDS +import com.tangem.domain.models.scan.CardDTO.Companion.RING_BATCH_PREFIX + +fun isRing(batchId: String): Boolean { + return RING_BATCH_IDS.contains(batchId) || batchId.startsWith(RING_BATCH_PREFIX) +} \ No newline at end of file diff --git a/domain/settings/build.gradle.kts b/domain/settings/build.gradle.kts index 0c8b0de6e1..7b8e6bc2ad 100644 --- a/domain/settings/build.gradle.kts +++ b/domain/settings/build.gradle.kts @@ -7,4 +7,5 @@ dependencies { implementation(deps.kotlin.coroutines) implementation(deps.arrow.core) implementation(projects.domain.balanceHiding.models) + implementation(projects.domain.wallets.models) } \ No newline at end of file diff --git a/domain/settings/src/main/java/com/tangem/domain/settings/ShouldShowRingPromoUseCase.kt b/domain/settings/src/main/java/com/tangem/domain/settings/ShouldShowRingPromoUseCase.kt new file mode 100644 index 0000000000..67d9174e78 --- /dev/null +++ b/domain/settings/src/main/java/com/tangem/domain/settings/ShouldShowRingPromoUseCase.kt @@ -0,0 +1,14 @@ +package com.tangem.domain.settings + +import com.tangem.domain.settings.repositories.PromoSettingsRepository +import com.tangem.domain.wallets.models.UserWalletId +import kotlinx.coroutines.flow.Flow + +class ShouldShowRingPromoUseCase(private val promoSettingsRepository: PromoSettingsRepository) { + + operator fun invoke(userWalletId: UserWalletId): Flow { + return promoSettingsRepository.isReadyToShowRingPromo(userWalletId) + } + + suspend fun neverToShow() = promoSettingsRepository.setNeverToShowRingPromo() +} \ No newline at end of file diff --git a/domain/settings/src/main/java/com/tangem/domain/settings/repositories/PromoSettingsRepository.kt b/domain/settings/src/main/java/com/tangem/domain/settings/repositories/PromoSettingsRepository.kt index 0e3946bb89..758763828a 100644 --- a/domain/settings/src/main/java/com/tangem/domain/settings/repositories/PromoSettingsRepository.kt +++ b/domain/settings/src/main/java/com/tangem/domain/settings/repositories/PromoSettingsRepository.kt @@ -1,5 +1,6 @@ package com.tangem.domain.settings.repositories +import com.tangem.domain.wallets.models.UserWalletId import kotlinx.coroutines.flow.Flow interface PromoSettingsRepository { @@ -14,4 +15,8 @@ interface PromoSettingsRepository { fun isReadyToShowWalletTravalaPromo(): Flow suspend fun setNeverToShowWalletTravalaPromo() + + fun isReadyToShowRingPromo(userWalletId: UserWalletId): Flow + + suspend fun setNeverToShowRingPromo() } \ No newline at end of file diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/InvalidatePendingTransactionsUseCase.kt b/domain/staking/src/main/java/com/tangem/domain/staking/InvalidatePendingTransactionsUseCase.kt index 3ed9de1336..bdc4d9565f 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/InvalidatePendingTransactionsUseCase.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/InvalidatePendingTransactionsUseCase.kt @@ -9,6 +9,7 @@ import com.tangem.domain.staking.repositories.StakingErrorResolver import com.tangem.domain.staking.repositories.StakingPendingTransactionRepository import com.tangem.domain.wallets.models.UserWalletId import org.joda.time.DateTime +import java.math.BigDecimal import java.util.UUID class InvalidatePendingTransactionsUseCase( @@ -41,12 +42,13 @@ class InvalidatePendingTransactionsUseCase( newBalancesId: Int, pendingData: List>, ): Pair, List> { - val balances = realData.associateBy { Pair(it.groupId to it.type, it.amount to it.date) }.toMutableMap() + val balances = realData.associateBy { BalanceIdentity(it.groupId, it.type, it.amount, it.date) } + .toMutableMap() val transactionsToRemove = mutableListOf() pendingData.forEach { (pendingTransaction, balanceItem) -> - val key = Pair(balanceItem.groupId to balanceItem.type, balanceItem.amount to balanceItem.date) + val key = BalanceIdentity(balanceItem.groupId, balanceItem.type, balanceItem.amount, balanceItem.date) val oldBalancesId = pendingTransaction.balancesId when { @@ -59,20 +61,36 @@ class InvalidatePendingTransactionsUseCase( else -> { val groupId = UUID.randomUUID().toString() val now = DateTime.now() - balances[Pair(groupId to BalanceType.STAKED, pendingTransaction.amount to now)] = BalanceItem( - groupId = groupId, - type = pendingTransaction.type, - amount = pendingTransaction.amount, - rawCurrencyId = pendingTransaction.rawCurrencyId, - validatorAddress = pendingTransaction.validator?.address, - date = null, - pendingActions = emptyList(), - isPending = true, - ) + balances[BalanceIdentity(groupId, BalanceType.STAKED, pendingTransaction.amount, now)] = + BalanceItem( + groupId = groupId, + type = pendingTransaction.type, + amount = pendingTransaction.amount, + rawCurrencyId = pendingTransaction.rawCurrencyId, + validatorAddress = pendingTransaction.validator?.address, + date = null, + pendingActions = emptyList(), + isPending = true, + ) + + balances.entries.find { + it.key.type == BalanceType.STAKED && + it.key.amount == pendingTransaction.amount && + !it.value.isPending + } + ?.key + ?.let { balances.remove(it) } } } } return balances.values.toList() to transactionsToRemove } + + private data class BalanceIdentity( + val groupId: String, + val type: BalanceType, + val amount: BigDecimal, + val date: DateTime?, + ) } \ No newline at end of file diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenSwapPromoAnalyticsEvent.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenSwapPromoAnalyticsEvent.kt index 6e48ed4272..6242d8bbc2 100644 --- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenSwapPromoAnalyticsEvent.kt +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenSwapPromoAnalyticsEvent.kt @@ -39,5 +39,6 @@ sealed class TokenSwapPromoAnalyticsEvent( enum class ProgramName { Travala, OKX, + Ring, } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/PromoRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/PromoRepository.kt index 51ba0a57f9..3cd5eacc17 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/PromoRepository.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/PromoRepository.kt @@ -9,4 +9,6 @@ interface PromoRepository { suspend fun getTravalaPromoBanner(): PromoBanner? suspend fun getOkxPromoBanner(): PromoBanner? + + suspend fun getRingPromoBanner(): PromoBanner? } \ No newline at end of file diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/TransactionRepository.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/TransactionRepository.kt index 247f3c7605..975bac4246 100644 --- a/domain/transaction/src/main/java/com/tangem/domain/transaction/TransactionRepository.kt +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/TransactionRepository.kt @@ -1,9 +1,9 @@ package com.tangem.domain.transaction import com.tangem.blockchain.common.Amount -import com.tangem.blockchain.common.CommonSigner import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.TransactionExtras +import com.tangem.blockchain.common.TransactionSigner import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionSendResult import com.tangem.blockchain.common.transaction.TransactionsSendResult @@ -54,14 +54,14 @@ interface TransactionRepository { suspend fun sendTransaction( txData: TransactionData, - signer: CommonSigner, + signer: TransactionSigner, userWalletId: UserWalletId, network: Network, ): com.tangem.blockchain.extensions.Result suspend fun sendMultipleTransactions( txsData: List, - signer: CommonSigner, + signer: TransactionSigner, userWalletId: UserWalletId, network: Network, ): com.tangem.blockchain.extensions.Result diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletsRepository.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletsRepository.kt index 003f9b9ff2..ff8f854078 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletsRepository.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletsRepository.kt @@ -1,5 +1,6 @@ package com.tangem.domain.wallets.repository +import com.tangem.domain.wallets.models.UserWalletId import kotlinx.coroutines.flow.Flow interface WalletsRepository { @@ -9,4 +10,8 @@ interface WalletsRepository { fun shouldSaveUserWallets(): Flow suspend fun saveShouldSaveUserWallets(item: Boolean) + + suspend fun isWalletWithRing(userWalletId: UserWalletId): Boolean + + suspend fun setHasWalletsWithRing(userWalletId: UserWalletId) } \ No newline at end of file diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt index f3a0d6cbe4..92424fa514 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt @@ -38,11 +38,10 @@ import timber.log.Timber import java.io.IOException import java.math.BigDecimal import java.util.UUID -import javax.inject.Inject import com.tangem.datasource.api.express.models.request.LeastTokenInfo as NetworkLeastTokenInfo @Suppress("LongParameterList", "LargeClass") -internal class DefaultSwapRepository @Inject constructor( +internal class DefaultSwapRepository( private val tangemExpressApi: TangemExpressApi, private val coroutineDispatcher: CoroutineDispatcherProvider, private val walletManagersFacade: WalletManagersFacade, @@ -187,6 +186,7 @@ internal class DefaultSwapRepository @Inject constructor( } override suspend fun findBestQuote( + userWalletId: UserWalletId, fromContractAddress: String, fromNetwork: String, toContractAddress: String, diff --git a/features/swap/domain/api/src/main/java/com/tangem/feature/swap/domain/api/SwapRepository.kt b/features/swap/domain/api/src/main/java/com/tangem/feature/swap/domain/api/SwapRepository.kt index 99bc772d79..41dbd1cf70 100644 --- a/features/swap/domain/api/src/main/java/com/tangem/feature/swap/domain/api/SwapRepository.kt +++ b/features/swap/domain/api/src/main/java/com/tangem/feature/swap/domain/api/SwapRepository.kt @@ -18,6 +18,7 @@ interface SwapRepository { @Suppress("LongParameterList") suspend fun findBestQuote( + userWalletId: UserWalletId, fromContractAddress: String, fromNetwork: String, toContractAddress: String, diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index 97ad3eb467..fcd6a988c0 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -318,6 +318,7 @@ internal class SwapInteractorImpl @AssistedInject constructor( isBalanceWithoutFeeEnough: Boolean, ): Pair { val maybeQuotes = repository.findBestQuote( + userWalletId = userWalletId, fromContractAddress = fromToken.currency.getContractAddress(), fromNetwork = fromToken.currency.network.backendId, toContractAddress = toToken.currency.getContractAddress(), @@ -1192,14 +1193,15 @@ internal class SwapInteractorImpl @AssistedInject constructor( } val quotes = repository.findBestQuote( + userWalletId = userWalletId, fromContractAddress = fromToken.getContractAddress(), fromNetwork = fromToken.network.backendId, toContractAddress = toToken.getContractAddress(), toNetwork = toToken.network.backendId, fromAmount = amountToRequest.toStringWithRightOffset(), fromDecimals = amount.decimals, - providerId = provider.providerId, toDecimals = toToken.decimals, + providerId = provider.providerId, rateType = RateType.FLOAT, ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt index 2686a1ec9d..fecbf47463 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt @@ -56,6 +56,10 @@ internal class WalletWarningsAnalyticsSender @Inject constructor( is WalletNotification.Warning.SomeNetworksUnreachable, is WalletNotification.Warning.NetworksUnreachable, -> null + is WalletNotification.RingPromo -> TokenSwapPromoAnalyticsEvent.NoticePromotionBanner( + source = AnalyticsParam.ScreensSources.Main, + programName = TokenSwapPromoAnalyticsEvent.ProgramName.Ring, + ) } } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt index 031c843c16..5ea90e12d4 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt @@ -6,7 +6,7 @@ import com.tangem.domain.core.lce.Lce import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.promo.PromoBanner import com.tangem.domain.settings.IsReadyToShowRateAppUseCase -import com.tangem.domain.settings.ShouldShowSwapPromoWalletUseCase +import com.tangem.domain.settings.ShouldShowRingPromoUseCase import com.tangem.domain.tokens.GetTokenListUseCase import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.model.CryptoCurrency @@ -33,7 +33,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( private val getTokenListUseCase: GetTokenListUseCase, private val isDemoCardUseCase: IsDemoCardUseCase, private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase, - private val shouldShowSwapPromoWalletUseCase: ShouldShowSwapPromoWalletUseCase, + private val shouldShowRingPromoUseCase: ShouldShowRingPromoUseCase, private val promoRepository: PromoRepository, private val isNeedToBackupUseCase: IsNeedToBackupUseCase, private val backupValidator: BackupValidator, @@ -42,16 +42,16 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( fun create(userWallet: UserWallet, clickIntents: WalletClickIntents): Flow> { val cardTypesResolver = userWallet.scanResponse.cardTypesResolver - val promoFlow = flow { emit(promoRepository.getOkxPromoBanner()) } + val promoFlow = flow { emit(promoRepository.getRingPromoBanner()) } return combine( flow = getTokenListUseCase.launch(userWallet.walletId), flow2 = isReadyToShowRateAppUseCase(), flow3 = isNeedToBackupUseCase(userWallet.walletId), - flow4 = shouldShowSwapPromoWalletUseCase(), + flow4 = shouldShowRingPromoUseCase(userWalletId = userWallet.walletId), flow5 = promoFlow, ) { maybeTokenList, isReadyToShowRating, isNeedToBackup, shouldShowPromo, promoBanner -> buildList { - addSwapPromoNotification(shouldShowPromo, promoBanner, clickIntents) + addRingPromoNotification(shouldShowPromo, promoBanner, clickIntents) addCriticalNotifications(userWallet, clickIntents) @@ -70,17 +70,13 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( } } - private fun MutableList.addSwapPromoNotification( + private fun MutableList.addRingPromoNotification( shouldShowPromo: Boolean, promoBanner: PromoBanner?, clickIntents: WalletClickIntents, ) { promoBanner ?: return - val promoNotification = WalletNotification.SwapPromo( - startDateTime = promoBanner.bannerState.timeline.start, - endDateTime = promoBanner.bannerState.timeline.end, - onCloseClick = clickIntents::onCloseSwapPromoClick, - ) + val promoNotification = WalletNotification.RingPromo(onCloseClick = clickIntents::onCloseRingPromoClick) addIf( element = promoNotification, condition = shouldShowPromo && promoBanner.isActive, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt index c878757dbc..2741cc56ca 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt @@ -194,4 +194,14 @@ sealed class WalletNotification(val config: NotificationConfig) { onCloseClick = onCloseClick, ), ) + + data class RingPromo(val onCloseClick: () -> Unit) : WalletNotification( + config = NotificationConfig( + title = resourceReference(id = R.string.ring_promo_title), + subtitle = resourceReference(id = R.string.ring_promo_text), + iconResId = R.drawable.img_card_with_ring, + backgroundResId = R.drawable.img_ring_promo_background, + onCloseClick = onCloseClick, + ), + ) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt index 6ceb82e75c..55ca2d223b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt @@ -1,11 +1,12 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.common import android.content.res.Configuration -import androidx.annotation.DrawableRes import androidx.annotation.StringRes -import androidx.compose.animation.* +import androidx.compose.animation.AnimatedContent import androidx.compose.animation.core.tween -import androidx.compose.foundation.Image +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.togetherWith import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.background import androidx.compose.foundation.gestures.detectTapGestures @@ -25,11 +26,9 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalHapticFeedback -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview @@ -55,7 +54,7 @@ import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.common.WalletPreviewData import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState -private const val HALF_OF_ITEM_WIDTH = 0.5 +// private const val HALF_OF_ITEM_WIDTH = 0.5 /** * Wallet card @@ -117,31 +116,33 @@ internal fun WalletCard(state: WalletCardState, isBalanceHidden: Boolean, modifi bottom.linkTo(anchor = parent.bottom, margin = contentVerticalMargin) if (additionalText != null) { - width = if (state.imageResId != null) { - end.linkTo(imageRef.start) - Dimension.fillToConstraints - } else { - Dimension.wrapContent - } + width = Dimension.wrapContent + // TODO: [REDACTED_JIRA] + // width = if (state.imageResId != null) { + // end.linkTo(imageRef.start) + // Dimension.fillToConstraints + // } else { + // Dimension.wrapContent + // } } }, ) + // TODO: [REDACTED_JIRA] // If balance has a large width then image must be hidden - val hasSpaceForImage by remember(key1 = balanceWidth, key2 = itemSize.width) { - mutableStateOf(value = balanceWidth < itemSize.width * HALF_OF_ITEM_WIDTH) - } - - if (hasSpaceForImage) { - Image( - id = state.imageResId, - modifier = Modifier.constrainAs(imageRef) { - end.linkTo(parent.end) - bottom.linkTo(parent.bottom) - height = Dimension.fillToConstraints - }, - ) - } + // val hasSpaceForImage by remember(key1 = balanceWidth, key2 = itemSize.width) { + // mutableStateOf(value = balanceWidth < itemSize.width * HALF_OF_ITEM_WIDTH) + // } + // if (hasSpaceForImage) { + // Image( + // id = state.imageResId, + // modifier = Modifier.constrainAs(imageRef) { + // end.linkTo(parent.end) + // bottom.linkTo(parent.bottom) + // height = Dimension.fillToConstraints + // }, + // ) + // } } } @@ -370,19 +371,19 @@ private fun LockedContent(modifier: Modifier = Modifier) { ) } -@Composable -private fun Image(@DrawableRes id: Int?, modifier: Modifier = Modifier) { - AnimatedVisibility(visible = id != null, modifier = modifier) { - val imageRes = id ?: return@AnimatedVisibility - - Image( - painter = painterResource(id = imageRes), - contentDescription = null, - modifier = Modifier.width(width = TangemTheme.dimens.size120), - contentScale = ContentScale.FillWidth, - ) - } -} +// @Composable +// private fun Image(@DrawableRes id: Int?, modifier: Modifier = Modifier) { +// AnimatedVisibility(visible = id != null, modifier = modifier) { +// val imageRes = id ?: return@AnimatedVisibility +// +// Image( +// painter = painterResource(id = imageRes), +// contentDescription = null, +// modifier = Modifier.width(width = TangemTheme.dimens.size120), +// contentScale = ContentScale.FillWidth, +// ) +// } +// } // region Preview diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt index b27ba3f5e5..39dbbb2618 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.lazy.items import androidx.compose.ui.Modifier import com.tangem.core.ui.components.notifications.Notification import com.tangem.core.ui.components.notifications.OkxPromoNotification +import com.tangem.core.ui.components.notifications.RingPromoNotification import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification import kotlinx.collections.immutable.ImmutableList @@ -33,6 +34,9 @@ internal fun LazyListScope.notifications(configs: ImmutableList { + RingPromoNotification(config = it.config, modifier = modifier.animateItemPlacement()) + } else -> { Notification( config = it.config, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt index 6798d988ff..385874122d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt @@ -8,10 +8,7 @@ import com.tangem.domain.card.DerivePublicKeysUseCase import com.tangem.domain.card.SetCardWasScannedUseCase import com.tangem.domain.redux.LegacyAction import com.tangem.domain.redux.ReduxStateHolder -import com.tangem.domain.settings.NeverToSuggestRateAppUseCase -import com.tangem.domain.settings.RemindToRateAppLaterUseCase -import com.tangem.domain.settings.ShouldShowSwapPromoWalletUseCase -import com.tangem.domain.settings.ShouldShowTravalaPromoWalletUseCase +import com.tangem.domain.settings.* import com.tangem.domain.tokens.FetchTokenListUseCase import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.analytics.TokenSwapPromoAnalyticsEvent @@ -59,6 +56,8 @@ internal interface WalletWarningsClickIntents { fun onCloseSwapPromoClick() + fun onCloseRingPromoClick() + fun onTravalaPromoClick(link: String?) fun onCloseTravalaPromoClick() @@ -83,6 +82,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor( private val reduxStateHolder: ReduxStateHolder, private val dispatchers: CoroutineDispatcherProvider, private val shouldShowSwapPromoWalletUseCase: ShouldShowSwapPromoWalletUseCase, + private val shouldShowRingPromoUseCase: ShouldShowRingPromoUseCase, private val shouldShowTravalaPromoWalletUseCase: ShouldShowTravalaPromoWalletUseCase, ) : BaseWalletClickIntents(), WalletWarningsClickIntents { @@ -239,6 +239,20 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor( } } + override fun onCloseRingPromoClick() { + analyticsEventHandler.send( + TokenSwapPromoAnalyticsEvent.PromotionBannerClicked( + source = AnalyticsParam.ScreensSources.Main, + programName = TokenSwapPromoAnalyticsEvent.ProgramName.Ring, + action = TokenSwapPromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Closed, + ), + ) + + viewModelScope.launch { + shouldShowRingPromoUseCase.neverToShow() + } + } + override fun onTravalaPromoClick(link: String?) { analyticsEventHandler.send( TokenSwapPromoAnalyticsEvent.PromotionBannerClicked( diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index e029565698..0b8a79c023 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -91,7 +91,7 @@ markdownComposeView = "0.5.4" # region Tangem tangemBlockchainSdk = "release-app_5.16-813" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "release-app_5.16-386" +tangemCardSdk = "release-app_5.16-390" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ tangemVico = "2.0.0-alpha.25-tangem17" #tangemVico = "0.0.1" # Keep it! - used for local builds ^ diff --git a/libs/auth/src/main/java/com/tangem/lib/auth/ExpressAuthProvider.kt b/libs/auth/src/main/java/com/tangem/lib/auth/ExpressAuthProvider.kt index 8582fd21ed..ebca655e53 100644 --- a/libs/auth/src/main/java/com/tangem/lib/auth/ExpressAuthProvider.kt +++ b/libs/auth/src/main/java/com/tangem/lib/auth/ExpressAuthProvider.kt @@ -5,4 +5,6 @@ interface ExpressAuthProvider { fun getUserId(): String fun getSessionId(): String + + fun getRefCode(): String } \ No newline at end of file diff --git a/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/signer/TransactionSignerFactory.kt b/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/signer/TransactionSignerFactory.kt new file mode 100644 index 0000000000..da53a4d024 --- /dev/null +++ b/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/signer/TransactionSignerFactory.kt @@ -0,0 +1,12 @@ +package com.tangem.blockchainsdk.signer + +import com.tangem.TangemSdk +import com.tangem.blockchain.common.TransactionSigner + +/** +[REDACTED_AUTHOR] + */ +interface TransactionSignerFactory { + + fun createTransactionSigner(cardId: String?, sdk: TangemSdk): TransactionSigner +} \ No newline at end of file