Updated on 2026-08-14
This commit is contained in:
commit
2f84def7cc
1244 changed files with 20573 additions and 7307 deletions
|
|
@ -83,6 +83,7 @@ sealed class AnalyticsParam {
|
|||
data object Onboarding : ScreensSources("Onboarding")
|
||||
data object LongTap : ScreensSources("Long Tap")
|
||||
data object Markets : ScreensSources("Markets")
|
||||
data object HotWallet : ScreensSources("Hot Wallet")
|
||||
}
|
||||
|
||||
sealed class TxSentFrom(val value: String) {
|
||||
|
|
|
|||
|
|
@ -3,10 +3,6 @@
|
|||
"name": "NEW_CARD_SCANNING_ENABLED",
|
||||
"version": "undefined"
|
||||
},
|
||||
{
|
||||
"name": "ONRAMP_ENABLED",
|
||||
"version": "5.24.2"
|
||||
},
|
||||
{
|
||||
"name": "VISA_ONBOARDING_ENABLED",
|
||||
"version": "undefined"
|
||||
|
|
@ -15,18 +11,6 @@
|
|||
"name": "STAKING_TON_ENABLED",
|
||||
"version": "undefined"
|
||||
},
|
||||
{
|
||||
"name": "NFT_ENABLED",
|
||||
"version": "5.25.0"
|
||||
},
|
||||
{
|
||||
"name": "NFT_EVM_ENABLED",
|
||||
"version": "5.25.0"
|
||||
},
|
||||
{
|
||||
"name": "NFT_SOLANA_ENABLED",
|
||||
"version": "5.25.0"
|
||||
},
|
||||
{
|
||||
"name": "NFT_MEDIA_CONTENT_ENABLED",
|
||||
"version": "undefined"
|
||||
|
|
@ -35,26 +19,10 @@
|
|||
"name": "STAKING_CARDANO_ENABLED",
|
||||
"version": "undefined"
|
||||
},
|
||||
{
|
||||
"name": "NEW_ARTWORK_LOADING",
|
||||
"version": "5.25.0"
|
||||
},
|
||||
{
|
||||
"name": "NEW_ATTESTATION_ENABLED",
|
||||
"version": "5.24.0"
|
||||
},
|
||||
{
|
||||
"name": "WALLET_CONNECT_REDESIGN_ENABLED",
|
||||
"version": "undefined"
|
||||
},
|
||||
{
|
||||
"name": "STAKING_LOADING_REFACTORING_ENABLED",
|
||||
"version": "5.25.0"
|
||||
},
|
||||
{
|
||||
"name": "DEEPLINK_NAVIGATION_ENABLED",
|
||||
"version": "5.25.0"
|
||||
},
|
||||
{
|
||||
"name": "PUSH_NOTIFICATIONS_ENABLED",
|
||||
"version": "5.26.2"
|
||||
|
|
@ -77,6 +45,10 @@
|
|||
},
|
||||
{
|
||||
"name": "WALLET_BALANCE_FETCHER_ENABLED",
|
||||
"version": "5.27.0"
|
||||
},
|
||||
{
|
||||
"name": "HOT_WALLET_ENABLED",
|
||||
"version": "undefined"
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -92,4 +92,5 @@ dependencies {
|
|||
testRuntimeOnly(deps.test.junit5.engine)
|
||||
testImplementation(deps.test.mockk)
|
||||
testImplementation(deps.test.truth)
|
||||
testImplementation(projects.common.test)
|
||||
}
|
||||
|
|
@ -25,7 +25,6 @@ sealed class ApiConfig {
|
|||
TangemTech,
|
||||
StakeKit,
|
||||
TangemPay,
|
||||
Attestation,
|
||||
BlockAid,
|
||||
}
|
||||
|
||||
|
|
@ -35,7 +34,6 @@ sealed class ApiConfig {
|
|||
is TangemTech -> ID.TangemTech
|
||||
is StakeKit -> ID.StakeKit
|
||||
is TangemPay -> ID.TangemPay
|
||||
is Attestation -> ID.Attestation
|
||||
is BlockAid -> ID.BlockAid
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,9 +13,15 @@ enum class ApiEnvironment {
|
|||
@Json(name = "DEV")
|
||||
DEV,
|
||||
|
||||
@Json(name = "DEV_2")
|
||||
DEV_2,
|
||||
|
||||
@Json(name = "STAGE")
|
||||
STAGE,
|
||||
|
||||
@Json(name = "MOCK")
|
||||
MOCK,
|
||||
|
||||
@Json(name = "PROD")
|
||||
PROD,
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
package com.tangem.datasource.api.common.config
|
||||
|
||||
/**
|
||||
* Attestation [ApiConfig] that used in CardSDK
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class Attestation : ApiConfig() {
|
||||
|
||||
override val defaultEnvironment: ApiEnvironment = ApiEnvironment.PROD
|
||||
|
||||
override val environmentConfigs: List<ApiEnvironmentConfig> = listOf(
|
||||
createDevEnvironment(),
|
||||
createProdEnvironment(),
|
||||
)
|
||||
|
||||
private fun createDevEnvironment(): ApiEnvironmentConfig = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.DEV,
|
||||
baseUrl = "[REDACTED_ENV_URL]",
|
||||
)
|
||||
|
||||
private fun createProdEnvironment(): ApiEnvironmentConfig = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.PROD,
|
||||
baseUrl = "https://api.tangem-tech.com/",
|
||||
)
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ import com.tangem.utils.version.AppVersionProvider
|
|||
* @property environmentConfigStorage environment config storage
|
||||
* @property expressAuthProvider express auth provider
|
||||
* @property appVersionProvider app version provider
|
||||
* @property appInfoProvider app info provider
|
||||
*/
|
||||
internal class Express(
|
||||
private val environmentConfigStorage: EnvironmentConfigStorage,
|
||||
|
|
@ -26,22 +27,51 @@ internal class Express(
|
|||
|
||||
override val environmentConfigs: List<ApiEnvironmentConfig> = listOf(
|
||||
createDevEnvironment(),
|
||||
createDev2Environment(),
|
||||
createStageEnvironment(),
|
||||
createMockedEnvironment(),
|
||||
createProdEnvironment(),
|
||||
)
|
||||
|
||||
private fun getInitialEnvironment(): ApiEnvironment {
|
||||
return when (BuildConfig.BUILD_TYPE) {
|
||||
DEBUG_BUILD_TYPE,
|
||||
-> ApiEnvironment.DEV
|
||||
INTERNAL_BUILD_TYPE,
|
||||
-> ApiEnvironment.STAGE
|
||||
MOCKED_BUILD_TYPE,
|
||||
-> ApiEnvironment.MOCK
|
||||
EXTERNAL_BUILD_TYPE,
|
||||
RELEASE_BUILD_TYPE,
|
||||
-> ApiEnvironment.PROD
|
||||
else -> error("Unknown build type [${BuildConfig.BUILD_TYPE}]")
|
||||
}
|
||||
}
|
||||
|
||||
private fun createDevEnvironment(): ApiEnvironmentConfig = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.DEV,
|
||||
baseUrl = "[REDACTED_ENV_URL]",
|
||||
headers = createHeaders(isProd = false),
|
||||
)
|
||||
|
||||
private fun createDev2Environment(): ApiEnvironmentConfig = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.DEV_2,
|
||||
baseUrl = "[REDACTED_ENV_URL]",
|
||||
headers = createHeaders(isProd = false),
|
||||
)
|
||||
|
||||
private fun createStageEnvironment(): ApiEnvironmentConfig = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.STAGE,
|
||||
baseUrl = "[REDACTED_ENV_URL]",
|
||||
headers = createHeaders(isProd = false),
|
||||
)
|
||||
|
||||
private fun createMockedEnvironment(): ApiEnvironmentConfig = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.MOCK,
|
||||
baseUrl = "[REDACTED_ENV_URL]",
|
||||
headers = createHeaders(isProd = false),
|
||||
)
|
||||
|
||||
private fun createProdEnvironment(): ApiEnvironmentConfig = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.PROD,
|
||||
baseUrl = "https://express.tangem.com/v1/",
|
||||
|
|
@ -63,21 +93,4 @@ internal class Express(
|
|||
?.apiKey
|
||||
?: error("No express config provided")
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
fun getInitialEnvironment(): ApiEnvironment {
|
||||
return when (BuildConfig.BUILD_TYPE) {
|
||||
DEBUG_BUILD_TYPE,
|
||||
-> ApiEnvironment.DEV
|
||||
INTERNAL_BUILD_TYPE,
|
||||
MOCKED_BUILD_TYPE,
|
||||
-> ApiEnvironment.STAGE
|
||||
EXTERNAL_BUILD_TYPE,
|
||||
RELEASE_BUILD_TYPE,
|
||||
-> ApiEnvironment.PROD
|
||||
else -> error("Unknown build type [${BuildConfig.BUILD_TYPE}]")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.datasource.api.common.config
|
||||
|
||||
import com.tangem.datasource.BuildConfig
|
||||
import com.tangem.utils.ProviderSuspend
|
||||
import com.tangem.utils.version.AppVersionProvider
|
||||
|
||||
|
|
@ -7,11 +8,37 @@ internal class TangemPay(
|
|||
private val appVersionProvider: AppVersionProvider,
|
||||
) : ApiConfig() {
|
||||
|
||||
override val defaultEnvironment: ApiEnvironment = ApiEnvironment.DEV
|
||||
override val defaultEnvironment: ApiEnvironment = getInitialEnvironment()
|
||||
|
||||
override val environmentConfigs = listOf(
|
||||
createProdEnvironment(),
|
||||
createDevEnvironment(),
|
||||
createMockedEnvironment(),
|
||||
createProdEnvironment(),
|
||||
)
|
||||
|
||||
private fun getInitialEnvironment(): ApiEnvironment {
|
||||
return when (BuildConfig.BUILD_TYPE) {
|
||||
MOCKED_BUILD_TYPE -> ApiEnvironment.MOCK
|
||||
DEBUG_BUILD_TYPE,
|
||||
INTERNAL_BUILD_TYPE,
|
||||
-> ApiEnvironment.DEV
|
||||
EXTERNAL_BUILD_TYPE,
|
||||
RELEASE_BUILD_TYPE,
|
||||
-> ApiEnvironment.PROD
|
||||
else -> error("Unknown build type [${BuildConfig.BUILD_TYPE}]")
|
||||
}
|
||||
}
|
||||
|
||||
private fun createDevEnvironment(): ApiEnvironmentConfig = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.DEV,
|
||||
baseUrl = "[REDACTED_ENV_URL]",
|
||||
headers = createHeaders(),
|
||||
)
|
||||
|
||||
private fun createMockedEnvironment(): ApiEnvironmentConfig = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.MOCK,
|
||||
baseUrl = "[REDACTED_ENV_URL]",
|
||||
headers = createHeaders(),
|
||||
)
|
||||
|
||||
private fun createProdEnvironment(): ApiEnvironmentConfig = ApiEnvironmentConfig(
|
||||
|
|
@ -20,12 +47,6 @@ internal class TangemPay(
|
|||
headers = createHeaders(),
|
||||
)
|
||||
|
||||
private fun createDevEnvironment(): ApiEnvironmentConfig = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.DEV,
|
||||
baseUrl = "[REDACTED_ENV_URL]",
|
||||
headers = createHeaders(),
|
||||
)
|
||||
|
||||
private fun createHeaders() = mapOf(
|
||||
"version" to ProviderSuspend { appVersionProvider.versionName },
|
||||
"platform" to ProviderSuspend { "Android" },
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.datasource.api.common.config
|
||||
|
||||
import com.tangem.datasource.BuildConfig
|
||||
import com.tangem.datasource.api.common.AuthProvider
|
||||
import com.tangem.datasource.utils.RequestHeader
|
||||
import com.tangem.utils.info.AppInfoProvider
|
||||
|
|
@ -12,18 +13,27 @@ internal class TangemTech(
|
|||
private val appInfoProvider: AppInfoProvider,
|
||||
) : ApiConfig() {
|
||||
|
||||
override val defaultEnvironment: ApiEnvironment = ApiEnvironment.PROD
|
||||
override val defaultEnvironment: ApiEnvironment = getInitialEnvironment()
|
||||
|
||||
override val environmentConfigs = listOf(
|
||||
createDevEnvironment(),
|
||||
createStageEnvironment(),
|
||||
createMockedEnvironment(),
|
||||
createProdEnvironment(),
|
||||
)
|
||||
|
||||
private fun createProdEnvironment(): ApiEnvironmentConfig = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.PROD,
|
||||
baseUrl = "https://api.tangem.org/v1/",
|
||||
headers = createHeaders(),
|
||||
)
|
||||
private fun getInitialEnvironment(): ApiEnvironment {
|
||||
return when (BuildConfig.BUILD_TYPE) {
|
||||
MOCKED_BUILD_TYPE,
|
||||
-> ApiEnvironment.MOCK
|
||||
DEBUG_BUILD_TYPE,
|
||||
INTERNAL_BUILD_TYPE,
|
||||
EXTERNAL_BUILD_TYPE,
|
||||
RELEASE_BUILD_TYPE,
|
||||
-> ApiEnvironment.PROD
|
||||
else -> error("Unknown build type [${BuildConfig.BUILD_TYPE}]")
|
||||
}
|
||||
}
|
||||
|
||||
private fun createDevEnvironment(): ApiEnvironmentConfig = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.DEV,
|
||||
|
|
@ -31,6 +41,24 @@ internal class TangemTech(
|
|||
headers = createHeaders(),
|
||||
)
|
||||
|
||||
private fun createStageEnvironment(): ApiEnvironmentConfig = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.STAGE,
|
||||
baseUrl = "[REDACTED_ENV_URL]",
|
||||
headers = createHeaders(),
|
||||
)
|
||||
|
||||
private fun createMockedEnvironment(): ApiEnvironmentConfig = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.MOCK,
|
||||
baseUrl = "[REDACTED_ENV_URL]",
|
||||
headers = createHeaders(),
|
||||
)
|
||||
|
||||
private fun createProdEnvironment(): ApiEnvironmentConfig = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.PROD,
|
||||
baseUrl = "https://api.tangem.org/",
|
||||
headers = createHeaders(),
|
||||
)
|
||||
|
||||
private fun createHeaders() = buildMap {
|
||||
putAll(from = RequestHeader.AppVersionPlatformHeaders(appVersionProvider, appInfoProvider).values)
|
||||
putAll(from = RequestHeader.AuthenticationHeader(authProvider).values)
|
||||
|
|
|
|||
|
|
@ -20,44 +20,42 @@ import kotlinx.coroutines.flow.*
|
|||
* @property dispatchers coroutine dispatcher provider
|
||||
*/
|
||||
internal class DevApiConfigsManager(
|
||||
apiConfigs: ApiConfigs,
|
||||
private val apiConfigs: ApiConfigs,
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : MutableApiConfigsManager {
|
||||
) : MutableApiConfigsManager() {
|
||||
|
||||
override val configs: Flow<Map<ApiConfig, ApiEnvironment>> get() = _apiConfigs
|
||||
override val configs: StateFlow<Map<ApiConfig, ApiEnvironment>>
|
||||
field = MutableStateFlow(value = getInitialConfigs())
|
||||
|
||||
private val _apiConfigs = MutableStateFlow(value = apiConfigs.associateWith { it.defaultEnvironment })
|
||||
|
||||
override val isInitialized: StateFlow<Boolean> get() = _isInitialized.asStateFlow()
|
||||
|
||||
private val _isInitialized = MutableStateFlow(value = false)
|
||||
override val isInitialized: StateFlow<Boolean>
|
||||
field = MutableStateFlow(value = false)
|
||||
|
||||
override fun initialize() {
|
||||
_isInitialized.value = false
|
||||
isInitialized.value = false
|
||||
|
||||
// We can't use appPreferencesStore.getObjectMap as base flow,
|
||||
// because we should keep possibility to work with configs synchronous.
|
||||
// See [getBaseUrl]
|
||||
appPreferencesStore.getObjectMap<ApiEnvironment>(PreferencesKeys.apiConfigsEnvironmentKey)
|
||||
.distinctUntilChanged()
|
||||
.onEach { savedEnvironments ->
|
||||
_apiConfigs.update { apiConfigs ->
|
||||
apiConfigs.mapValues {
|
||||
val (config, currentEnvironment) = it
|
||||
val apiConfigs = configs.value
|
||||
|
||||
savedEnvironments[config.id.name] ?: currentEnvironment
|
||||
}
|
||||
configs.value = apiConfigs.mapValues {
|
||||
val (config, currentEnvironment) = it
|
||||
|
||||
savedEnvironments[config.id.name] ?: currentEnvironment
|
||||
}
|
||||
|
||||
if (!_isInitialized.value) {
|
||||
_isInitialized.value = true
|
||||
if (!isInitialized.value) {
|
||||
isInitialized.value = true
|
||||
}
|
||||
|
||||
notifyListeners(apiConfigs = apiConfigs, savedEnvironments = savedEnvironments)
|
||||
}
|
||||
.launchIn(CoroutineScope(SupervisorJob() + dispatchers.default))
|
||||
}
|
||||
|
||||
override fun getEnvironmentConfig(id: ApiConfig.ID): ApiEnvironmentConfig {
|
||||
val apiConfigs = _apiConfigs.value
|
||||
val apiConfigs = configs.value
|
||||
|
||||
val config = apiConfigs.map { it }.firstOrNull { it.key.id == id }?.key
|
||||
?: error("Api config with id [$id] not found")
|
||||
|
|
@ -78,4 +76,56 @@ internal class DevApiConfigsManager(
|
|||
mutablePreferences.setObjectMap(PreferencesKeys.apiConfigsEnvironmentKey, updatedMap)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun changeEnvironment(environment: ApiEnvironment) {
|
||||
val supportedConfigs = apiConfigs
|
||||
.filter { config ->
|
||||
config.environmentConfigs.any { it.environment == environment }
|
||||
}
|
||||
.map { it.id.name }
|
||||
|
||||
appPreferencesStore.editData { mutablePreferences ->
|
||||
val updatedMap = mutablePreferences.getObjectMap<ApiEnvironment>(PreferencesKeys.apiConfigsEnvironmentKey)
|
||||
.mapValues { (configName, currentEnvironment) ->
|
||||
val isEnvSupported = supportedConfigs.contains(configName)
|
||||
|
||||
if (isEnvSupported) environment else currentEnvironment
|
||||
}
|
||||
|
||||
mutablePreferences.setObjectMap(key = PreferencesKeys.apiConfigsEnvironmentKey, value = updatedMap)
|
||||
}
|
||||
}
|
||||
|
||||
private fun notifyListeners(
|
||||
apiConfigs: Map<ApiConfig, ApiEnvironment>,
|
||||
savedEnvironments: Map<String, ApiEnvironment>,
|
||||
) {
|
||||
if (registerListeners.isNotEmpty()) {
|
||||
val changedConfigs = apiConfigs.mapNotNull { (config, prevEnvironment) ->
|
||||
val newEnvironment = savedEnvironments[config.id.name] ?: config.defaultEnvironment
|
||||
|
||||
if (prevEnvironment == newEnvironment) return@mapNotNull null
|
||||
|
||||
val environmentConfig = config.environmentConfigs
|
||||
.firstOrNull { it.environment == newEnvironment }
|
||||
?: return@mapNotNull null
|
||||
|
||||
config.id to environmentConfig
|
||||
}
|
||||
|
||||
if (changedConfigs.isNotEmpty()) {
|
||||
registerListeners.forEach { listener ->
|
||||
val changedConfig = changedConfigs.firstOrNull { it.first == listener.id }?.second
|
||||
|
||||
if (changedConfig != null) {
|
||||
listener.onChange(changedConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getInitialConfigs(): Map<ApiConfig, ApiEnvironment> {
|
||||
return apiConfigs.associateWith(ApiConfig::defaultEnvironment)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
package com.tangem.datasource.api.common.config.managers
|
||||
|
||||
import com.tangem.datasource.api.common.config.ApiConfig
|
||||
import com.tangem.datasource.api.common.config.ApiConfigs
|
||||
import com.tangem.datasource.api.common.config.ApiEnvironment
|
||||
import com.tangem.datasource.api.common.config.ApiEnvironmentConfig
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
/**
|
||||
* Implementation of [ApiConfigsManager] in MOCK environment
|
||||
*
|
||||
* @property apiConfigs api configs
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class MockApiConfigsManager(
|
||||
private val apiConfigs: ApiConfigs,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
) : MutableApiConfigsManager() {
|
||||
|
||||
override val configs: StateFlow<Map<ApiConfig, ApiEnvironment>>
|
||||
field = MutableStateFlow(value = getInitialConfigs())
|
||||
|
||||
override val isInitialized: StateFlow<Boolean> = MutableStateFlow(value = true)
|
||||
|
||||
private val coroutineScope = CoroutineScope(SupervisorJob() + dispatchers.default)
|
||||
|
||||
override fun initialize() = Unit
|
||||
|
||||
override fun getEnvironmentConfig(id: ApiConfig.ID): ApiEnvironmentConfig {
|
||||
val apiConfigs = configs.value
|
||||
|
||||
val (config, currentEnvironment) = apiConfigs.entries.firstOrNull { it.key.id == id }
|
||||
?: error("Api config with id [$id] not found")
|
||||
|
||||
return config.environmentConfigs.firstOrNull { it.environment == currentEnvironment }
|
||||
?: error("Api config with id [$id] doesn't contain environment [$currentEnvironment]")
|
||||
}
|
||||
|
||||
override suspend fun changeEnvironment(id: String, environment: ApiEnvironment) {
|
||||
configs.update { apiConfigs ->
|
||||
val apiConfig = apiConfigs.keys.firstOrNull { it.id.name == id }
|
||||
?: error("Api config with id [$id] not found. Check that ApiConfig with id [$id] was provided into DI")
|
||||
|
||||
apiConfigs + (apiConfig to environment)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun changeEnvironment(environment: ApiEnvironment) {
|
||||
configs.update { apiConfigs ->
|
||||
apiConfigs.mapValues { (config, currentEnvironment) ->
|
||||
val isEnvSupported = config.environmentConfigs.any { it.environment == environment }
|
||||
|
||||
if (isEnvSupported) environment else currentEnvironment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun addListener(listener: ApiConfigEnvChangeListener) {
|
||||
super.addListener(listener)
|
||||
|
||||
configs
|
||||
.map { it.entries.firstOrNull { it.key.id == listener.id } }
|
||||
.filterNotNull()
|
||||
.onEach { (apiConfig, currentEnvironment) ->
|
||||
listener.onChange(
|
||||
environmentConfig = apiConfig.environmentConfigs.first { it.environment == currentEnvironment },
|
||||
)
|
||||
}
|
||||
.launchIn(coroutineScope)
|
||||
}
|
||||
|
||||
private fun getInitialConfigs(): Map<ApiConfig, ApiEnvironment> {
|
||||
return apiConfigs.associateWith { it.defaultEnvironment }
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.datasource.api.common.config.managers
|
|||
|
||||
import com.tangem.datasource.api.common.config.ApiConfig
|
||||
import com.tangem.datasource.api.common.config.ApiEnvironment
|
||||
import com.tangem.datasource.api.common.config.ApiEnvironmentConfig
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
|
|
@ -9,11 +10,41 @@ import kotlinx.coroutines.flow.Flow
|
|||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface MutableApiConfigsManager : ApiConfigsManager {
|
||||
abstract class MutableApiConfigsManager : ApiConfigsManager {
|
||||
|
||||
/** Api configs with current [ApiEnvironment] */
|
||||
val configs: Flow<Map<ApiConfig, ApiEnvironment>>
|
||||
abstract val configs: Flow<Map<ApiConfig, ApiEnvironment>>
|
||||
|
||||
/**
|
||||
* A set of listeners registered to observe changes in API environment configurations.
|
||||
* These listeners are notified whenever an environment change occurs.
|
||||
*/
|
||||
protected val registerListeners: Set<ApiConfigEnvChangeListener>
|
||||
field = mutableSetOf<ApiConfigEnvChangeListener>()
|
||||
|
||||
/** Change api environment [environment] by [id] */
|
||||
suspend fun changeEnvironment(id: String, environment: ApiEnvironment)
|
||||
abstract suspend fun changeEnvironment(id: String, environment: ApiEnvironment)
|
||||
|
||||
/** Change api environment [environment] for all configs */
|
||||
abstract suspend fun changeEnvironment(environment: ApiEnvironment)
|
||||
|
||||
/** Adds a [listener] to observe changes in API environment configurations */
|
||||
open fun addListener(listener: ApiConfigEnvChangeListener) {
|
||||
registerListeners += listener
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for observing changes in API environment configurations
|
||||
*
|
||||
* @property id the identifier of the API configuration this listener is associated with
|
||||
*/
|
||||
abstract class ApiConfigEnvChangeListener(val id: ApiConfig.ID) {
|
||||
|
||||
/**
|
||||
* Called when the environment configuration changes
|
||||
*
|
||||
* @param environmentConfig the updated [ApiEnvironmentConfig] for the associated API configuration
|
||||
*/
|
||||
abstract fun onChange(environmentConfig: ApiEnvironmentConfig)
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ import retrofit2.http.Query
|
|||
interface TangemTechMarketsApi {
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@GET("coins/list")
|
||||
@GET("v1/coins/list")
|
||||
suspend fun getCoinsList(
|
||||
@Query("currency") currency: String,
|
||||
@Query("interval") interval: String,
|
||||
|
|
@ -20,24 +20,24 @@ interface TangemTechMarketsApi {
|
|||
@Query("timestamp") timestamp: Long?,
|
||||
): ApiResponse<TokenMarketListResponse>
|
||||
|
||||
@GET("coins/{coin_id}")
|
||||
@GET("v1/coins/{coin_id}")
|
||||
suspend fun getCoinMarketData(
|
||||
@Path("coin_id") coinId: String,
|
||||
@Query("currency") currency: String,
|
||||
@Query("language") language: String,
|
||||
): ApiResponse<TokenMarketInfoResponse>
|
||||
|
||||
@GET("coins/{coin_id}/history")
|
||||
@GET("v1/coins/{coin_id}/history")
|
||||
suspend fun getCoinChart(
|
||||
@Path("coin_id") coinId: String,
|
||||
@Query("currency") currency: String,
|
||||
@Query("interval") interval: String,
|
||||
): ApiResponse<TokenMarketChartResponse>
|
||||
|
||||
@GET("coins/{coin_id}/exchanges")
|
||||
@GET("v1/coins/{coin_id}/exchanges")
|
||||
suspend fun getCoinExchanges(@Path("coin_id") coinId: String): ApiResponse<TokenMarketExchangesResponse>
|
||||
|
||||
@GET("coins/history_preview")
|
||||
@GET("v1/coins/history_preview")
|
||||
suspend fun getCoinsListCharts(
|
||||
@Query("coin_ids") coinIds: String,
|
||||
@Query("currency") currency: String,
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import java.util.concurrent.TimeUnit
|
|||
@Suppress("TooManyFunctions")
|
||||
interface TangemTechApi {
|
||||
|
||||
@GET("coins")
|
||||
@GET("v1/coins")
|
||||
suspend fun getCoins(
|
||||
@Header("Cache-Control") cacheControl: String = "max-age=600",
|
||||
@Query("contractAddress") contractAddress: String? = null,
|
||||
|
|
@ -30,150 +30,150 @@ interface TangemTechApi {
|
|||
@Query("limit") limit: Int? = null,
|
||||
): ApiResponse<CoinsResponse>
|
||||
|
||||
@GET("rates")
|
||||
@GET("v1/rates")
|
||||
suspend fun getRates(@Query("currencyId") currencyId: String, @Query("coinIds") coinIds: String): RatesResponse
|
||||
|
||||
@GET("currencies")
|
||||
@GET("v1/currencies")
|
||||
suspend fun getCurrencyList(
|
||||
@Header("Cache-Control") cacheControl: String = "max-age=600",
|
||||
): ApiResponse<CurrenciesResponse>
|
||||
|
||||
@GET("geo")
|
||||
@GET("v1/geo")
|
||||
suspend fun getUserCountryCode(): GeoResponse
|
||||
|
||||
@GET("user-tokens/{user-id}")
|
||||
@GET("v1/user-tokens/{user-id}")
|
||||
suspend fun getUserTokens(@Path(value = "user-id") userId: String): ApiResponse<UserTokensResponse>
|
||||
|
||||
@PUT("user-tokens/{user-id}")
|
||||
@PUT("v1/user-tokens/{user-id}")
|
||||
suspend fun saveUserTokens(
|
||||
@Path(value = "user-id") userId: String,
|
||||
@Body userTokens: UserTokensResponse,
|
||||
): ApiResponse<Unit>
|
||||
|
||||
@POST("user-tokens")
|
||||
@POST("v1/user-tokens")
|
||||
suspend fun markUserWallerWasCreated(@Body body: MarkUserWalletWasCreatedBody): ApiResponse<Unit>
|
||||
|
||||
/** Returns referral status by [walletId] */
|
||||
@GET("referral/{walletId}")
|
||||
@GET("v1/referral/{walletId}")
|
||||
suspend fun getReferralStatus(@Path("walletId") walletId: String): ApiResponse<ReferralResponse>
|
||||
|
||||
/** Make user referral, requires [StartReferralBody] */
|
||||
@POST("referral")
|
||||
@POST("v1/referral")
|
||||
suspend fun startReferral(@Body startReferralBody: StartReferralBody): ApiResponse<ReferralResponse>
|
||||
|
||||
@GET("quotes")
|
||||
@GET("v1/quotes")
|
||||
suspend fun getQuotes(
|
||||
@Query("currencyId") currencyId: String,
|
||||
@Query("coinIds") coinIds: String,
|
||||
@Query("fields") fields: String,
|
||||
): ApiResponse<QuotesResponse>
|
||||
|
||||
@GET("promotion")
|
||||
@GET("v1/promotion")
|
||||
suspend fun getPromotionInfo(
|
||||
@Query("programName") name: String,
|
||||
@Header("Cache-Control") cacheControl: String = "max-age=600",
|
||||
): ApiResponse<PromotionInfoResponse>
|
||||
|
||||
@GET("settings/{wallet_id}")
|
||||
@GET("v1/settings/{wallet_id}")
|
||||
suspend fun getUserTokensSettings(@Path("wallet_id") walletId: String): ApiResponse<UserTokensSettingsResponse>
|
||||
|
||||
@PUT("settings/{wallet_id}")
|
||||
@PUT("v1/settings/{wallet_id}")
|
||||
suspend fun saveUserTokensSettings(
|
||||
@Path("wallet_id") walletId: String,
|
||||
@Body userTokensSettings: UserTokensSettingsResponse,
|
||||
): ApiResponse<Unit>
|
||||
|
||||
@POST("user-network-account")
|
||||
@POST("v1/user-network-account")
|
||||
suspend fun createUserNetworkAccount(
|
||||
@Body body: CreateUserNetworkAccountBody,
|
||||
): ApiResponse<CreateUserNetworkAccountResponse>
|
||||
|
||||
@POST("account")
|
||||
@POST("v1/account")
|
||||
suspend fun createUserTokensAccount(
|
||||
@Body body: CreateUserTokensAccountBody,
|
||||
): ApiResponse<UserTokensAccountResponse>
|
||||
|
||||
@PUT("account/{account_id}")
|
||||
@PUT("v1/account/{account_id}")
|
||||
suspend fun updateUserTokensAccount(
|
||||
@Path("account_id") accountId: Int,
|
||||
@Body body: UpdateUserTokensAccountBody,
|
||||
): ApiResponse<UserTokensAccountResponse>
|
||||
|
||||
@PUT("account/{account_id}/archive")
|
||||
@PUT("v1/account/{account_id}/archive")
|
||||
suspend fun archiveUserTokensAccount(@Path("account_id") accountId: Int): ApiResponse<UserTokensAccountResponse>
|
||||
|
||||
@PUT("account/{account_id}/unarchive")
|
||||
@PUT("v1/account/{account_id}/unarchive")
|
||||
suspend fun restoreUserTokensAccount(@Path("account_id") accountId: Int): ApiResponse<UserTokensAccountResponse>
|
||||
|
||||
@GET("features")
|
||||
@GET("v1/features")
|
||||
suspend fun getFeatures(): ApiResponse<FeaturesResponse>
|
||||
|
||||
@ReadTimeout(duration = 5, unit = TimeUnit.SECONDS)
|
||||
@GET("networks/providers")
|
||||
@GET("v1/networks/providers")
|
||||
suspend fun getBlockchainProviders(): Map<String, List<ProviderModel>>
|
||||
|
||||
@GET("seedphrase-notification/{wallet_id}")
|
||||
@GET("v1/seedphrase-notification/{wallet_id}")
|
||||
suspend fun getSeedPhraseNotificationStatus(
|
||||
@Path("wallet_id") walletId: String,
|
||||
): ApiResponse<SeedPhraseNotificationDTO>
|
||||
|
||||
@PUT("seedphrase-notification/{wallet_id}")
|
||||
@PUT("v1/seedphrase-notification/{wallet_id}")
|
||||
suspend fun updateSeedPhraseNotificationStatus(
|
||||
@Path("wallet_id") walletId: String,
|
||||
@Body body: SeedPhraseNotificationDTO,
|
||||
): ApiResponse<Unit>
|
||||
|
||||
@GET("seedphrase-notification/{wallet_id}/confirmed")
|
||||
@GET("v1/seedphrase-notification/{wallet_id}/confirmed")
|
||||
suspend fun getSeedPhraseSecondNotificationStatus(
|
||||
@Path("wallet_id") walletId: String,
|
||||
): ApiResponse<SeedPhraseNotificationDTO>
|
||||
|
||||
@PUT("seedphrase-notification/{wallet_id}/confirmed")
|
||||
@PUT("v1/seedphrase-notification/{wallet_id}/confirmed")
|
||||
suspend fun updateSeedPhraseSecondNotificationStatus(
|
||||
@Path("wallet_id") walletId: String,
|
||||
@Body body: SeedPhraseNotificationDTO,
|
||||
): ApiResponse<Unit>
|
||||
|
||||
@GET("hot_crypto")
|
||||
@GET("v1/hot_crypto")
|
||||
suspend fun getHotCrypto(@Query("currency") currencyId: String): ApiResponse<HotCryptoResponse>
|
||||
|
||||
@GET("stories/{story_id}")
|
||||
@GET("v1/stories/{story_id}")
|
||||
suspend fun getStoryById(@Path("story_id") storyId: String): ApiResponse<StoryContentResponse>
|
||||
|
||||
// region push notifications
|
||||
@GET("notification/push_notifications_eligible_networks")
|
||||
@GET("v1/notification/push_notifications_eligible_networks")
|
||||
suspend fun getEligibleNetworksForPushNotifications(): ApiResponse<List<CryptoNetworkResponse>>
|
||||
|
||||
@POST("user-wallets/applications/")
|
||||
@POST("v1/user-wallets/applications/")
|
||||
suspend fun createApplicationId(
|
||||
@Body
|
||||
body: NotificationApplicationCreateBody,
|
||||
): ApiResponse<NotificationApplicationIdResponse>
|
||||
|
||||
@PATCH("user-wallets/applications/{application_id}")
|
||||
@PATCH("v1/user-wallets/applications/{application_id}")
|
||||
suspend fun updatePushTokenForApplicationId(
|
||||
@Path("application_id") applicationId: String,
|
||||
@Body body: NotificationApplicationCreateBody,
|
||||
): ApiResponse<Unit>
|
||||
|
||||
@PATCH("user-wallets/wallets/{wallet_id}/notify")
|
||||
@PATCH("v1/user-wallets/wallets/{wallet_id}/notify")
|
||||
suspend fun setNotificationsEnabled(@Path("wallet_id") walletId: String, @Body body: WalletBody): ApiResponse<Unit>
|
||||
// endregion
|
||||
|
||||
// region wallets
|
||||
@PATCH("user-wallets/wallets/{wallet_id}")
|
||||
@PATCH("v1/user-wallets/wallets/{wallet_id}")
|
||||
suspend fun updateWallet(@Path("wallet_id") walletId: String, @Body body: WalletBody): ApiResponse<Unit>
|
||||
|
||||
@POST("user-wallets/wallets/create-and-connect-by-appuid/{application_id}")
|
||||
@POST("v1/user-wallets/wallets/create-and-connect-by-appuid/{application_id}")
|
||||
suspend fun associateApplicationIdWithWallets(
|
||||
@Path("application_id") applicationId: String,
|
||||
@Body body: List<WalletIdBody>,
|
||||
): ApiResponse<Unit>
|
||||
|
||||
@GET("user-wallets/wallets/{wallet_id}")
|
||||
@GET("v1/user-wallets/wallets/{wallet_id}")
|
||||
suspend fun getWalletById(@Path("wallet_id") walletId: String): ApiResponse<WalletResponse>
|
||||
|
||||
@GET("user-wallets/wallets/by-app/{app_id}")
|
||||
@GET("v1/user-wallets/wallets/by-app/{app_id}")
|
||||
suspend fun getWallets(@Path("app_id") appId: String): ApiResponse<List<WalletResponse>>
|
||||
// endregion
|
||||
}
|
||||
|
|
@ -60,8 +60,4 @@ internal object ApiConfigsModule {
|
|||
fun provideBlockAidConfig(environmentConfigStorage: EnvironmentConfigStorage): ApiConfig {
|
||||
return BlockAid(environmentConfigStorage)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@IntoSet
|
||||
fun provideAttestationConfig(): ApiConfig = Attestation()
|
||||
}
|
||||
|
|
@ -6,18 +6,20 @@ import com.tangem.core.analytics.api.AnalyticsErrorHandler
|
|||
import com.tangem.datasource.BuildConfig
|
||||
import com.tangem.datasource.api.common.blockaid.BlockAidApi
|
||||
import com.tangem.datasource.api.common.config.ApiConfig
|
||||
import com.tangem.datasource.api.common.config.ApiConfig.Companion.MOCKED_BUILD_TYPE
|
||||
import com.tangem.datasource.api.common.config.ApiConfigs
|
||||
import com.tangem.datasource.api.common.config.managers.ApiConfigsManager
|
||||
import com.tangem.datasource.api.common.config.managers.DevApiConfigsManager
|
||||
import com.tangem.datasource.api.common.config.managers.MockApiConfigsManager
|
||||
import com.tangem.datasource.api.common.config.managers.ProdApiConfigsManager
|
||||
import com.tangem.datasource.api.common.response.ApiResponseCallAdapterFactory
|
||||
import com.tangem.datasource.api.express.TangemExpressApi
|
||||
import com.tangem.datasource.api.markets.TangemTechMarketsApi
|
||||
import com.tangem.datasource.api.onramp.OnrampApi
|
||||
import com.tangem.datasource.api.pay.TangemPayApi
|
||||
import com.tangem.datasource.api.stakekit.StakeKitApi
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApiV2
|
||||
import com.tangem.datasource.api.pay.TangemPayApi
|
||||
import com.tangem.datasource.local.logs.AppLogsStore
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.utils.*
|
||||
|
|
@ -45,7 +47,7 @@ internal object NetworkModule {
|
|||
private const val STAKE_KIT_API_TIMEOUT_SECONDS = 60L
|
||||
|
||||
private val excludedApiForLogging: Set<ApiConfig.ID> = setOf(
|
||||
// ApiConfig.ID.StakeKit,
|
||||
ApiConfig.ID.StakeKit,
|
||||
)
|
||||
|
||||
@Provides
|
||||
|
|
@ -55,10 +57,10 @@ internal object NetworkModule {
|
|||
appPreferencesStore: AppPreferencesStore,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): ApiConfigsManager {
|
||||
return if (BuildConfig.TESTER_MENU_ENABLED) {
|
||||
DevApiConfigsManager(apiConfigs, appPreferencesStore, dispatchers)
|
||||
} else {
|
||||
ProdApiConfigsManager(apiConfigs)
|
||||
return when {
|
||||
BuildConfig.BUILD_TYPE == MOCKED_BUILD_TYPE -> MockApiConfigsManager(apiConfigs, dispatchers)
|
||||
BuildConfig.TESTER_MENU_ENABLED -> DevApiConfigsManager(apiConfigs, appPreferencesStore, dispatchers)
|
||||
else -> ProdApiConfigsManager(apiConfigs)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,10 @@ import com.squareup.moshi.Moshi
|
|||
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.YieldDTO
|
||||
import com.tangem.datasource.local.datastore.RuntimeDataStore
|
||||
import com.tangem.datasource.local.datastore.RuntimeSharedStore
|
||||
import com.tangem.datasource.local.token.*
|
||||
import com.tangem.datasource.local.token.DefaultStakingActionsStore
|
||||
import com.tangem.datasource.local.token.DefaultStakingYieldsStore
|
||||
import com.tangem.datasource.local.token.StakingActionsStore
|
||||
import com.tangem.datasource.local.token.StakingYieldsStore
|
||||
import com.tangem.datasource.utils.MoshiDataStoreSerializer
|
||||
import com.tangem.datasource.utils.listTypes
|
||||
import com.tangem.datasource.utils.mapWithStringKeyTypes
|
||||
|
|
@ -66,17 +68,6 @@ internal object StakingStoreModule {
|
|||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideStakingBalanceStore(
|
||||
persistenceStore: DataStore<Map<String, Set<YieldBalanceWrapperDTO>>>,
|
||||
): StakingBalanceStore {
|
||||
return DefaultStakingBalanceStore(
|
||||
persistenceStore = persistenceStore,
|
||||
runtimeStore = RuntimeSharedStore(),
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideStakingActionsStore(): StakingActionsStore {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.tangem.datasource.exchangeservice.hotcrypto
|
|||
|
||||
import com.tangem.datasource.api.tangemTech.models.HotCryptoResponse
|
||||
import com.tangem.datasource.local.datastore.RuntimeStateStore
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ import com.tangem.domain.core.lce.Lce
|
|||
import com.tangem.domain.core.utils.lceContent
|
||||
import com.tangem.domain.core.utils.lceError
|
||||
import com.tangem.domain.core.utils.lceLoading
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ package com.tangem.datasource.exchangeservice.swap
|
|||
import com.tangem.datasource.api.express.models.request.LeastTokenInfo
|
||||
import com.tangem.datasource.api.express.models.response.Asset
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import com.tangem.common.CardIdRangeDec
|
|||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||
import com.tangem.datasource.local.preferences.utils.getSyncOrDefault
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
object ExpressUtils {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import androidx.room.Insert
|
|||
import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
import com.tangem.datasource.local.db.entity.CryptoCurrenciesAccountEntity
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import androidx.room.Dao
|
|||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import com.tangem.datasource.local.db.entity.CryptoCurrencyEntity
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.datasource.local.db.utils
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
||||
internal class Converters {
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import com.tangem.datasource.local.nft.custom.NFTPriceId
|
|||
import com.tangem.datasource.utils.MoshiDataStoreSerializer
|
||||
import com.tangem.datasource.utils.listTypes
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.datasource.local.token
|
|||
import androidx.datastore.core.DataStore
|
||||
import com.tangem.datasource.api.express.models.response.Asset
|
||||
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ package com.tangem.datasource.local.token
|
|||
|
||||
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingAction
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
|
|
|
|||
|
|
@ -1,269 +0,0 @@
|
|||
package com.tangem.datasource.local.token
|
||||
|
||||
import androidx.datastore.core.DataStore
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
|
||||
import com.tangem.datasource.local.datastore.RuntimeSharedStore
|
||||
import com.tangem.datasource.local.token.StakingBalanceStore.StakingID
|
||||
import com.tangem.datasource.local.token.converter.YieldBalanceConverter
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.extensions.addOrReplace
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
internal typealias YieldBalanceWrappersDTO = Map<String, Set<YieldBalanceWrapperDTO>>
|
||||
internal typealias YieldBalanceListByWalletId = Map<UserWalletId, Set<YieldBalance>>
|
||||
|
||||
/**
|
||||
* Default implementation of [StakingBalanceStore]
|
||||
*
|
||||
* @property persistenceStore persistence store
|
||||
* @property runtimeStore runtime store
|
||||
*/
|
||||
internal class DefaultStakingBalanceStore(
|
||||
private val persistenceStore: DataStore<YieldBalanceWrappersDTO>,
|
||||
private val runtimeStore: RuntimeSharedStore<YieldBalanceListByWalletId>,
|
||||
) : StakingBalanceStore {
|
||||
|
||||
override fun get(userWalletId: UserWalletId, stakingIds: List<StakingID>): Flow<Set<YieldBalance>> = channelFlow {
|
||||
val cachedBalances = persistenceStore.data
|
||||
.map {
|
||||
val wrappers = it[userWalletId.stringValue].orEmpty()
|
||||
.filter { wrapper ->
|
||||
stakingIds.any { id ->
|
||||
id.address == wrapper.addresses.address && id.integrationId == wrapper.integrationId
|
||||
}
|
||||
}
|
||||
|
||||
YieldBalanceConverter(isCached = true).convertSet(input = wrappers)
|
||||
}
|
||||
.firstOrNull()
|
||||
.orEmpty()
|
||||
|
||||
if (cachedBalances.isNotEmpty()) {
|
||||
send(cachedBalances)
|
||||
}
|
||||
|
||||
runtimeStore.get()
|
||||
.map {
|
||||
it[userWalletId].orEmpty().filter { balance ->
|
||||
stakingIds.any { id ->
|
||||
id.address == balance.address && id.integrationId == balance.integrationId
|
||||
}
|
||||
}
|
||||
.toSet()
|
||||
}
|
||||
.onEach {
|
||||
val mergedBalances = mergeYieldBalances(
|
||||
stakingIds = stakingIds,
|
||||
cachedBalances = cachedBalances,
|
||||
runtimeBalances = it,
|
||||
)
|
||||
|
||||
send(mergedBalances)
|
||||
}
|
||||
.launchIn(scope = this)
|
||||
}
|
||||
|
||||
override fun get(userWalletId: UserWalletId, stakingID: StakingID): Flow<YieldBalance?> {
|
||||
return get(userWalletId = userWalletId, stakingIds = listOf(stakingID)).map { balances ->
|
||||
balances.getBalance(stakingID = stakingID)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getSyncOrNull(userWalletId: UserWalletId): Set<YieldBalance>? {
|
||||
val runtimeBalances = runtimeStore.getSyncOrNull()?.getValue(userWalletId).orEmpty()
|
||||
val cachedBalances = persistenceStore.data.firstOrNull()?.get(userWalletId.stringValue).orEmpty()
|
||||
|
||||
if (runtimeBalances.isEmpty() && cachedBalances.isEmpty()) return null
|
||||
|
||||
return cachedBalances.mapTo(hashSetOf()) {
|
||||
val cached = YieldBalanceConverter(source = StatusSource.ONLY_CACHE).convert(value = it)
|
||||
val runtime = runtimeBalances.getBalance(address = cached.address, integrationId = cached.integrationId)
|
||||
|
||||
if (runtime == null || runtime is YieldBalance.Error) cached else runtime
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getSyncOrNull(userWalletId: UserWalletId, stakingIds: List<StakingID>): Set<YieldBalance>? {
|
||||
val runtime = runtimeStore.getSyncOrNull()?.getValue(userWalletId)
|
||||
val cached = persistenceStore.data.firstOrNull()?.get(userWalletId.stringValue)
|
||||
|
||||
if (runtime.isNullOrEmpty() && cached.isNullOrEmpty()) return null
|
||||
|
||||
return mergeYieldBalances(
|
||||
cachedBalances = YieldBalanceConverter(source = StatusSource.ONLY_CACHE)
|
||||
.convertSet(input = cached.orEmpty()),
|
||||
runtimeBalances = runtime.orEmpty(),
|
||||
stakingIds = stakingIds,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getSyncOrNull(userWalletId: UserWalletId, stakingID: StakingID): YieldBalance? {
|
||||
val balances = getSyncOrNull(userWalletId = userWalletId, stakingIds = listOf(stakingID)) ?: return null
|
||||
|
||||
return balances.getBalance(stakingID = stakingID)
|
||||
}
|
||||
|
||||
override suspend fun store(userWalletId: UserWalletId, items: Set<YieldBalanceWrapperDTO>) {
|
||||
coroutineScope {
|
||||
launch {
|
||||
val newBalances = YieldBalanceConverter(isCached = false).convertSet(input = items)
|
||||
|
||||
runtimeStore.update(default = emptyMap()) { saved ->
|
||||
saved.toMutableMap().apply {
|
||||
this[userWalletId] = saved[userWalletId]
|
||||
?.addOrReplace(newBalances) { old, new ->
|
||||
old.integrationId == new.integrationId && old.address == new.address
|
||||
}
|
||||
?: newBalances
|
||||
}
|
||||
}
|
||||
}
|
||||
launch { storeInPersistenceStore(userWalletId = userWalletId, items = items) }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun refresh(userWalletId: UserWalletId, stakingIds: List<StakingID>) {
|
||||
updateRuntimeStore(userWalletId = userWalletId) { saved ->
|
||||
saved.mapTo(hashSetOf()) {
|
||||
val yieldBalance = it.takeIf { balance ->
|
||||
stakingIds.any { id -> balance.integrationId == id.integrationId && balance.address == id.address }
|
||||
}
|
||||
|
||||
yieldBalance?.copySealed(source = StatusSource.CACHE) ?: it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun store(userWalletId: UserWalletId, stakingID: StakingID, item: YieldBalanceWrapperDTO) {
|
||||
coroutineScope {
|
||||
launch {
|
||||
storeInRuntimeStore(
|
||||
userWalletId = userWalletId,
|
||||
integrationId = stakingID.integrationId,
|
||||
address = stakingID.address,
|
||||
item = item,
|
||||
)
|
||||
|
||||
storeInPersistenceStore(
|
||||
userWalletId = userWalletId,
|
||||
integrationId = stakingID.integrationId,
|
||||
address = stakingID.address,
|
||||
item = item,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun storeSingleYieldBalance(userWalletId: UserWalletId, item: YieldBalance) {
|
||||
runtimeStore.update(default = emptyMap()) { saved ->
|
||||
saved.toMutableMap().apply {
|
||||
this[userWalletId] = saved[userWalletId]
|
||||
?.addOrReplace(item) { it.integrationId == item.integrationId && it.address == item.address }
|
||||
?: setOf(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun storeInRuntimeStore(
|
||||
userWalletId: UserWalletId,
|
||||
integrationId: String,
|
||||
address: String,
|
||||
item: YieldBalanceWrapperDTO,
|
||||
) {
|
||||
val newBalance = YieldBalanceConverter(isCached = false).convert(value = item)
|
||||
|
||||
runtimeStore.update(default = emptyMap()) { saved ->
|
||||
saved.toMutableMap().apply {
|
||||
this[userWalletId] = saved[userWalletId]
|
||||
?.addOrReplace(newBalance) { it.integrationId == integrationId && it.address == address }
|
||||
?: setOf(newBalance)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun updateRuntimeStore(
|
||||
userWalletId: UserWalletId,
|
||||
function: (Set<YieldBalance>) -> Set<YieldBalance>,
|
||||
) {
|
||||
runtimeStore.update(default = emptyMap()) { saved ->
|
||||
saved.toMutableMap().apply {
|
||||
this[userWalletId] = function(this[userWalletId].orEmpty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun storeInPersistenceStore(userWalletId: UserWalletId, items: Set<YieldBalanceWrapperDTO>) {
|
||||
persistenceStore.updateData { current ->
|
||||
current.toMutableMap().apply {
|
||||
this[userWalletId.stringValue] = current[userWalletId.stringValue]
|
||||
?.addOrReplace(items = items) { old, new ->
|
||||
old.integrationId == new.integrationId && old.addresses.address == new.addresses.address
|
||||
}
|
||||
?: items
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun storeInPersistenceStore(
|
||||
userWalletId: UserWalletId,
|
||||
integrationId: String,
|
||||
address: String,
|
||||
item: YieldBalanceWrapperDTO,
|
||||
) {
|
||||
persistenceStore.updateData { current ->
|
||||
current.toMutableMap().apply {
|
||||
this[userWalletId.stringValue] = current[userWalletId.stringValue]
|
||||
?.addOrReplace(item) { it.integrationId == integrationId && it.addresses.address == address }
|
||||
?: setOf(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun mergeYieldBalances(
|
||||
cachedBalances: Set<YieldBalance>,
|
||||
runtimeBalances: Set<YieldBalance>,
|
||||
stakingIds: List<StakingID>,
|
||||
): Set<YieldBalance> {
|
||||
return stakingIds.mapTo(hashSetOf()) { id ->
|
||||
val runtime = runtimeBalances.getBalance(stakingID = id)
|
||||
|
||||
if (runtime == null || runtime is YieldBalance.Error) {
|
||||
getCachedBalanceIfPossible(cachedBalances = cachedBalances, stakingID = id)
|
||||
} else {
|
||||
runtime
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCachedBalanceIfPossible(cachedBalances: Set<YieldBalance>, stakingID: StakingID): YieldBalance {
|
||||
val cached = cachedBalances.getBalance(stakingID)
|
||||
?: return YieldBalance.Error(integrationId = stakingID.address, address = stakingID.integrationId)
|
||||
|
||||
val updatedCached = when (cached) {
|
||||
is YieldBalance.Data -> cached.copy(source = StatusSource.ONLY_CACHE)
|
||||
is YieldBalance.Empty -> cached.copy(source = StatusSource.ONLY_CACHE)
|
||||
is YieldBalance.Error,
|
||||
is YieldBalance.Unsupported,
|
||||
-> null
|
||||
}
|
||||
|
||||
return updatedCached ?: YieldBalance.Error(integrationId = stakingID.address, address = stakingID.integrationId)
|
||||
}
|
||||
|
||||
private fun Set<YieldBalance>.getBalance(stakingID: StakingID): YieldBalance? {
|
||||
return getBalance(address = stakingID.address, integrationId = stakingID.integrationId)
|
||||
}
|
||||
|
||||
private fun Set<YieldBalance>.getBalance(address: String?, integrationId: String?): YieldBalance? {
|
||||
return firstOrNull { yieldBalance ->
|
||||
val isCorrectAddress = address != null && address == yieldBalance.address
|
||||
val isCorrectIntegration = integrationId != null && yieldBalance.integrationId == integrationId
|
||||
|
||||
isCorrectIntegration && isCorrectAddress
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ import com.tangem.datasource.local.preferences.AppPreferencesStore
|
|||
import com.tangem.datasource.local.preferences.utils.getObject
|
||||
import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
|
||||
import com.tangem.datasource.local.preferences.utils.storeObject
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.datasource.local.token
|
||||
|
||||
import com.tangem.datasource.api.express.models.response.Asset
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
||||
interface ExpressAssetsStore {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package com.tangem.datasource.local.token
|
||||
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingAction
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface StakingActionsStore {
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
package com.tangem.datasource.local.token
|
||||
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalanceList
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/** Staking balance store */
|
||||
interface StakingBalanceStore {
|
||||
|
||||
/** Get flow of [YieldBalanceList] by [userWalletId] and [stakingIds] */
|
||||
fun get(userWalletId: UserWalletId, stakingIds: List<StakingID>): Flow<Set<YieldBalance>>
|
||||
|
||||
/** Get flow of [YieldBalance] by [userWalletId] and [stakingID] */
|
||||
fun get(userWalletId: UserWalletId, stakingID: StakingID): Flow<YieldBalance?>
|
||||
|
||||
/** Get all [YieldBalance] synchronously or null by [userWalletId] */
|
||||
suspend fun getSyncOrNull(userWalletId: UserWalletId): Set<YieldBalance>?
|
||||
|
||||
/** Get [YieldBalanceList] synchronously or null by [userWalletId] and [stakingIds] */
|
||||
suspend fun getSyncOrNull(userWalletId: UserWalletId, stakingIds: List<StakingID>): Set<YieldBalance>?
|
||||
|
||||
/** Get [YieldBalance] synchronously or null by [userWalletId] and [stakingID] */
|
||||
suspend fun getSyncOrNull(userWalletId: UserWalletId, stakingID: StakingID): YieldBalance?
|
||||
|
||||
/** Store [items] by [userWalletId] */
|
||||
suspend fun store(userWalletId: UserWalletId, items: Set<YieldBalanceWrapperDTO>)
|
||||
|
||||
/** Store [item] by [userWalletId] and [stakingID] */
|
||||
suspend fun store(userWalletId: UserWalletId, stakingID: StakingID, item: YieldBalanceWrapperDTO)
|
||||
|
||||
/** Store [item] by [userWalletId] */
|
||||
suspend fun storeSingleYieldBalance(userWalletId: UserWalletId, item: YieldBalance)
|
||||
|
||||
/** Refresh balances of [stakingIds] by [userWalletId] */
|
||||
suspend fun refresh(userWalletId: UserWalletId, stakingIds: List<StakingID>)
|
||||
|
||||
data class StakingID(val integrationId: String, val address: String)
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.datasource.local.token
|
||||
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ package com.tangem.datasource.local.txhistory
|
|||
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.TxInfo
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.txhistory.models.Page
|
||||
import com.tangem.domain.txhistory.models.PaginationWrapper
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
interface TxHistoryItemsStore {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package com.tangem.datasource.local.userwallet
|
||||
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface UserWalletsStore {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import com.tangem.blockchain.common.Blockchain
|
|||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
|
||||
import com.tangem.datasource.local.datastore.core.StringKeyDataStoreDecorator
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.utils.extensions.addOrReplace
|
||||
import com.tangem.utils.extensions.removeBy
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.tangem.datasource.local.walletmanager
|
|||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface WalletManagersStore {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.chuckerteam.chucker.api.ChuckerInterceptor
|
|||
import com.tangem.datasource.BuildConfig
|
||||
import com.tangem.datasource.api.common.SwitchEnvironmentInterceptor
|
||||
import com.tangem.datasource.api.common.config.ApiConfig
|
||||
import com.tangem.datasource.api.common.config.ApiConfig.Companion.MOCKED_BUILD_TYPE
|
||||
import com.tangem.datasource.api.common.config.managers.ApiConfigsManager
|
||||
import com.tangem.datasource.api.common.createNetworkLoggingInterceptor
|
||||
import com.tangem.datasource.api.utils.ConnectTimeout
|
||||
|
|
@ -93,7 +94,7 @@ internal fun OkHttpClient.Builder.applyApiConfig(
|
|||
id: ApiConfig.ID,
|
||||
apiConfigsManager: ApiConfigsManager,
|
||||
): OkHttpClient.Builder {
|
||||
return if (BuildConfig.TESTER_MENU_ENABLED) {
|
||||
return if (BuildConfig.TESTER_MENU_ENABLED || BuildConfig.BUILD_TYPE == MOCKED_BUILD_TYPE) {
|
||||
addInterceptor(
|
||||
interceptor = SwitchEnvironmentInterceptor(
|
||||
id = id,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
package com.tangem.datasource.api.common.config
|
||||
|
||||
import com.google.common.truth.Truth
|
||||
import io.mockk.mockk
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class ApiConfigTest {
|
||||
|
||||
@Test
|
||||
fun `all baseUrls ends with slash`() {
|
||||
// Arrange
|
||||
val allBaseUrls = createApiConfigs().flatMap { it.environmentConfigs.map { it.baseUrl } }
|
||||
|
||||
// Actual
|
||||
val actual = allBaseUrls.all { it.endsWith("/") }
|
||||
|
||||
Timber.e(allBaseUrls.joinToString(separator = "\n"))
|
||||
|
||||
// Assert
|
||||
Truth.assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
private fun createApiConfigs(): ApiConfigs {
|
||||
return ApiConfig.ID.entries.mapTo(destination = hashSetOf()) {
|
||||
when (it) {
|
||||
ApiConfig.ID.Express -> {
|
||||
Express(
|
||||
environmentConfigStorage = mockk(),
|
||||
expressAuthProvider = mockk(),
|
||||
appVersionProvider = mockk(),
|
||||
appInfoProvider = mockk(),
|
||||
)
|
||||
}
|
||||
ApiConfig.ID.TangemTech -> {
|
||||
TangemTech(
|
||||
appVersionProvider = mockk(),
|
||||
authProvider = mockk(),
|
||||
appInfoProvider = mockk(),
|
||||
)
|
||||
}
|
||||
ApiConfig.ID.StakeKit -> StakeKit(stakeKitAuthProvider = mockk())
|
||||
ApiConfig.ID.TangemPay -> TangemPay(appVersionProvider = mockk())
|
||||
ApiConfig.ID.BlockAid -> BlockAid(configStorage = mockk())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ internal class MockEnvironmentConfigStorage : EnvironmentConfigStorage {
|
|||
private val environmentConfig = EnvironmentConfig(
|
||||
express = ExpressModel(apiKey = EXPRESS_API_KEY, signVerifierPublicKey = "vocibus"),
|
||||
devExpress = ExpressModel(apiKey = EXPRESS_DEV_API_KEY, signVerifierPublicKey = "pellentesque"),
|
||||
blockAidApiKey = BLOCK_AID_API_KEY,
|
||||
)
|
||||
|
||||
override suspend fun initialize() = environmentConfig
|
||||
|
|
@ -24,5 +25,6 @@ internal class MockEnvironmentConfigStorage : EnvironmentConfigStorage {
|
|||
companion object {
|
||||
const val EXPRESS_API_KEY = "express_api_key"
|
||||
const val EXPRESS_DEV_API_KEY = "express_dev_api_key"
|
||||
const val BLOCK_AID_API_KEY = "block_aid_api_key"
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.datasource.api.common.config.managers
|
|||
|
||||
import android.os.Build
|
||||
import com.google.common.truth.Truth
|
||||
import com.tangem.common.test.utils.ProvideTestModels
|
||||
import com.tangem.datasource.BuildConfig
|
||||
import com.tangem.datasource.api.common.AuthProvider
|
||||
import com.tangem.datasource.api.common.config.*
|
||||
|
|
@ -10,46 +11,47 @@ import com.tangem.datasource.api.common.config.ApiConfig.Companion.EXTERNAL_BUIL
|
|||
import com.tangem.datasource.api.common.config.ApiConfig.Companion.INTERNAL_BUILD_TYPE
|
||||
import com.tangem.datasource.api.common.config.ApiConfig.Companion.MOCKED_BUILD_TYPE
|
||||
import com.tangem.datasource.api.common.config.ApiConfig.Companion.RELEASE_BUILD_TYPE
|
||||
import com.tangem.datasource.api.common.config.managers.MockEnvironmentConfigStorage.Companion.BLOCK_AID_API_KEY
|
||||
import com.tangem.lib.auth.ExpressAuthProvider
|
||||
import com.tangem.lib.auth.StakeKitAuthProvider
|
||||
import com.tangem.utils.ProviderSuspend
|
||||
import com.tangem.utils.info.AppInfoProvider
|
||||
import com.tangem.utils.version.AppVersionProvider
|
||||
import io.mockk.clearMocks
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
import java.util.Locale
|
||||
import java.util.TimeZone
|
||||
|
||||
private val configManager = MockEnvironmentConfigStorage()
|
||||
private val appVersionProvider = mockk<AppVersionProvider>()
|
||||
private val expressAuthProvider = mockk<ExpressAuthProvider>()
|
||||
private val stakeKitAuthProvider = mockk<StakeKitAuthProvider>()
|
||||
private val appAuthProvider = mockk<AuthProvider>()
|
||||
private val appInfoProvider = mockk<AppInfoProvider>()
|
||||
|
||||
// Don't forget to add new config !!!
|
||||
private val API_CONFIGS = setOf(
|
||||
Express(configManager, expressAuthProvider, appVersionProvider, appInfoProvider),
|
||||
TangemTech(appVersionProvider, appAuthProvider, appInfoProvider),
|
||||
StakeKit(stakeKitAuthProvider),
|
||||
TangemPay(appVersionProvider),
|
||||
)
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@RunWith(Parameterized::class)
|
||||
internal class ProdApiConfigsManagerTest(private val model: Model) {
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
internal class ProdApiConfigsManagerTest {
|
||||
|
||||
private val manager = ProdApiConfigsManager(API_CONFIGS)
|
||||
private val environmentConfigStorage = MockEnvironmentConfigStorage()
|
||||
private val appVersionProvider = mockk<AppVersionProvider>()
|
||||
private val expressAuthProvider = mockk<ExpressAuthProvider>()
|
||||
private val stakeKitAuthProvider = mockk<StakeKitAuthProvider>()
|
||||
private val appAuthProvider = mockk<AuthProvider>()
|
||||
private val appInfoProvider = mockk<AppInfoProvider>()
|
||||
|
||||
@Before
|
||||
private val manager = ProdApiConfigsManager(apiConfigs = createApiConfigs())
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
clearMocks(
|
||||
appVersionProvider,
|
||||
expressAuthProvider,
|
||||
stakeKitAuthProvider,
|
||||
appAuthProvider,
|
||||
appInfoProvider,
|
||||
)
|
||||
|
||||
every { appVersionProvider.versionName } returns VERSION_NAME
|
||||
every { expressAuthProvider.getSessionId() } returns EXPRESS_SESSION_ID
|
||||
every { stakeKitAuthProvider.getApiKey() } returns STAKE_KIT_API_KEY
|
||||
|
|
@ -58,8 +60,9 @@ internal class ProdApiConfigsManagerTest(private val model: Model) {
|
|||
every { appInfoProvider.osVersion } returns "Android 16"
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_getEnvironmentConfig() {
|
||||
@ParameterizedTest
|
||||
@ProvideTestModels
|
||||
fun getEnvironmentConfig(model: TestModel) {
|
||||
val actual = manager.getEnvironmentConfig(id = model.id)
|
||||
|
||||
Truth.assertThat(actual.environment).isEqualTo(model.expected.environment)
|
||||
|
|
@ -69,160 +72,175 @@ internal class ProdApiConfigsManagerTest(private val model: Model) {
|
|||
.isEqualTo(model.expected.headers.mapValues { runBlocking { it.value() } })
|
||||
}
|
||||
|
||||
data class Model(val id: ApiConfig.ID, val expected: ApiEnvironmentConfig)
|
||||
private fun createApiConfigs(): ApiConfigs {
|
||||
return ApiConfig.ID.entries.mapTo(destination = hashSetOf()) {
|
||||
when (it) {
|
||||
ApiConfig.ID.Express -> {
|
||||
Express(
|
||||
environmentConfigStorage = environmentConfigStorage,
|
||||
expressAuthProvider = expressAuthProvider,
|
||||
appVersionProvider = appVersionProvider,
|
||||
appInfoProvider = appInfoProvider,
|
||||
)
|
||||
}
|
||||
ApiConfig.ID.TangemTech -> {
|
||||
TangemTech(
|
||||
appVersionProvider = appVersionProvider,
|
||||
authProvider = appAuthProvider,
|
||||
appInfoProvider = appInfoProvider,
|
||||
)
|
||||
}
|
||||
ApiConfig.ID.StakeKit -> StakeKit(stakeKitAuthProvider = stakeKitAuthProvider)
|
||||
ApiConfig.ID.TangemPay -> TangemPay(appVersionProvider = appVersionProvider)
|
||||
ApiConfig.ID.BlockAid -> BlockAid(configStorage = environmentConfigStorage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal companion object {
|
||||
private fun provideTestModels() = ApiConfig.ID.entries.map {
|
||||
when (it) {
|
||||
ApiConfig.ID.Express -> createExpressModel()
|
||||
ApiConfig.ID.TangemTech -> createTangemTechModel()
|
||||
ApiConfig.ID.StakeKit -> createStakeKitModel()
|
||||
ApiConfig.ID.TangemPay -> createTangemPayModel()
|
||||
ApiConfig.ID.BlockAid -> createBlockAidSdkModel()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createExpressModel(): TestModel {
|
||||
val environment = when (BuildConfig.BUILD_TYPE) {
|
||||
DEBUG_BUILD_TYPE,
|
||||
-> ApiEnvironment.DEV
|
||||
INTERNAL_BUILD_TYPE,
|
||||
MOCKED_BUILD_TYPE,
|
||||
-> ApiEnvironment.STAGE
|
||||
EXTERNAL_BUILD_TYPE,
|
||||
RELEASE_BUILD_TYPE,
|
||||
-> ApiEnvironment.PROD
|
||||
else -> error("Unknown build type [${BuildConfig.BUILD_TYPE}]")
|
||||
}
|
||||
|
||||
return TestModel(
|
||||
id = ApiConfig.ID.Express,
|
||||
expected = ApiEnvironmentConfig(
|
||||
environment = environment,
|
||||
baseUrl = when (BuildConfig.BUILD_TYPE) {
|
||||
DEBUG_BUILD_TYPE,
|
||||
-> "[REDACTED_ENV_URL]"
|
||||
INTERNAL_BUILD_TYPE,
|
||||
MOCKED_BUILD_TYPE,
|
||||
-> "[REDACTED_ENV_URL]"
|
||||
EXTERNAL_BUILD_TYPE,
|
||||
RELEASE_BUILD_TYPE,
|
||||
-> "https://express.tangem.com/v1/"
|
||||
else -> error("Unknown build type [${BuildConfig.BUILD_TYPE}]")
|
||||
},
|
||||
headers = mapOf(
|
||||
"api-key" to ProviderSuspend {
|
||||
if (environment == ApiEnvironment.PROD) {
|
||||
MockEnvironmentConfigStorage.EXPRESS_API_KEY
|
||||
} else {
|
||||
MockEnvironmentConfigStorage.EXPRESS_DEV_API_KEY
|
||||
}
|
||||
},
|
||||
"session-id" to ProviderSuspend { EXPRESS_SESSION_ID },
|
||||
"version" to ProviderSuspend { VERSION_NAME },
|
||||
"system_version" to ProviderSuspend { "Android 16" },
|
||||
"platform" to ProviderSuspend { "android" },
|
||||
"language" to ProviderSuspend { Locale.getDefault().language.checkHeaderValueOrEmpty() },
|
||||
"timezone" to ProviderSuspend {
|
||||
TimeZone.getDefault().getDisplayName(false, TimeZone.SHORT).checkHeaderValueOrEmpty()
|
||||
},
|
||||
"device" to ProviderSuspend { "${Build.MANUFACTURER} ${Build.MODEL}".checkHeaderValueOrEmpty() },
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createTangemTechModel(): TestModel {
|
||||
return TestModel(
|
||||
id = ApiConfig.ID.TangemTech,
|
||||
expected = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.PROD,
|
||||
baseUrl = "https://api.tangem.org/",
|
||||
headers = mapOf(
|
||||
"card_id" to ProviderSuspend { APP_CARD_ID },
|
||||
"card_public_key" to ProviderSuspend { APP_CARD_PUBLIC_KEY },
|
||||
"version" to ProviderSuspend { VERSION_NAME },
|
||||
"platform" to ProviderSuspend { "android" },
|
||||
"system_version" to ProviderSuspend { "Android 16" },
|
||||
"language" to ProviderSuspend { Locale.getDefault().language.checkHeaderValueOrEmpty() },
|
||||
"timezone" to ProviderSuspend {
|
||||
TimeZone.getDefault().getDisplayName(false, TimeZone.SHORT).checkHeaderValueOrEmpty()
|
||||
},
|
||||
"device" to ProviderSuspend { "${Build.MANUFACTURER} ${Build.MODEL}".checkHeaderValueOrEmpty() },
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createStakeKitModel(): TestModel {
|
||||
return TestModel(
|
||||
id = ApiConfig.ID.StakeKit,
|
||||
expected = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.PROD,
|
||||
baseUrl = "https://api.stakek.it/v1/",
|
||||
headers = mapOf(
|
||||
"X-API-KEY" to ProviderSuspend { STAKE_KIT_API_KEY },
|
||||
"accept" to ProviderSuspend { "application/json" },
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createTangemPayModel(): TestModel {
|
||||
return TestModel(
|
||||
id = ApiConfig.ID.TangemPay,
|
||||
expected = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.DEV,
|
||||
baseUrl = "[REDACTED_ENV_URL]",
|
||||
headers = mapOf(
|
||||
"version" to ProviderSuspend { VERSION_NAME },
|
||||
"platform" to ProviderSuspend { "Android" },
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createBlockAidSdkModel(): TestModel {
|
||||
return TestModel(
|
||||
id = ApiConfig.ID.BlockAid,
|
||||
expected = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.PROD,
|
||||
baseUrl = "https://api.blockaid.io/v0/",
|
||||
headers = mapOf(
|
||||
"X-API-KEY" to ProviderSuspend { BLOCK_AID_API_KEY },
|
||||
"accept" to ProviderSuspend { "application/json" },
|
||||
"content-type" to ProviderSuspend { "application/json" },
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun String.checkHeaderValueOrEmpty(): String {
|
||||
for (i in this.indices) {
|
||||
val c = this[i]
|
||||
val charCondition = c == '\t' || c in '\u0020'..'\u007e'
|
||||
if (!charCondition) {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
data class TestModel(val id: ApiConfig.ID, val expected: ApiEnvironmentConfig)
|
||||
|
||||
private companion object {
|
||||
|
||||
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 STAKE_KIT_API_KEY = "stake_kit_api_key"
|
||||
const val APP_CARD_ID = "app_card_id"
|
||||
const val APP_CARD_PUBLIC_KEY = "app_public_key"
|
||||
|
||||
@JvmStatic
|
||||
@Parameterized.Parameters
|
||||
fun data(): Collection<Model> = API_CONFIGS.map {
|
||||
when (it) {
|
||||
is Express -> createExpressModel()
|
||||
is TangemTech -> createTangemTechModel()
|
||||
is StakeKit -> createStakeKitModel()
|
||||
is TangemPay -> createVisaModel()
|
||||
is Attestation -> createAttestationModel()
|
||||
is BlockAid -> createBlockAidSdkModel()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createExpressModel(): Model {
|
||||
val environment = when (BuildConfig.BUILD_TYPE) {
|
||||
DEBUG_BUILD_TYPE,
|
||||
-> ApiEnvironment.DEV
|
||||
INTERNAL_BUILD_TYPE,
|
||||
MOCKED_BUILD_TYPE,
|
||||
-> ApiEnvironment.STAGE
|
||||
EXTERNAL_BUILD_TYPE,
|
||||
RELEASE_BUILD_TYPE,
|
||||
-> ApiEnvironment.PROD
|
||||
else -> error("Unknown build type [${BuildConfig.BUILD_TYPE}]")
|
||||
}
|
||||
|
||||
return Model(
|
||||
id = ApiConfig.ID.Express,
|
||||
expected = ApiEnvironmentConfig(
|
||||
environment = environment,
|
||||
baseUrl = when (BuildConfig.BUILD_TYPE) {
|
||||
DEBUG_BUILD_TYPE,
|
||||
-> "[REDACTED_ENV_URL]"
|
||||
INTERNAL_BUILD_TYPE,
|
||||
MOCKED_BUILD_TYPE,
|
||||
-> "[REDACTED_ENV_URL]"
|
||||
EXTERNAL_BUILD_TYPE,
|
||||
RELEASE_BUILD_TYPE,
|
||||
-> "https://express.tangem.com/v1/"
|
||||
else -> error("Unknown build type [${BuildConfig.BUILD_TYPE}]")
|
||||
},
|
||||
headers = mapOf(
|
||||
"api-key" to ProviderSuspend {
|
||||
if (environment == ApiEnvironment.PROD) {
|
||||
MockEnvironmentConfigStorage.EXPRESS_API_KEY
|
||||
} else {
|
||||
MockEnvironmentConfigStorage.EXPRESS_DEV_API_KEY
|
||||
}
|
||||
},
|
||||
"session-id" to ProviderSuspend { EXPRESS_SESSION_ID },
|
||||
"version" to ProviderSuspend { VERSION_NAME },
|
||||
"system_version" to ProviderSuspend { "Android 16" },
|
||||
"platform" to ProviderSuspend { "android" },
|
||||
"language" to ProviderSuspend { Locale.getDefault().language.checkHeaderValueOrEmpty() },
|
||||
"timezone" to ProviderSuspend {
|
||||
TimeZone.getDefault().getDisplayName(false, TimeZone.SHORT).checkHeaderValueOrEmpty()
|
||||
},
|
||||
"device" to ProviderSuspend { "${Build.MANUFACTURER} ${Build.MODEL}".checkHeaderValueOrEmpty() },
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createTangemTechModel(): Model {
|
||||
return Model(
|
||||
id = ApiConfig.ID.TangemTech,
|
||||
expected = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.PROD,
|
||||
baseUrl = "https://api.tangem.org/v1/",
|
||||
headers = mapOf(
|
||||
"card_id" to ProviderSuspend { APP_CARD_ID },
|
||||
"card_public_key" to ProviderSuspend { APP_CARD_PUBLIC_KEY },
|
||||
"version" to ProviderSuspend { VERSION_NAME },
|
||||
"platform" to ProviderSuspend { "android" },
|
||||
"system_version" to ProviderSuspend { "Android 16" },
|
||||
"language" to ProviderSuspend { Locale.getDefault().language.checkHeaderValueOrEmpty() },
|
||||
"timezone" to ProviderSuspend {
|
||||
TimeZone.getDefault().getDisplayName(false, TimeZone.SHORT).checkHeaderValueOrEmpty()
|
||||
},
|
||||
"device" to ProviderSuspend { "${Build.MANUFACTURER} ${Build.MODEL}".checkHeaderValueOrEmpty() },
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createStakeKitModel(): Model {
|
||||
return Model(
|
||||
id = ApiConfig.ID.StakeKit,
|
||||
expected = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.PROD,
|
||||
baseUrl = "https://api.stakek.it/v1/",
|
||||
headers = mapOf(
|
||||
"X-API-KEY" to ProviderSuspend { STAKE_KIT_API_KEY },
|
||||
"accept" to ProviderSuspend { "application/json" },
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createVisaModel(): Model {
|
||||
return Model(
|
||||
id = ApiConfig.ID.TangemPay,
|
||||
expected = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.DEV,
|
||||
baseUrl = "[REDACTED_ENV_URL]",
|
||||
headers = mapOf(
|
||||
"version" to ProviderSuspend { VERSION_NAME },
|
||||
"platform" to ProviderSuspend { "Android" },
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createAttestationModel(): Model {
|
||||
return Model(
|
||||
id = ApiConfig.ID.Attestation,
|
||||
expected = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.PROD,
|
||||
baseUrl = "https://api.tangem-tech.com/",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createBlockAidSdkModel(): Model {
|
||||
return Model(
|
||||
id = ApiConfig.ID.BlockAid,
|
||||
expected = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.PROD,
|
||||
baseUrl = "https://api.blockaid.io/v0/",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun String.checkHeaderValueOrEmpty(): String {
|
||||
for (i in this.indices) {
|
||||
val c = this[i]
|
||||
val charCondition = c == '\t' || c in '\u0020'..'\u007e'
|
||||
if (!charCondition) {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package com.tangem.core.decompose.utils
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.arkivanov.essenty.instancekeeper.getOrCreateSimple
|
||||
import com.arkivanov.essenty.lifecycle.subscribe
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class ActivityInstanceHolder<T : Any> {
|
||||
private var instance: T? = null
|
||||
|
||||
lateinit var instanceAccess: WeakReference<T>
|
||||
private set
|
||||
|
||||
fun set(instance: T) {
|
||||
this.instance = instance
|
||||
instanceAccess = WeakReference(instance)
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
instance = null
|
||||
instanceAccess.clear()
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> AppComponentContext.getOrCreateActivityInstanceHolder(
|
||||
noinline factory: (AppCompatActivity) -> T,
|
||||
): ActivityInstanceHolder<T> {
|
||||
val holder = instanceKeeper.getOrCreateSimple {
|
||||
ActivityInstanceHolder<T>()
|
||||
}
|
||||
|
||||
lifecycle.subscribe(
|
||||
onCreate = {
|
||||
holder.set(factory(activity))
|
||||
},
|
||||
onDestroy = {
|
||||
holder.clear()
|
||||
},
|
||||
)
|
||||
|
||||
return holder
|
||||
}
|
||||
1
core/deep-links/.gitignore
vendored
1
core/deep-links/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
|||
/build
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
plugins {
|
||||
alias(deps.plugins.android.library)
|
||||
alias(deps.plugins.kotlin.android)
|
||||
alias(deps.plugins.kotlin.kapt)
|
||||
id("configuration")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.tangem.core.deeplink"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
/* Common */
|
||||
implementation(projects.common.routing)
|
||||
|
||||
/* Core */
|
||||
implementation(projects.core.decompose)
|
||||
|
||||
/* Libs - AndroidX */
|
||||
implementation(deps.lifecycle.runtime.ktx)
|
||||
|
||||
/* Libs - Other */
|
||||
implementation(deps.timber)
|
||||
|
||||
/* DI */
|
||||
implementation(deps.hilt.android)
|
||||
kapt(deps.hilt.kapt)
|
||||
|
||||
/* Tests */
|
||||
testImplementation(deps.test.junit)
|
||||
testImplementation(deps.test.coroutine)
|
||||
testImplementation(deps.test.truth)
|
||||
testImplementation(deps.test.mockk)
|
||||
}
|
||||
1
core/deep-links/global/.gitignore
vendored
1
core/deep-links/global/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
|||
/build
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
plugins {
|
||||
alias(deps.plugins.android.library)
|
||||
alias(deps.plugins.kotlin.android)
|
||||
id("configuration")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.tangem.core.deeplink.global"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
/* Project */
|
||||
implementation(projects.core.deepLinks)
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
package com.tangem.core.deeplink.global
|
||||
|
||||
import com.tangem.core.deeplink.DeepLink
|
||||
|
||||
@Deprecated("Use BuyDeepLinkHandler")
|
||||
class BuyCurrencyDeepLink(
|
||||
val onReceive: () -> Unit,
|
||||
) : DeepLink() {
|
||||
|
||||
override val uri = BUY_REDIRECT_DEEPLINK
|
||||
|
||||
override fun onReceive(params: Map<String, String>) {
|
||||
onReceive()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val BUY_REDIRECT_DEEPLINK = "tangem://redirect?action=dismissBrowser"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
package com.tangem.core.deeplink.global
|
||||
|
||||
import com.tangem.core.deeplink.DeepLink
|
||||
|
||||
@Deprecated("Use ReferralDeepLinkHandler")
|
||||
class ReferralDeepLink(
|
||||
val onReceive: () -> Unit,
|
||||
) : DeepLink(shouldHandleDelayed = true) {
|
||||
override val uri: String = "tangem://referral"
|
||||
|
||||
override fun onReceive(params: Map<String, String>) {
|
||||
onReceive()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
package com.tangem.core.deeplink.global
|
||||
|
||||
import com.tangem.core.deeplink.DeepLink
|
||||
|
||||
@Deprecated("Use SellDeepLinkHandler")
|
||||
class SellCurrencyDeepLink(
|
||||
val onReceive: (data: Data) -> Unit,
|
||||
shouldHandleDelayed: Boolean,
|
||||
) : DeepLink(shouldHandleDelayed) {
|
||||
|
||||
override val uri: String = "tangem://redirect_sell"
|
||||
|
||||
override fun onReceive(params: Map<String, String>) {
|
||||
val data = Data(
|
||||
transactionId = params["transactionId"] ?: return,
|
||||
baseCurrencyAmount = params["baseCurrencyAmount"] ?: return,
|
||||
depositWalletAddress = params["depositWalletAddress"] ?: return,
|
||||
currencyId = params["currency_id"] ?: return,
|
||||
depositWalletAddressTag = params["depositWalletAddressTag"],
|
||||
)
|
||||
|
||||
onReceive(data)
|
||||
}
|
||||
|
||||
data class Data(
|
||||
val transactionId: String,
|
||||
val baseCurrencyAmount: String,
|
||||
val depositWalletAddress: String,
|
||||
val currencyId: String,
|
||||
val depositWalletAddressTag: String?,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
package com.tangem.core.deeplink
|
||||
|
||||
/**
|
||||
* Represents a deep link.
|
||||
*/
|
||||
abstract class DeepLink(val shouldHandleDelayed: Boolean = false) {
|
||||
|
||||
/**
|
||||
* ID of the deep link.
|
||||
*
|
||||
* By default, it is the same as the [uri].
|
||||
* */
|
||||
val id: String get() = uri
|
||||
|
||||
/**
|
||||
* URI of the deep link.
|
||||
*
|
||||
* **Note: Remember to add the URI in the AndroidManifest.xml file in the `app` module.**
|
||||
*
|
||||
* Query parameters will be received automatically.
|
||||
*
|
||||
* Path parameters can be added using the following syntax:
|
||||
* ```kotlin
|
||||
* "tangem://link" // Without parameters
|
||||
* "tangem://link/{param1}/{param2}" // With path parameters
|
||||
* ```
|
||||
* */
|
||||
abstract val uri: String
|
||||
|
||||
/**
|
||||
* Method to be called when this deep link is received.
|
||||
*
|
||||
* @param params Map of parameters received from the deep link.
|
||||
* */
|
||||
abstract fun onReceive(params: Map<String, String>)
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
package com.tangem.core.deeplink
|
||||
|
||||
import android.content.Intent
|
||||
|
||||
/**
|
||||
* Key to pass deeplink via intent
|
||||
*/
|
||||
const val DEEPLINK_KEY = "deeplink"
|
||||
const val WEBLINK_KEY = "link"
|
||||
|
||||
// TODO: Add tests
|
||||
/**
|
||||
* Provides functionality to handle deep links.
|
||||
*
|
||||
* Allows deep links to be launched, registered, or unregistered.
|
||||
*/
|
||||
interface DeepLinksRegistry {
|
||||
|
||||
/**
|
||||
* Finds matches registered deep links for the given [intent] and launches them.
|
||||
*
|
||||
* @return `true` if any deep link was received, `false` otherwise.
|
||||
*/
|
||||
fun launch(intent: Intent): Boolean
|
||||
|
||||
/**
|
||||
* Registers the given [deepLink].
|
||||
*/
|
||||
fun register(deepLink: DeepLink)
|
||||
|
||||
/**
|
||||
* Registers the given [deepLinks].
|
||||
*/
|
||||
fun register(deepLinks: Collection<DeepLink>)
|
||||
|
||||
/**
|
||||
* Unregisters the given [deepLinks].
|
||||
*/
|
||||
fun unregister(deepLinks: Collection<DeepLink>)
|
||||
|
||||
/**
|
||||
* Unregisters the given [deepLink].
|
||||
*/
|
||||
fun unregister(deepLink: DeepLink)
|
||||
|
||||
/**
|
||||
* Unregisters deep links with the given [ids].
|
||||
* */
|
||||
fun unregisterByIds(ids: Collection<String>)
|
||||
|
||||
/**
|
||||
* Triggers run last launched [Intent] with deeplink handlers that can handle delayed deeplink
|
||||
* of specific [deepLinkClass] after handle [Intent] clear that and second time no intent will be handled
|
||||
*/
|
||||
fun triggerDelayedDeeplink(deepLinkClass: Class<out DeepLink>)
|
||||
|
||||
fun cancelDelayedDeeplink()
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package com.tangem.core.deeplink
|
||||
|
||||
object DeeplinkConst {
|
||||
const val TANGEM_SCHEME = "tangem"
|
||||
const val WALLET_ID_KEY = "user_wallet_id"
|
||||
const val NETWORK_ID_KEY = "network_id"
|
||||
const val TYPE_KEY = "type"
|
||||
const val TOKEN_ID_KEY = "token_id"
|
||||
const val DERIVATION_PATH_KEY = "derivation_path"
|
||||
const val TRANSACTION_ID_KEY = "transaction_id"
|
||||
const val NAME_KEY = "name"
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
package com.tangem.core.deeplink.converter
|
||||
|
||||
import com.tangem.core.deeplink.DeeplinkConst.TANGEM_SCHEME
|
||||
|
||||
/**
|
||||
* Builder class for constructing deep links with a fluent interface.
|
||||
*/
|
||||
internal class DeepLinkBuilder {
|
||||
private var scheme: String = TANGEM_SCHEME
|
||||
private var action: String = ""
|
||||
private val pathParams: MutableList<String> = mutableListOf()
|
||||
private val queryParams: MutableMap<String, String> = mutableMapOf()
|
||||
|
||||
/**
|
||||
* Sets the scheme for the deep link (e.g., "tangem", "https")
|
||||
*/
|
||||
fun setScheme(scheme: String): DeepLinkBuilder {
|
||||
this.scheme = scheme
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the action for the deep link (e.g., "link", "wallet")
|
||||
*/
|
||||
fun setAction(action: String): DeepLinkBuilder {
|
||||
this.action = action
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a path parameter to the deep link
|
||||
*/
|
||||
fun addPathParam(param: String): DeepLinkBuilder {
|
||||
pathParams.add(param)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a query parameter to the deep link
|
||||
*/
|
||||
fun addQueryParam(key: String, value: String): DeepLinkBuilder {
|
||||
queryParams[key] = value
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the deep link URI string
|
||||
*/
|
||||
fun build(): String {
|
||||
val path = if (pathParams.isEmpty()) {
|
||||
action
|
||||
} else {
|
||||
"$action/${pathParams.joinToString("/")}"
|
||||
}
|
||||
|
||||
val queryString = if (queryParams.isEmpty()) {
|
||||
""
|
||||
} else {
|
||||
"?" + queryParams.entries.joinToString("&") { "${it.key}=${it.value}" }
|
||||
}
|
||||
|
||||
return "$scheme://$path$queryString"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
package com.tangem.core.deeplink.converter
|
||||
|
||||
import android.os.Bundle
|
||||
import com.tangem.common.routing.DeepLinkRoute
|
||||
import com.tangem.common.routing.DeepLinkScheme
|
||||
import com.tangem.core.deeplink.DEEPLINK_KEY
|
||||
import com.tangem.core.deeplink.DeeplinkConst.DERIVATION_PATH_KEY
|
||||
import com.tangem.core.deeplink.DeeplinkConst.NAME_KEY
|
||||
import com.tangem.core.deeplink.DeeplinkConst.NETWORK_ID_KEY
|
||||
import com.tangem.core.deeplink.DeeplinkConst.TOKEN_ID_KEY
|
||||
import com.tangem.core.deeplink.DeeplinkConst.TRANSACTION_ID_KEY
|
||||
import com.tangem.core.deeplink.DeeplinkConst.TYPE_KEY
|
||||
import com.tangem.core.deeplink.DeeplinkConst.WALLET_ID_KEY
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
object PayloadToDeeplinkConverter : Converter<Map<String, String>, String?> {
|
||||
|
||||
override fun convert(value: Map<String, String>): String? {
|
||||
return when {
|
||||
value[DEEPLINK_KEY] != null -> value[DEEPLINK_KEY]
|
||||
isTangemPushNotificationPayload(value) -> buildNotificationDeeplink(value)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun convertBundle(bundle: Bundle?): String? {
|
||||
if (bundle == null) return null
|
||||
val bundleDataMap = mutableMapOf<String, String>()
|
||||
for (key in bundle.keySet()) {
|
||||
val value = bundle.getString(key)
|
||||
if (value != null) {
|
||||
bundleDataMap[key] = value
|
||||
}
|
||||
}
|
||||
return convert(bundleDataMap)
|
||||
}
|
||||
|
||||
@Suppress("ReturnCount")
|
||||
private fun buildNotificationDeeplink(payload: Map<String, String>): String? {
|
||||
val type = payload[TYPE_KEY] ?: return null
|
||||
val networkId = payload[NETWORK_ID_KEY] ?: return null
|
||||
val tokenId = payload[TOKEN_ID_KEY] ?: return null
|
||||
val walletId = payload[WALLET_ID_KEY] ?: return null
|
||||
val derivationPath = payload[DERIVATION_PATH_KEY].orEmpty()
|
||||
val transactionId = payload[TRANSACTION_ID_KEY]
|
||||
val name = payload[NAME_KEY]
|
||||
|
||||
return DeepLinkBuilder().setScheme(DeepLinkScheme.Tangem.scheme).apply {
|
||||
setAction(DeepLinkRoute.TokenDetails.host)
|
||||
addQueryParam(NETWORK_ID_KEY, networkId)
|
||||
addQueryParam(TOKEN_ID_KEY, tokenId)
|
||||
addQueryParam(TYPE_KEY, type)
|
||||
addQueryParam(WALLET_ID_KEY, walletId)
|
||||
if (derivationPath.isNotBlank()) {
|
||||
addQueryParam(DERIVATION_PATH_KEY, derivationPath)
|
||||
}
|
||||
|
||||
transactionId?.let { addQueryParam(TRANSACTION_ID_KEY, it) }
|
||||
name?.let { addQueryParam(NAME_KEY, it) }
|
||||
}.build()
|
||||
}
|
||||
|
||||
private fun isTangemPushNotificationPayload(payload: Map<String, String>): Boolean {
|
||||
return payload.containsKey(TYPE_KEY) &&
|
||||
payload.containsKey(NETWORK_ID_KEY) &&
|
||||
payload.containsKey(TOKEN_ID_KEY) &&
|
||||
payload.containsKey(WALLET_ID_KEY)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package com.tangem.core.deeplink.di
|
||||
|
||||
import com.tangem.core.deeplink.DeepLinksRegistry
|
||||
import com.tangem.core.deeplink.impl.DefaultDeepLinksRegistry
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object DeepLinksModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideDeepLinksRegistry(): DeepLinksRegistry {
|
||||
return DefaultDeepLinksRegistry()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,201 +0,0 @@
|
|||
package com.tangem.core.deeplink.impl
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.core.net.toUri
|
||||
import com.tangem.core.deeplink.DEEPLINK_KEY
|
||||
import com.tangem.core.deeplink.DeepLink
|
||||
import com.tangem.core.deeplink.DeepLinksRegistry
|
||||
import timber.log.Timber
|
||||
|
||||
internal class DefaultDeepLinksRegistry : DeepLinksRegistry {
|
||||
|
||||
private var registries: List<DeepLink> = emptyList()
|
||||
private var lastDeepLink: Uri? = null
|
||||
|
||||
override fun launch(intent: Intent): Boolean {
|
||||
// Try to get deeplink from data (direct deeplink flow)
|
||||
// Otherwise, try to get from extras (notification deeplink flow)
|
||||
val deepLinkExtras = intent.getStringExtra(DEEPLINK_KEY)?.toUri()
|
||||
val received = intent.data ?: deepLinkExtras ?: return false
|
||||
lastDeepLink = received
|
||||
var hasMatch = false
|
||||
|
||||
Timber.i(
|
||||
"""
|
||||
Received deep link intent
|
||||
|- Received URI: $received
|
||||
|- Registries: $registries
|
||||
""".trimIndent(),
|
||||
)
|
||||
registries.forEach { deepLink ->
|
||||
val expected = deepLink.uri.toUri()
|
||||
|
||||
if (!isMatches(expected, received)) return@forEach
|
||||
hasMatch = true
|
||||
|
||||
val params = getParams(expected, received)
|
||||
|
||||
logMatch(hasMatch, expected, received, params)
|
||||
|
||||
deepLink.onReceive(params)
|
||||
lastDeepLink = null // clear deeplink if it was handled
|
||||
}
|
||||
|
||||
if (!hasMatch) {
|
||||
logMatch(hasMatch, null, received, null)
|
||||
}
|
||||
|
||||
return hasMatch
|
||||
}
|
||||
|
||||
override fun register(deepLinks: Collection<DeepLink>) {
|
||||
registries = (registries + deepLinks).distinctBy(DeepLink::id)
|
||||
|
||||
Timber.d(
|
||||
"""
|
||||
Registered deep links
|
||||
|- Registries: $registries
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
override fun register(deepLink: DeepLink) {
|
||||
registries = (registries + deepLink).distinctBy(DeepLink::id)
|
||||
|
||||
Timber.d(
|
||||
"""
|
||||
Registered deep link
|
||||
|- Registries: $registries
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
override fun unregister(deepLinks: Collection<DeepLink>) {
|
||||
registries = registries.filter { it !in deepLinks }
|
||||
|
||||
Timber.d(
|
||||
"""
|
||||
Unregistered deep links
|
||||
|- Registries: $registries
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
override fun unregister(deepLink: DeepLink) {
|
||||
registries = registries.filter { it.id != deepLink.id }
|
||||
|
||||
Timber.d(
|
||||
"""
|
||||
Unregistered deep link
|
||||
|- Registries: $registries
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
override fun unregisterByIds(ids: Collection<String>) {
|
||||
registries = registries.filter { it.id !in ids }
|
||||
|
||||
Timber.d(
|
||||
"""
|
||||
Unregistered deep links
|
||||
|- Registries: $registries
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
override fun triggerDelayedDeeplink(deepLinkClass: Class<out DeepLink>) {
|
||||
val received = lastDeepLink
|
||||
if (received != null) {
|
||||
var hasMatch = false
|
||||
registries
|
||||
.filterIsInstance(deepLinkClass)
|
||||
.forEach { deepLink ->
|
||||
if (!deepLink.shouldHandleDelayed) return@forEach
|
||||
val expected = deepLink.uri.toUri()
|
||||
if (!isMatches(expected, received)) return@forEach
|
||||
hasMatch = true
|
||||
|
||||
val params = getParams(expected, received)
|
||||
logMatch(hasMatch, expected, received, params)
|
||||
deepLink.onReceive(params)
|
||||
}
|
||||
|
||||
if (!hasMatch) {
|
||||
logMatch(hasMatch, null, received, null)
|
||||
}
|
||||
lastDeepLink = null // clear deeplink in any case handle or not
|
||||
}
|
||||
}
|
||||
|
||||
override fun cancelDelayedDeeplink() {
|
||||
lastDeepLink = null
|
||||
}
|
||||
|
||||
private fun logMatch(hasMatch: Boolean, expected: Uri?, received: Uri?, params: Map<String, String>?) {
|
||||
if (hasMatch) {
|
||||
Timber.i(
|
||||
"""
|
||||
Matched deep link
|
||||
|- Expected URI: $expected
|
||||
|- Received URI: $received
|
||||
|- Params: $params
|
||||
""".trimIndent(),
|
||||
)
|
||||
} else {
|
||||
Timber.i(
|
||||
"""
|
||||
No match found for deep link
|
||||
|- Received URI: $received
|
||||
|- Registries: $registries
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isMatches(received: Uri, expected: Uri): Boolean {
|
||||
if (received == expected) return true
|
||||
if (received.authority != expected.authority ||
|
||||
received.pathSegments.size != expected.pathSegments.size
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
received.pathSegments.forEachIndexed { index, receivedSegment ->
|
||||
val expectedSegment = expected.pathSegments[index]
|
||||
if (receivedSegment != expectedSegment &&
|
||||
!(receivedSegment.startsWith(prefix = "{") && receivedSegment.endsWith(suffix = "}"))
|
||||
) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun getParams(received: Uri, expected: Uri): Map<String, String> {
|
||||
val params = mutableMapOf<String, String>()
|
||||
|
||||
received.pathSegments.forEachIndexed { index, receivedSegment ->
|
||||
val expectedSegment = expected.pathSegments[index]
|
||||
if (receivedSegment != expectedSegment &&
|
||||
receivedSegment.startsWith(prefix = "{") &&
|
||||
receivedSegment.endsWith(suffix = "}")
|
||||
) {
|
||||
val path = receivedSegment
|
||||
.replace(oldValue = "{", newValue = "")
|
||||
.replace(oldValue = "}", newValue = "")
|
||||
|
||||
params[path] = expectedSegment
|
||||
}
|
||||
}
|
||||
|
||||
expected.queryParameterNames.forEach { paramName ->
|
||||
expected.getQueryParameter(paramName)?.let { param ->
|
||||
params[paramName] = param
|
||||
}
|
||||
}
|
||||
|
||||
return params
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
package com.tangem.core.deeplink.utils
|
||||
|
||||
import com.arkivanov.essenty.lifecycle.subscribe
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.deeplink.DeepLink
|
||||
import com.tangem.core.deeplink.DeepLinksRegistry
|
||||
|
||||
fun AppComponentContext.registerDeepLinks(registry: DeepLinksRegistry, vararg deepLinks: DeepLink) {
|
||||
registerDeepLinks(registry, deepLinks.toList())
|
||||
}
|
||||
|
||||
fun AppComponentContext.registerDeepLinks(registry: DeepLinksRegistry, deepLinks: Collection<DeepLink>) {
|
||||
lifecycle.subscribe(
|
||||
onCreate = {
|
||||
registry.register(deepLinks)
|
||||
},
|
||||
onDestroy = {
|
||||
registry.unregister(deepLinks)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
package com.tangem.core.deeplink.converter
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.core.deeplink.DeeplinkConst
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
internal class DeepLinkBuilderTest {
|
||||
|
||||
private lateinit var deepLinkBuilder: DeepLinkBuilder
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
deepLinkBuilder = DeepLinkBuilder()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN default builder WHEN build THEN should return default scheme`() {
|
||||
// WHEN
|
||||
val result = deepLinkBuilder.build()
|
||||
|
||||
// THEN
|
||||
assertThat(result).isEqualTo("${DeeplinkConst.TANGEM_SCHEME}://")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN custom scheme WHEN setScheme THEN should use custom scheme`() {
|
||||
// GIVEN
|
||||
val customScheme = "https"
|
||||
|
||||
// WHEN
|
||||
val result = deepLinkBuilder
|
||||
.setScheme(customScheme)
|
||||
.build()
|
||||
|
||||
// THEN
|
||||
assertThat(result).isEqualTo("$customScheme://")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN action WHEN setAction THEN should include action in path`() {
|
||||
// GIVEN
|
||||
val action = "wallet"
|
||||
|
||||
// WHEN
|
||||
val result = deepLinkBuilder
|
||||
.setAction(action)
|
||||
.build()
|
||||
|
||||
// THEN
|
||||
assertThat(result).isEqualTo("${DeeplinkConst.TANGEM_SCHEME}://$action")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN path params WHEN addPathParam THEN should include params in path`() {
|
||||
// GIVEN
|
||||
val action = "wallet"
|
||||
val param1 = "123"
|
||||
val param2 = "456"
|
||||
|
||||
// WHEN
|
||||
val result = deepLinkBuilder
|
||||
.setAction(action)
|
||||
.addPathParam(param1)
|
||||
.addPathParam(param2)
|
||||
.build()
|
||||
|
||||
// THEN
|
||||
assertThat(result).isEqualTo("${DeeplinkConst.TANGEM_SCHEME}://$action/$param1/$param2")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN query params WHEN addQueryParam THEN should include params in query string`() {
|
||||
// GIVEN
|
||||
val action = "wallet"
|
||||
val key1 = "param1"
|
||||
val value1 = "value1"
|
||||
val key2 = "param2"
|
||||
val value2 = "value2"
|
||||
|
||||
// WHEN
|
||||
val result = deepLinkBuilder
|
||||
.setAction(action)
|
||||
.addQueryParam(key1, value1)
|
||||
.addQueryParam(key2, value2)
|
||||
.build()
|
||||
|
||||
// THEN
|
||||
assertThat(result).isEqualTo("${DeeplinkConst.TANGEM_SCHEME}://$action?$key1=$value1&$key2=$value2")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN complex deep link WHEN build THEN should construct correct URI`() {
|
||||
// GIVEN
|
||||
val scheme = "https"
|
||||
val action = "wallet"
|
||||
val pathParam = "123"
|
||||
val queryKey = "token"
|
||||
val queryValue = "abc"
|
||||
|
||||
// WHEN
|
||||
val result = deepLinkBuilder
|
||||
.setScheme(scheme)
|
||||
.setAction(action)
|
||||
.addPathParam(pathParam)
|
||||
.addQueryParam(queryKey, queryValue)
|
||||
.build()
|
||||
|
||||
// THEN
|
||||
assertThat(result).isEqualTo("$scheme://$action/$pathParam?$queryKey=$queryValue")
|
||||
}
|
||||
}
|
||||
|
|
@ -1,148 +0,0 @@
|
|||
package com.tangem.core.deeplink.converter
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.core.deeplink.DEEPLINK_KEY
|
||||
import com.tangem.core.deeplink.DeeplinkConst.DERIVATION_PATH_KEY
|
||||
import com.tangem.core.deeplink.DeeplinkConst.NETWORK_ID_KEY
|
||||
import com.tangem.core.deeplink.DeeplinkConst.TOKEN_ID_KEY
|
||||
import com.tangem.core.deeplink.DeeplinkConst.TYPE_KEY
|
||||
import com.tangem.core.deeplink.DeeplinkConst.WALLET_ID_KEY
|
||||
import org.junit.Test
|
||||
|
||||
internal class PayloadToDeeplinkConverterTest {
|
||||
|
||||
@Test
|
||||
fun `GIVEN payload with deeplink key WHEN convert THEN should return deeplink value`() {
|
||||
// GIVEN
|
||||
val payload = mapOf(
|
||||
DEEPLINK_KEY to "tangem://token-details?networkId=ethereum&tokenId=0x123&type=token&user_wallet_id=wallet123" +
|
||||
"&derivation_path=m'0'0'0",
|
||||
)
|
||||
|
||||
// WHEN
|
||||
val result = PayloadToDeeplinkConverter.convert(payload)
|
||||
|
||||
// THEN
|
||||
assertThat(result).isEqualTo(
|
||||
"tangem://token-details?networkId=ethereum&tokenId=0x123&type=token&user_wallet_id=wallet123&derivation_path=m'0'0'0",
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN valid push notification payload with all vital values WHEN convert THEN should return correct deeplink`() {
|
||||
// GIVEN
|
||||
val payload = mapOf(
|
||||
TYPE_KEY to "token",
|
||||
NETWORK_ID_KEY to "ethereum",
|
||||
TOKEN_ID_KEY to "0x123",
|
||||
WALLET_ID_KEY to "wallet123",
|
||||
DERIVATION_PATH_KEY to "m'0'0'0",
|
||||
)
|
||||
|
||||
// WHEN
|
||||
val result = PayloadToDeeplinkConverter.convert(payload)
|
||||
|
||||
// THEN
|
||||
assertThat(result).isEqualTo(
|
||||
"tangem://token?network_id=ethereum&token_id=0x123&type=token&user_wallet_id=wallet123&derivation_path=m'0'0'0",
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN push notification payload without derivationPath WHEN convert THEN should return correct deeplink without derivation_path`() {
|
||||
// GIVEN
|
||||
val payload = mapOf(
|
||||
TYPE_KEY to "token",
|
||||
NETWORK_ID_KEY to "ethereum",
|
||||
TOKEN_ID_KEY to "0x123",
|
||||
WALLET_ID_KEY to "wallet123",
|
||||
)
|
||||
|
||||
// WHEN
|
||||
val result = PayloadToDeeplinkConverter.convert(payload)
|
||||
|
||||
// THEN
|
||||
assertThat(result).isEqualTo(
|
||||
"tangem://token?network_id=ethereum&token_id=0x123&type=token&user_wallet_id=wallet123",
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN push notification payload with missing type WHEN convert THEN should return null`() {
|
||||
// GIVEN
|
||||
val payload = mapOf(
|
||||
NETWORK_ID_KEY to "ethereum",
|
||||
TOKEN_ID_KEY to "0x123",
|
||||
WALLET_ID_KEY to "wallet123",
|
||||
DERIVATION_PATH_KEY to "m'0'0'0",
|
||||
)
|
||||
|
||||
// WHEN
|
||||
val result = PayloadToDeeplinkConverter.convert(payload)
|
||||
|
||||
// THEN
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN push notification payload with missing networkId WHEN convert THEN should return null`() {
|
||||
// GIVEN
|
||||
val payload = mapOf(
|
||||
TYPE_KEY to "token",
|
||||
TOKEN_ID_KEY to "0x123",
|
||||
WALLET_ID_KEY to "wallet123",
|
||||
DERIVATION_PATH_KEY to "m'0'0'0",
|
||||
)
|
||||
|
||||
// WHEN
|
||||
val result = PayloadToDeeplinkConverter.convert(payload)
|
||||
|
||||
// THEN
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN push notification payload with missing tokenId WHEN convert THEN should return null`() {
|
||||
// GIVEN
|
||||
val payload = mapOf(
|
||||
TYPE_KEY to "token",
|
||||
NETWORK_ID_KEY to "ethereum",
|
||||
WALLET_ID_KEY to "wallet123",
|
||||
DERIVATION_PATH_KEY to "m'0'0'0",
|
||||
)
|
||||
|
||||
// WHEN
|
||||
val result = PayloadToDeeplinkConverter.convert(payload)
|
||||
|
||||
// THEN
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN push notification payload with missing walletId WHEN convert THEN should return null`() {
|
||||
// GIVEN
|
||||
val payload = mapOf(
|
||||
TYPE_KEY to "token",
|
||||
NETWORK_ID_KEY to "ethereum",
|
||||
TOKEN_ID_KEY to "0x123",
|
||||
)
|
||||
|
||||
// WHEN
|
||||
val result = PayloadToDeeplinkConverter.convert(payload)
|
||||
|
||||
// THEN
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN empty payload WHEN convert THEN should return null`() {
|
||||
// GIVEN
|
||||
val payload = emptyMap<String, String>()
|
||||
|
||||
// WHEN
|
||||
val result = PayloadToDeeplinkConverter.convert(payload)
|
||||
|
||||
// THEN
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources>
|
||||
<string name="access_code_alert_skip_description">Ohne Zugangscode ist Deine Wallet nicht geschützt.</string>
|
||||
<string name="access_code_alert_skip_ok">Trotzdem überspringen</string>
|
||||
<string name="access_code_alert_skip_title">Zugangscode nicht festgelegt</string>
|
||||
<string name="access_code_check_title">Zugangscode eingeben</string>
|
||||
<string name="access_code_confirm_description">Bestätige Deinen zuvor eingegebenen Code, um fortzufahren</string>
|
||||
<string name="access_code_confirm_title">Zugangscode erneut eingeben</string>
|
||||
<string name="access_code_create_description">Lege einen %s -stelligen Zugangscode fest, um Deine Wallet zu entsperren.</string>
|
||||
<string name="access_code_create_title">Zugangscode erstellen</string>
|
||||
<string name="access_code_navtitle">Zugangscode</string>
|
||||
<string name="action_buttons_buy_empty_search_message">Du kannst Deinen Token nicht finden? Gehe zum Bereich „Markt“ auf der Hauptseite und fügen diesen zum kaufen im Portfolio hinzu.</string>
|
||||
<string name="action_buttons_sell_empty_search_message">Du kannst Deinen Token nicht finden? Gehe zum Bereich „Markt“ auf der Hauptseite und fügen diesen zum verkaufen im Portfolio hinzu.</string>
|
||||
<string name="action_buttons_sell_navigation_bar_title">Verkaufen</string>
|
||||
|
|
@ -49,6 +58,19 @@
|
|||
<string name="app_settings_theme_mode_system">Systemstandard</string>
|
||||
<string name="app_settings_theme_selector_title">Thema</string>
|
||||
<string name="app_settings_title">App Einstellungen</string>
|
||||
<string name="backup_complete_description">Du hast Deine Wallet erfolgreich gesichert.</string>
|
||||
<string name="backup_complete_seed_description">Diese Wörter können bei Verlust nicht wiederhergestellt werden. Bewahre diese an einem sicheren Ort auf.</string>
|
||||
<string name="backup_complete_title">Sicherung abgeschlossen</string>
|
||||
<string name="backup_info_description">Deine geheime Wiederherstellungsphrase ist ein fester Satz von %s Zufällige Wörter, die für den Zugriff auf und die Wiederherstellung Deiner Wallet verwendet werden.</string>
|
||||
<string name="backup_info_keep_description">Diese Wörter können bei Verlust nicht wiederhergestellt werden. Bewahre diese an einem sicheren Ort auf.</string>
|
||||
<string name="backup_info_keep_title">Sicher aufbewahren</string>
|
||||
<string name="backup_info_save_description">Bewahre diese %s Wörter an einem sicheren Ort auf , z. B. in einem Passwort-Manager, und teile diese niemals mit Dritten.</string>
|
||||
<string name="backup_info_save_title">Keine Wiederherstellung möglich</string>
|
||||
<string name="backup_info_title">Wiederherstellungsphrase</string>
|
||||
<string name="backup_seed_caution">Gebe diese Wörter niemals weiter. Jeder, der sie erfährt, kann Deine gesamten Kryptowährungen stehlen. Tangem wird Dich niemals danach fragen. Die folgenden %s Wörter sind die Wiederherstellungsphrase Deiner Wallets. Mit dieser Phrase kannst Du Deine Wallet wiederherstellen, falls Du Dein Gerät verlierst.</string>
|
||||
<string name="backup_seed_description">Schreibe diese %s Wörter der Reihe nach auf und bewahre sie sicher und vertraulich auf</string>
|
||||
<string name="backup_seed_responsibility">Die volle Verantwortung für die Sicherheit und Sicherung der Wallet und der Wiederherstellungsphrase liegt beim Benutzer, nicht bei Tangem.</string>
|
||||
<string name="backup_seed_title">Wiederherstellungsphrase</string>
|
||||
<string name="balance_hidden_description">Um deine Kontostände ein- oder auszublenden, flippe einfach das Display deines Geräts nach unten, oder schalte es in den Einstellungen aus.</string>
|
||||
<string name="balance_hidden_do_not_show_button">Nicht mehr anzeigen</string>
|
||||
<string name="balance_hidden_got_it_button">Verstanden</string>
|
||||
|
|
@ -103,6 +125,7 @@
|
|||
<string name="common_approval">Genehmigung</string>
|
||||
<string name="common_approve">Genehmigen</string>
|
||||
<string name="common_attention">Achtung</string>
|
||||
<string name="common_backup">Sicherungskopie</string>
|
||||
<string name="common_balance">Bilanz: %s</string>
|
||||
<string name="common_balance_title">Saldo</string>
|
||||
<string name="common_biometric_authentication">biometrische Authentifizierung</string>
|
||||
|
|
@ -119,10 +142,12 @@
|
|||
<string name="common_claim">Beanspruchen</string>
|
||||
<string name="common_claim_rewards">Stakingbelohnungen beanspruchen</string>
|
||||
<string name="common_close">Schließen</string>
|
||||
<string name="common_coming_soon">Demnächst verfügbar</string>
|
||||
<string name="common_confirm">Bestätigen</string>
|
||||
<string name="common_contact_tangem_support">Kontakt zum Tangem-Support</string>
|
||||
<string name="common_contact_visa_support">Kontakt zum Visa-Support</string>
|
||||
<string name="common_continue">Weiter</string>
|
||||
<string name="common_convert">Umwandeln</string>
|
||||
<string name="common_copy">Kopieren</string>
|
||||
<string name="common_copy_address">Adresse kopieren</string>
|
||||
<string name="common_create">Erstellen</string>
|
||||
|
|
@ -154,6 +179,7 @@
|
|||
<string name="common_fee_selector_option_slow">Langsam</string>
|
||||
<string name="common_fee_selector_title">Gebühren</string>
|
||||
<string name="common_finish">Beenden</string>
|
||||
<string name="common_free">Kostenlos</string>
|
||||
<string name="common_generate_addresses">Adressen synchronisieren</string>
|
||||
<string name="common_go_to_provider">Zum Anbieter gehen</string>
|
||||
<string name="common_go_to_token">Zum Token</string>
|
||||
|
|
@ -206,6 +232,7 @@
|
|||
<string name="common_show_more">Mehr anzeigen</string>
|
||||
<string name="common_sign">Signieren</string>
|
||||
<string name="common_sign_and_send">Signieren und senden</string>
|
||||
<string name="common_skip">Überspringen</string>
|
||||
<string name="common_stake">Staken</string>
|
||||
<string name="common_staking">Staking</string>
|
||||
<string name="common_start">Start</string>
|
||||
|
|
@ -226,6 +253,7 @@
|
|||
<string name="common_unreachable">Nicht erreichbar</string>
|
||||
<string name="common_unstake">Staking beenden</string>
|
||||
<string name="common_utxo_validate_withdrawal_message_warning">Aufgrund der Beschränkungen von %1$s können nur %2$d UTXOs in eine einzige Transaktion passen. Das bedeutet, dass du nur %3$s oder weniger senden kannst. Du musst den Betrag reduzieren.</string>
|
||||
<string name="common_value_copied">Wert kopiert</string>
|
||||
<string name="common_week">woche</string>
|
||||
<string name="common_with">mit</string>
|
||||
<string name="common_yes">Ja</string>
|
||||
|
|
@ -308,7 +336,7 @@
|
|||
<string name="express_exchange_notification_verification_title">KYC-Überprüfung durch den Anbieter erforderlich</string>
|
||||
<string name="express_exchange_status_bought">Gekauft</string>
|
||||
<string name="express_exchange_status_buying">Einkaufen</string>
|
||||
<string name="express_exchange_status_buying_active">Einkaufen...</string>
|
||||
<string name="express_exchange_status_buying_active">Warten auf Kauf...</string>
|
||||
<string name="express_exchange_status_canceled">Transaktion abgebrochen</string>
|
||||
<string name="express_exchange_status_confirmed">Anzahlung bestätigt</string>
|
||||
<string name="express_exchange_status_confirming">Warten auf Bestätigung</string>
|
||||
|
|
@ -386,6 +414,23 @@
|
|||
<string name="home_button_scan">Karte oder Ring scannen</string>
|
||||
<string name="hot_crypto_add_token_subtitle">An %s</string>
|
||||
<string name="hot_crypto_token_network">Im %s Netzwerk</string>
|
||||
<string name="hw_activation_need_backup">Jetzt sichern</string>
|
||||
<string name="hw_activation_need_description">Um die Einrichtung abzuschließen, sicher Deine Wallet und sicher den App-Zugriff mit einem Access Code.</string>
|
||||
<string name="hw_activation_need_finish">Jetzt beenden</string>
|
||||
<string name="hw_activation_need_title">Wallet-Aktivierung abschließen</string>
|
||||
<string name="hw_backup_icloud_description">Stelle eine vorhandene Wallet wieder her, die in Deinem iCloud-Backup gespeichert ist</string>
|
||||
<string name="hw_backup_icloud_title">iCloud-Sicherung</string>
|
||||
<string name="hw_backup_need_action">Gehe zum Backup</string>
|
||||
<string name="hw_backup_need_description">Um Deine Wallet mit einem Zugangscode zu sichern, führe zuerst die Sicherung durch.</string>
|
||||
<string name="hw_backup_need_title">Sicherung zuerst beenden</string>
|
||||
<string name="hw_backup_no_backup">Kein Backup</string>
|
||||
<string name="hw_backup_seed_description">Physische Karten, die Deine Kryptowährung sicher offline speichern.</string>
|
||||
<string name="hw_backup_seed_title">Wiederherstellungs-Phrase</string>
|
||||
<string name="hw_create_keys_description">Über eingehende Transaktionen benachrichtigt werden</string>
|
||||
<string name="hw_create_keys_title">Schlüssel werden in der App gespeichert</string>
|
||||
<string name="hw_create_seed_description">Bleibe über die neuesten Funktionen und Neuigkeiten auf dem Laufenden</string>
|
||||
<string name="hw_create_seed_title">Sicherung der Seed-Phrase</string>
|
||||
<string name="hw_create_title">Mobile Wallet erstellen</string>
|
||||
<string name="information_generated_with_ai">Diese Informationen wurden mithilfe von KI generiert.\nTippe hier, wenn Du Fehler finden.</string>
|
||||
<string name="initial_message_change_access_code_body">Um den Zugangscode zu ändern, halte die Karte oder Ring wie oben gezeigt an das Gerät und entferne sie erst am Ende des Vorgangs.</string>
|
||||
<string name="initial_message_change_passcode_body">Um den Passcode zu ändern, halte die Karte oder Ring wie oben gezeigt an das Gerät und entferne sie erst am Ende des Vorgangs.</string>
|
||||
|
|
@ -631,6 +676,7 @@
|
|||
<string name="onboarding_create_wallet_options_title">Schlüssel anonym generieren</string>
|
||||
<string name="onboarding_done_body">Deine Karte oder Ring ist aktiviert und einsatzbereit</string>
|
||||
<string name="onboarding_done_header">Erfolgreich!</string>
|
||||
<string name="onboarding_done_wallet">Deine Wallet ist eingerichtet und einsatzbereit!</string>
|
||||
<string name="onboarding_exit_alert_message">In diesem Fall musst du ganz von vorne anfangen.</string>
|
||||
<string name="onboarding_exit_alert_title">Möchtest du den Aktivierungsprozess abbrechen?</string>
|
||||
<string name="onboarding_getting_started">Erste Schritte</string>
|
||||
|
|
@ -797,7 +843,9 @@
|
|||
<string name="send_alert_fee_too_low_text">Du hast eine Kommission angegeben, die unter dem empfohlenen Betrag liegt, was zu einer Verzögerung Ihrer Transaktion führen könnte. Weiter?</string>
|
||||
<string name="send_alert_transaction_failed_text">Grund: %1$s\nCode: %2$s</string>
|
||||
<string name="send_alert_transaction_failed_title">Die Transaktion ist nicht abgeschlossen</string>
|
||||
<string name="send_amount_convert_to_another_token">In ein anderes Token umwandeln</string>
|
||||
<string name="send_amount_label">Betrag</string>
|
||||
<string name="send_amount_receive_token_subtitle">Wird an den Empfänger gesendet</string>
|
||||
<string name="send_bitcoin_custom_fee_footer">Du kannst deine Transaktionsgebühr festlegen, indem du den Wert im Feld Satoshi pro vByte anpasst.</string>
|
||||
<string name="send_custom_amount_fee_footer">Die Gebühr, die für deine Transaktion erhoben wird. Du kannst einen eigenen Wert festlegen.</string>
|
||||
<string name="send_custom_evm_max_fee">Maximale Gebühr</string>
|
||||
|
|
@ -843,6 +891,7 @@
|
|||
<string name="send_notification_fee_too_high_title">Die individuelle Gebühr ist hoch</string>
|
||||
<string name="send_notification_high_fee_text">Aufgrund der Besonderheiten des Netzes %1$s ist die Gebühr für die Überweisung des gesamten Guthabens höher. Um die Kommission zu reduzieren, Kannst du %2$s verlassen.</string>
|
||||
<string name="send_notification_high_fee_title">Die Gebühr ist höher</string>
|
||||
<string name="send_notification_invalid_amount_rent_destination">Das Empfängerkonto ist nicht aktiviert. Der Mindestbetrag der Übertragung muss gleich oder höher sein als der mietfreie Restbetrag: %1$s.</string>
|
||||
<string name="send_notification_invalid_amount_rent_fee">Dein Kontostand darf nicht niedriger sein als die Miete. Bitte halte mindestens %1$s auf Deinem Konto oder ziehe den gesamten Betrag ab.</string>
|
||||
<string name="send_notification_invalid_amount_text">Die enthaltene Kommission übersteigt den Überweisungsbetrag, was zu einem negativen Wert führt</string>
|
||||
<string name="send_notification_invalid_amount_title">Ungültige Menge</string>
|
||||
|
|
@ -861,7 +910,7 @@
|
|||
<string name="send_recent_transactions">Jüngste</string>
|
||||
<string name="send_recipient">Empfänger</string>
|
||||
<string name="send_recipient_address_error">Keine gültige Adresse</string>
|
||||
<string name="send_recipient_address_footer">Vergewisser dich, dass die empfangende Wallet-Adresse im %s Netzwerk ist, damit du deine Token nicht verlierst.</string>
|
||||
<string name="send_recipient_address_footer">Stelle sicher, dass sich die empfangende Wallet-Adresse im ** %s -Netzwerk** befindet, um den Verlust Deiner Token zu vermeiden.</string>
|
||||
<string name="send_recipient_address_footer_highlighted_part">senden an %s</string>
|
||||
<string name="send_recipient_address_footer_v2">Stelle sicher, dass Du %s Netzwerkadresse hast, da Fehler zu verlorenen Übertragungen führen können</string>
|
||||
<string name="send_recipient_label">Senden an</string>
|
||||
|
|
@ -889,8 +938,10 @@
|
|||
<string name="send_validation_invalid_amount">Ungültiger Betrag</string>
|
||||
<string name="send_validation_invalid_fee">Die Gebühr geht über die Bilanz hinaus</string>
|
||||
<string name="send_validation_invalid_total">Der Gesamtbetrag geht über die Bilanz hinaus</string>
|
||||
<string name="send_with_swap_confirm_title">Tauschen und senden</string>
|
||||
<string name="send_with_swap_notification_text">Sende uns ein Token, und wir konvertieren es unterwegs. Dein Empfänger erhält genau das, was er braucht – nahtlos.</string>
|
||||
<string name="send_with_swap_recipient_amount_text">Wird an den Empfänger gesendet</string>
|
||||
<string name="send_with_swap_recipient_amount_title">Zu erhaltender Betrag</string>
|
||||
<string name="send_with_swap_title">Senden mit Swap</string>
|
||||
<string name="sent_transaction_sent_title">Transaktion gesendet</string>
|
||||
<string name="settings_card_settings_footer">Bereite das Scannen der Karte oder Ring vor, die du einrichten möchtest.</string>
|
||||
|
|
@ -1046,6 +1097,7 @@
|
|||
<string name="swap_story_second_title">Unschlagbare Preise</string>
|
||||
<string name="swap_story_third_subtitle">Problemlos und intuitiv, sodass Deine Token mit nur wenigen Handgriffen getauscht werden können</string>
|
||||
<string name="swap_story_third_title">Einfach bequem</string>
|
||||
<string name="swap_via_provider">Tausch über Anbieter</string>
|
||||
<string name="swapping_alert_cex_description">Der Betrag umfasst:\n- Gebühr des Dienstanbieters\n- Netzgebühr für die Rücksendung von %s von der Vermittlungsstelle an die Adresse des Nutzers.</string>
|
||||
<string name="swapping_alert_cex_description_with_slippage">Der Betrag beinhaltet:\n• Honorar des Dienstleisters\n• Netzwerkgebühr für das Senden %1$s von der Börse zurück an die Adresse des Benutzers. \n\n Provider-Slippage kann bis zu %2$s</string>
|
||||
<string name="swapping_alert_dex_description">Der Betrag enthält die Gebühren des Dienstleisters.</string>
|
||||
|
|
@ -1153,6 +1205,7 @@
|
|||
<string name="visa_onboarding_in_progress_description">Das wird nicht lange dauern. Wir richten gerade Dein Konto ein.</string>
|
||||
<string name="visa_onboarding_in_progress_issuer_description">Das wird nicht lange dauern. Wir schließen die Aktivierung ab.</string>
|
||||
<string name="visa_onboarding_in_progress_title">Alles wird vorbereitet!</string>
|
||||
<string name="visa_onboarding_pin_code_title">PIN-Code erstellen</string>
|
||||
<string name="visa_onboarding_pin_not_accepted">PIN wurde nicht akzeptiert. Versuche es erneut oder verwende einen anderen Code.</string>
|
||||
<string name="visa_onboarding_pin_validation_error_message">Ungültige PIN: Sequenzen oder Wiederholungen vermeiden</string>
|
||||
<string name="visa_onboarding_success_screen_description">Schon kann es losgehen!</string>
|
||||
|
|
@ -1201,6 +1254,29 @@
|
|||
<string name="wallet_connect_subtitle">Mit dApps verbinden</string>
|
||||
<string name="wallet_connect_title">WalletConnect</string>
|
||||
<string name="wallet_connect_toast_awaiting_session_proposal">Das Herstellen der Verbindung kann einige Sekunden dauern</string>
|
||||
<string name="wallet_create_hardware_badge">Von %s</string>
|
||||
<string name="wallet_create_hardware_description">Kaufe eine Tangem Wallet – physische Karten, die Deine Kryptowährungen sicher offline speichern.</string>
|
||||
<string name="wallet_create_hardware_title">Hardware-Wallet</string>
|
||||
<string name="wallet_create_mobile_description">Eine sichere Wallet wird in Sekundenschnelle auf Deinem Telefon erstellt.</string>
|
||||
<string name="wallet_create_mobile_title">Mobile Wallet</string>
|
||||
<string name="wallet_create_nav_info_title">Was soll ich wählen?</string>
|
||||
<string name="wallet_create_scan_question">Besitzt Du bereits eine Tangem Wallet?</string>
|
||||
<string name="wallet_create_scan_title">Jetzt scannen</string>
|
||||
<string name="wallet_create_title">Wähle, wie Du Deine Wallet erstellen möchten</string>
|
||||
<string name="wallet_import_buy_question">Möchtest Du eine Tangem-Wallet kaufen?</string>
|
||||
<string name="wallet_import_buy_title">Jetzt kaufen</string>
|
||||
<string name="wallet_import_google_drive_description">Eine vorhandene Wallet wiederherstellen, die Deinem Google Drive-Backup gespeichert ist</string>
|
||||
<string name="wallet_import_google_drive_title">Importieren aus Google Drive</string>
|
||||
<string name="wallet_import_navtitle">Vorhandenes Wallet hinzufügen</string>
|
||||
<string name="wallet_import_scan_description">Physische Karten, die Deine Kryptowährung sicher offline speichern.</string>
|
||||
<string name="wallet_import_scan_title">Scanne eine Tangem-Wallet</string>
|
||||
<string name="wallet_import_seed_description">Deine Seed-Phrase, um Deine Wallet wiederherzustellen</string>
|
||||
<string name="wallet_import_seed_navtitle">Wallet importieren</string>
|
||||
<string name="wallet_import_seed_title">Importieren einer Wiederherstellungsphrase</string>
|
||||
<string name="wallet_import_success_description">Du hast Deine Wallet erfolgreich gesichert.</string>
|
||||
<string name="wallet_import_success_navtitle">Wallet importieren</string>
|
||||
<string name="wallet_import_success_title">Import abgeschlossen</string>
|
||||
<string name="wallet_import_title">Wallet importieren</string>
|
||||
<string name="wallet_marketplace_block_title">%s Marktpreis</string>
|
||||
<string name="wallet_marketprice_block_update_time">letzte 24h</string>
|
||||
<string name="wallet_network_group_title">%s Netzwerk</string>
|
||||
|
|
@ -1209,6 +1285,8 @@
|
|||
<string name="wallet_promo_banner_button_title">Jetzt mit 10 % Rabatt kaufen</string>
|
||||
<string name="wallet_promo_banner_description">Greife auf 13.000+ Kryptowährungen zu. Kaufe, verkaufe, tausche und stake Sie mit einem einzigen Fingertipp.\nVerbinde bis zu drei Karten für ein Backup.</string>
|
||||
<string name="wallet_promo_banner_title">Entdecke die Tangem Wallet</string>
|
||||
<string name="wallet_settings_access_code_description">Geheimcode zum Schutz dieser Wallet. Wird für Anmeldung und Signaturen verwendet.</string>
|
||||
<string name="wallet_settings_access_code_title">Zugangscode festlegen/ändern</string>
|
||||
<string name="wallet_settings_push_notifications_description">Bleib stets über eingehende Wallet-Transaktionen und Tangem-Updates auf dem Laufenden.</string>
|
||||
<string name="wallet_settings_push_notifications_title">Transaktionsbenachrichtigungen</string>
|
||||
<string name="wallet_settings_title">Wallet-Einstellungen</string>
|
||||
|
|
@ -1238,7 +1316,7 @@
|
|||
<string name="warning_express_approval_in_progress_message">Die Genehmigung des Swaps ist im Gange und wird in Kürze abgeschlossen sein.</string>
|
||||
<string name="warning_express_approval_in_progress_title">Genehmigung in Arbeit</string>
|
||||
<string name="warning_express_dust_message">Der Mindestbetrag für den Tausch beträgt %1$s. Bitte stelle sicher, dass der Restsaldo nach dem Swap nicht unter %2$s liegt.</string>
|
||||
<string name="warning_express_no_exchangeable_coins_description">Du hast keine %s austauschbaren Coins in Deiner Liste</string>
|
||||
<string name="warning_express_no_exchangeable_coins_description">Sie haben keine Token in Ihrem Portfolio, gegen die %s getauscht werden kann. Bitte fügen Sie einen anderen Token hinzu, um den Tausch durchzuführen.</string>
|
||||
<string name="warning_express_no_exchangeable_coins_title">Keine Token zum Tauschen verfügbar</string>
|
||||
<string name="warning_express_not_enough_fee_for_token_tx_description">Um eine Transaktion durchzuführen, du etwas etwas einzahlen %1$s %2$s</string>
|
||||
<string name="warning_express_not_enough_fee_for_token_tx_title">Die Gebühr %s kann nicht gedeckt werden</string>
|
||||
|
|
@ -1298,13 +1376,15 @@
|
|||
<string name="warning_some_networks_unreachable_message">Wischen Sie nach unten, um zu aktualisieren, oder versuchen Sie es später erneut.</string>
|
||||
<string name="warning_some_networks_unreachable_title">Einige Netzwerke sind nicht erreichbar</string>
|
||||
<string name="warning_some_token_balances_not_updated">Einige Token-Guthaben konnten nicht aktualisiert werden</string>
|
||||
<string name="warning_stellar_token_trustline_not_enough_xlm">Nicht genug %s. Lade Dein XLM-Konto auf, um dieses Token zu verknüpfen</string>
|
||||
<string name="warning_testnet_card_message">Dies ist eine Testnet-Karte. Sie kann keine Transaktionen verarbeiten und sollte nur zu Test- und Entwicklungszwecken verwendet werden.</string>
|
||||
<string name="warning_testnet_card_title">Nur für Testzwecke</string>
|
||||
<string name="warning_token_balance_not_updated">Der Kontostand ist möglicherweise veraltet. Aktualisiere bittre die Seite.</string>
|
||||
<string name="warning_token_required_min_coin_reserve">Nicht genug %1$s. Lade Dein XLM-Konto auf, um dieses Token zu verknüpfen</string>
|
||||
<string name="warning_token_required_min_coin_reserve">Nicht genügend %1$s. Lade Dein %2$s -Konto auf, um dieses Token zu verknüpfen</string>
|
||||
<string name="warning_token_trustline_button_title">Trustline aktivieren</string>
|
||||
<string name="warning_token_trustline_subtitle">Um dieses Token zu erhalten, muss eine Trustline aktiviert sein. Das Netzwerk benötigt eine Reserve von %1$s %2$s.</string>
|
||||
<string name="warning_token_trustline_title">Trustline erforderlich</string>
|
||||
<string name="wc_alert_audit_malicious_domain">Bösartige/ verdächtige Domäne</string>
|
||||
<string name="wc_alert_audit_unknown_domain">Unbekannte Domäne</string>
|
||||
<string name="wc_alert_connect_anyway">Trotzdem verbinden</string>
|
||||
<string name="wc_alert_connection_timeout_description">Zeitüberschreitungsfehler. Bitte versuche es später erneut.</string>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<string name="action_buttons_service_loading_alert_message">Esto puede tardar unos segundos. Inténtelo de nuevo más tarde.</string>
|
||||
<string name="action_buttons_service_loading_alert_title">Los datos aún no se han cargado.</string>
|
||||
<string name="action_buttons_something_wrong_alert_message">La acción no está disponible actualmente. Inténtalo de nuevo más tarde o actualiza los datos deslizando el dedo hacia abajo en la pantalla.</string>
|
||||
<string name="action_buttons_something_wrong_alert_title">No disponible</string>
|
||||
<string name="action_buttons_something_wrong_alert_title">La acción no está disponible</string>
|
||||
<string name="action_buttons_swap_choose_token">Elige el token</string>
|
||||
<string name="action_buttons_swap_empty_search_message">¿No encuentra el token en tu billetera? Consulte los mercados para encontrarlo y agregarlo al intercambio</string>
|
||||
<string name="action_buttons_swap_no_available_pair_notification_message">No hay tokens disponibles para intercambiar con el token seleccionado. Por favor elige otro.</string>
|
||||
|
|
@ -49,6 +49,17 @@
|
|||
<string name="app_settings_theme_mode_system">Predeterminado del sistema</string>
|
||||
<string name="app_settings_theme_selector_title">Tema</string>
|
||||
<string name="app_settings_title">Ajustes de la app</string>
|
||||
<string name="backup_complete_description">Copia de seguridad de tu billetera realizada con éxito.</string>
|
||||
<string name="backup_complete_title">Copia de seguridad completada</string>
|
||||
<string name="backup_info_description">Su frase secreta de recuperación es un conjunto fijo de %s palabras aleatorias que se utilizan para acceder a tu billetera y recuperarla.</string>
|
||||
<string name="backup_info_keep_description">Estas palabras no pueden recuperarse si se pierden. Asegúrate de guardarlas en un lugar seguro.</string>
|
||||
<string name="backup_info_keep_title">Mantenlo seguro</string>
|
||||
<string name="backup_info_save_description">Guarda estas %s palabras en un lugar seguro, como un administrador de contraseñas, y nunca las compartas con nadie.</string>
|
||||
<string name="backup_info_save_title">No hay recuperación posible</string>
|
||||
<string name="backup_info_title">Frase de recuperación</string>
|
||||
<string name="backup_seed_description">Escribe estas palabras %s en orden y guárdalas en un lugar seguro y privado</string>
|
||||
<string name="backup_seed_responsibility">La responsabilidad total de la seguridad y la copia de seguridad de la billetera y la frase de recuperación recae en el usuario, no en Tangem.</string>
|
||||
<string name="backup_seed_title">Frase de recuperación</string>
|
||||
<string name="balance_hidden_description">Para ocultar o mostrar tus saldos, simplemente gira la pantalla de tu dispositivo hacia abajo, o desactívalo en Ajustes</string>
|
||||
<string name="balance_hidden_do_not_show_button">No mostrar de nuevo</string>
|
||||
<string name="balance_hidden_got_it_button">Entendido</string>
|
||||
|
|
@ -65,6 +76,10 @@
|
|||
<item quantity="one">%d tarjeta</item>
|
||||
<item quantity="other">%d tarjetas</item>
|
||||
</plurals>
|
||||
<plurals name="card_label_token_count">
|
||||
<item quantity="one">%s token</item>
|
||||
<item quantity="other">%s tokens</item>
|
||||
</plurals>
|
||||
<string name="card_settings_access_code_recovery_disabled_description">Desactive esta opción si no quiere que esta tarjeta se use para reiniciar códigos de acceso en otras tarjetas de esta billetera. Tenga en cuenta que esto también evitará que reinicie el código de acceso en esta tarjeta.</string>
|
||||
<string name="card_settings_access_code_recovery_enabled_description">Permite usar esta tarjeta para reiniciar el código de acceso en otras tarjetas de esta billetera</string>
|
||||
<string name="card_settings_access_code_recovery_title">Recuperación de código de acceso</string>
|
||||
|
|
@ -110,12 +125,17 @@
|
|||
<string name="common_cancel">Cancelar</string>
|
||||
<string name="common_change">Cambie</string>
|
||||
<string name="common_choose_action">Elige una acción</string>
|
||||
<string name="common_choose_network">Elegir red</string>
|
||||
<string name="common_choose_token">Elige token</string>
|
||||
<string name="common_claim">Reclamar</string>
|
||||
<string name="common_claim_rewards">Reclame recompensas</string>
|
||||
<string name="common_close">Cerrar</string>
|
||||
<string name="common_coming_soon">Próximamente</string>
|
||||
<string name="common_confirm">Confirme</string>
|
||||
<string name="common_contact_tangem_support">Contacte con el soporte de Tangem</string>
|
||||
<string name="common_contact_visa_support">Contacte con el soporte de Visa</string>
|
||||
<string name="common_continue">Continuar</string>
|
||||
<string name="common_convert">Convertir</string>
|
||||
<string name="common_copy">Copiar</string>
|
||||
<string name="common_copy_address">Copiar la dirección</string>
|
||||
<string name="common_create">Crear</string>
|
||||
|
|
@ -150,6 +170,7 @@
|
|||
<string name="common_generate_addresses">Sincronizar direcciones</string>
|
||||
<string name="common_go_to_provider">Ir al proveedor</string>
|
||||
<string name="common_go_to_token">Ir al token</string>
|
||||
<string name="common_got_it">Entiendo</string>
|
||||
<string name="common_hide">Ocultar</string>
|
||||
<string name="common_hour">hora</string>
|
||||
<string name="common_import">Importe</string>
|
||||
|
|
@ -194,6 +215,7 @@
|
|||
<string name="common_server_unavailable">El servidor no está disponible, por favor inténtelo de nuevo más tarde</string>
|
||||
<string name="common_share">Compartir</string>
|
||||
<string name="common_share_link">Compartir enlace</string>
|
||||
<string name="common_show_less">Mostrar menos</string>
|
||||
<string name="common_show_more">Mostrar más</string>
|
||||
<string name="common_sign">Firme</string>
|
||||
<string name="common_sign_and_send">Firme y envíe</string>
|
||||
|
|
@ -203,6 +225,7 @@
|
|||
<string name="common_submit">Enviar</string>
|
||||
<string name="common_success">Con éxito</string>
|
||||
<string name="common_support">Soporte</string>
|
||||
<string name="common_supported_networks">Redes compatibles</string>
|
||||
<string name="common_swap">Swap</string>
|
||||
<string name="common_terms_and_conditions">términos y condiciones</string>
|
||||
<string name="common_terms_of_use">Condiciones de uso</string>
|
||||
|
|
@ -216,6 +239,7 @@
|
|||
<string name="common_unreachable">Inaccesible</string>
|
||||
<string name="common_unstake">Termine el staking</string>
|
||||
<string name="common_utxo_validate_withdrawal_message_warning">Debido a limitaciónes sobre %1$s, solos %2$d UTXO pueden caber en una sola transacción. Esto significa que solo puedes enviar %3$s o menos. Debe reducir la cantidad.</string>
|
||||
<string name="common_value_copied">Valor copiado</string>
|
||||
<string name="common_week">semana</string>
|
||||
<string name="common_with">con</string>
|
||||
<string name="common_yes">Sí</string>
|
||||
|
|
@ -262,6 +286,7 @@
|
|||
<string name="details_row_subtitle_signed_hashes_format">%s hashes</string>
|
||||
<string name="details_row_title_cid">ID del dispositivo</string>
|
||||
<string name="details_row_title_contact_to_support">Contactar con el equipo de soporte</string>
|
||||
<string name="details_row_title_contact_to_support_chat">Abrir chat de soporte</string>
|
||||
<string name="details_row_title_create_backup">Vincular más tarjetas</string>
|
||||
<string name="details_row_title_currency">Moneda de la aplicación</string>
|
||||
<string name="details_row_title_flip_to_hide">Girar para ocultar saldos</string>
|
||||
|
|
@ -278,7 +303,8 @@
|
|||
<string name="exchange_tokens_unavailable_tokens_header">No se puede intercambiar por %s</string>
|
||||
<string name="express_by_provider">Proporcionado por</string>
|
||||
<string name="express_cex_status_button_title">Estado</string>
|
||||
<string name="express_choose_providers_subtitle">Tangem ofrece swaps de tokens a través de proveedores externos según los términos de cada proveedor</string>
|
||||
<string name="express_choose_provider">Elegir proveedor</string>
|
||||
<string name="express_choose_providers_subtitle">Tangem ofrece swaps de tokens a través de proveedores externos</string>
|
||||
<string name="express_choose_providers_title">Elija proveedor</string>
|
||||
<string name="express_error_code">Ha ocurrido un error. Código: %s</string>
|
||||
<string name="express_error_provider_amount_roundup">Error %1$s. El proveedor seleccionado no puede procesar la transacción especificada. Por favor, redondee el valor a %2$s o cámbielo</string>
|
||||
|
|
@ -294,37 +320,39 @@
|
|||
<string name="express_exchange_notification_refund_title">El monto fue reembolsado en %1$s (red %2$s)</string>
|
||||
<string name="express_exchange_notification_verification_text">Visite el sitio web del proveedor para la verificación</string>
|
||||
<string name="express_exchange_notification_verification_title">Verificación KYC requerida por el proveedor</string>
|
||||
<string name="express_exchange_status_bought">Comprado</string>
|
||||
<string name="express_exchange_status_buying">Comprando</string>
|
||||
<string name="express_exchange_status_bought">Compra completada</string>
|
||||
<string name="express_exchange_status_buying">A la espera de la compra</string>
|
||||
<string name="express_exchange_status_buying_active">Comprando...</string>
|
||||
<string name="express_exchange_status_canceled">Cancelado</string>
|
||||
<string name="express_exchange_status_confirmed">Confirmado</string>
|
||||
<string name="express_exchange_status_canceled">Transacción cancelada</string>
|
||||
<string name="express_exchange_status_confirmed">Depósito confirmado</string>
|
||||
<string name="express_exchange_status_confirming">Esperando confirmación</string>
|
||||
<string name="express_exchange_status_confirming_active">En proceso de confirmación</string>
|
||||
<string name="express_exchange_status_exchanged">Intercambio</string>
|
||||
<string name="express_exchange_status_exchanging">En intercambio</string>
|
||||
<string name="express_exchange_status_exchanging_active">En intercambio</string>
|
||||
<string name="express_exchange_status_failed">Fallido</string>
|
||||
<string name="express_exchange_status_paused">En pausa</string>
|
||||
<string name="express_exchange_status_confirming_active">Esperando confirmación...</string>
|
||||
<string name="express_exchange_status_exchanged">Intercambio completado</string>
|
||||
<string name="express_exchange_status_exchanging">A la espera del intercambio</string>
|
||||
<string name="express_exchange_status_exchanging_active">Esperando el intercambio...</string>
|
||||
<string name="express_exchange_status_failed">Intercambio fallido</string>
|
||||
<string name="express_exchange_status_paused">Intercambio en pausa</string>
|
||||
<string name="express_exchange_status_received">Depósito recibido</string>
|
||||
<string name="express_exchange_status_receiving">Esperando depósito</string>
|
||||
<string name="express_exchange_status_receiving_active">Esperando depósito</string>
|
||||
<string name="express_exchange_status_refunded">Reembolsado</string>
|
||||
<string name="express_exchange_status_refunded">Reembolso completado</string>
|
||||
<string name="express_exchange_status_refunding">A la espera del reembolso</string>
|
||||
<string name="express_exchange_status_sending">Transfiriendo a su cuenta</string>
|
||||
<string name="express_exchange_status_sending_active">Enviando...</string>
|
||||
<string name="express_exchange_status_sent">Enviado</string>
|
||||
<string name="express_exchange_status_sending_active">Enviando fondos...</string>
|
||||
<string name="express_exchange_status_sent">Fondos enviados</string>
|
||||
<string name="express_exchange_status_subtitle">Datos proporcionados por el proveedor. La cantidad estimada puede cambiar debido a las condiciones del mercado.</string>
|
||||
<string name="express_exchange_status_title">Estado del intercambio</string>
|
||||
<string name="express_exchange_status_verifying">Verificación requerida</string>
|
||||
<string name="express_exchange_status_waiting_tx_hash">Esperando hash de transacción</string>
|
||||
<string name="express_exchange_token_list_subtitle">Lista de todos los tokens agregados a su billetera</string>
|
||||
<string name="express_fetch_best_rates">Buscando las mejores tarifas…</string>
|
||||
<string name="express_fetch_best_rates">Buscar tarifas actuales...</string>
|
||||
<string name="express_floating_rate">Tasa flotante</string>
|
||||
<string name="express_legal_one_placeholder">Al utilizar la función de intercambio, acepta el %s</string>
|
||||
<string name="express_legal_two_placeholders">Al utilizar la función de intercambio, aceptas las condiciones de %1$s y %2$s del proveedor</string>
|
||||
<string name="express_more_providers_soon">Próximamente habrá más proveedores.\n¡Estén atentos!</string>
|
||||
<string name="express_more_providers_soon">Pronto habrá más proveedores disponibles</string>
|
||||
<string name="express_provider">Proveedor</string>
|
||||
<string name="express_provider_best_rate">Mejor tarifa</string>
|
||||
<string name="express_provider_fca_warning_list">Lista de advertencias de la FCA</string>
|
||||
<string name="express_provider_max_amount">Disponible hasta %s</string>
|
||||
<string name="express_provider_min_amount">Disponible desde %s</string>
|
||||
<string name="express_provider_not_available">No disponible para este par</string>
|
||||
|
|
@ -336,6 +364,9 @@
|
|||
<string name="express_status_hide_button_text">Ocultar esta transacción</string>
|
||||
<string name="express_status_hide_dialog_text">Una vez oculto, el estado de la transacción no se puede volver a ver. En su lugar, basta con deslizar el dedo para descartarla.</string>
|
||||
<string name="express_status_hide_dialog_title">¿Ocultar el estado de la transacción?</string>
|
||||
<string name="express_swap_not_supported_text">Este token no es compatible. Por favor, elige un token diferente para intercambiar.</string>
|
||||
<string name="express_swap_not_supported_title">%s no es compatible</string>
|
||||
<string name="express_swap_with">Intercambiar con</string>
|
||||
<string name="express_token_list_empty_search">No se encontraron fichas. Por favor intenta con otra solicitud</string>
|
||||
<string name="express_transaction_id">ID : %s</string>
|
||||
<string name="express_transaction_id_copied">ID de transacción copiado</string>
|
||||
|
|
@ -363,6 +394,8 @@
|
|||
<string name="give_permission_swap_subtitle" formatted="false">Para continuar, de autorización a los smart contracts de %1s para usar su %2s</string>
|
||||
<string name="give_permission_title">Dar autorización</string>
|
||||
<string name="give_permission_unlimited">Ilimitado</string>
|
||||
<string name="home_button_add_existing_wallet">Agregar billetera existente</string>
|
||||
<string name="home_button_create_new_wallet">Crear nueva billetera</string>
|
||||
<string name="home_button_order">Pedir Tangem</string>
|
||||
<string name="home_button_scan">Escanee</string>
|
||||
<string name="hot_crypto_add_token_subtitle">a %s</string>
|
||||
|
|
@ -418,6 +451,7 @@
|
|||
<string name="markets_add_to_my_portfolio_unavailable_description">Este activo no está disponible en la billetera</string>
|
||||
<string name="markets_add_to_my_portfolio_unavailable_for_wallet_description">Este activo no está disponible para esta billetera</string>
|
||||
<string name="markets_add_token">Añadir</string>
|
||||
<string name="markets_apy_placeholder">APY %s</string>
|
||||
<string name="markets_available_networks">Redes disponibles</string>
|
||||
<string name="markets_common_my_portfolio">Mi portafolio</string>
|
||||
<string name="markets_common_title">Mercado</string>
|
||||
|
|
@ -671,7 +705,7 @@
|
|||
<string name="onboarding_wallet_info_title_second">Tarjetas idénticas</string>
|
||||
<string name="onboarding_wallet_info_title_third">Código de acceso</string>
|
||||
<string name="onramp_avaiable_with_payment_methods">Disponible con %s</string>
|
||||
<string name="onramp_choose_provider_title_hint">La compra de criptomonedas en Tangem se lleva a cabo utilizando proveedores externos usando sus propios términos.</string>
|
||||
<string name="onramp_choose_provider_title_hint">La compra de criptomonedas en Tangem se lleva a cabo utilizando proveedores externos.</string>
|
||||
<string name="onramp_country_search">Buscar por país</string>
|
||||
<string name="onramp_country_unavailable">Indisponible</string>
|
||||
<string name="onramp_currency_other">Otras monedas</string>
|
||||
|
|
@ -698,6 +732,13 @@
|
|||
<string name="organize_tokens_sort_by_balance">Por saldo</string>
|
||||
<string name="organize_tokens_title">Organizar tokens</string>
|
||||
<string name="organize_tokens_ungroup">Desagrupar</string>
|
||||
<string name="push_notifications_more_info">Más información</string>
|
||||
<string name="push_notifications_permission_alert_description">Puedes activar las Notificaciones para Tangem en Ajustes.</string>
|
||||
<string name="push_notifications_permission_alert_negative_button">Activar más tarde</string>
|
||||
<string name="push_notifications_permission_alert_positive_button">Ajustes</string>
|
||||
<string name="push_notifications_permission_alert_title">Activar notificaciones</string>
|
||||
<string name="push_transactions_notifications_description">Reciba alertas de transacciones entrantes en las redes compatibles</string>
|
||||
<string name="push_transactions_notifications_title">Notificaciones de transacciones</string>
|
||||
<string name="qr_scanner_camera_denied_gallery_button">Seleccione de la galería</string>
|
||||
<string name="qr_scanner_camera_denied_settings_button">Ajustes</string>
|
||||
<string name="qr_scanner_camera_denied_text">No ha dado acceso a su cámara</string>
|
||||
|
|
@ -770,7 +811,9 @@
|
|||
<string name="send_alert_fee_too_low_text">Ha especificado una comisión inferior a la cantidad recomendada, lo que podría provocar un retraso en su transacción. ¿Continuar?</string>
|
||||
<string name="send_alert_transaction_failed_text">Razón: %1$s\nCódigo: %2$s</string>
|
||||
<string name="send_alert_transaction_failed_title">La transacción está incompleta</string>
|
||||
<string name="send_amount_convert_to_another_token">Convertir a otro token</string>
|
||||
<string name="send_amount_label">Montante</string>
|
||||
<string name="send_amount_receive_token_subtitle">Se enviará al destinatario</string>
|
||||
<string name="send_bitcoin_custom_fee_footer">Puede establecer tu tarifa de transacción ajustando el valor en el campo\nSatoshi por vByte.</string>
|
||||
<string name="send_custom_amount_fee_footer">La tarifa que se cobrará por tu transacción. Puedes establecer tu propio valor.</string>
|
||||
<string name="send_custom_evm_max_fee">Tarifa máxima</string>
|
||||
|
|
@ -794,6 +837,7 @@
|
|||
<string name="send_fee_unreachable_error_text">Compruebe su conexión de red</string>
|
||||
<string name="send_fee_unreachable_error_title">Información de tarifa de red no accesible</string>
|
||||
<string name="send_from_wallet_android">Desde</string>
|
||||
<string name="send_from_wallet_name">De %s</string>
|
||||
<string name="send_gas_limit">Límite de gas</string>
|
||||
<string name="send_gas_limit_footer">Este es el máximo de gas que se gastará para completar una transacción o contrato. Un límite de gas evita cargos inesperados o ilimitados al ejecutar una transacción.</string>
|
||||
<string name="send_gas_price">Precios del gas</string>
|
||||
|
|
@ -801,8 +845,12 @@
|
|||
<string name="send_max_amount">Max</string>
|
||||
<string name="send_max_amount_label">Importe máximo</string>
|
||||
<string name="send_max_fee">Tarifa hasta</string>
|
||||
<string name="send_memo">Memo: %s</string>
|
||||
<string name="send_memo_destination_tag_error">Memo no válido</string>
|
||||
<string name="send_network_fee_warning_title">Cobertura de tarifa de red</string>
|
||||
<string name="send_nonce">Nonce</string>
|
||||
<string name="send_nonce_footer">Número único para cada transacción. Utilízalo para reenviar o cancelar una transacción pendiente.</string>
|
||||
<string name="send_nonce_hint">Introduzca nonce..</string>
|
||||
<string name="send_notification_exceed_balance_text">Fondos insuficientes para la transferencia, ya que el total de la tarifa y el importe de la transferencia supera el saldo existente</string>
|
||||
<string name="send_notification_exceed_balance_title">El total supera el saldo</string>
|
||||
<string name="send_notification_existential_deposit_text">Se requiere un saldo de al menos %s para mantener su cuenta en blockchain para evitar riesgos de seguridad. Este monto permanecerá en su saldo y no podrá retirarse.</string>
|
||||
|
|
@ -830,6 +878,8 @@
|
|||
<string name="send_recipient">Destinatario</string>
|
||||
<string name="send_recipient_address_error">No es una dirección válida</string>
|
||||
<string name="send_recipient_address_footer">Asegúrese de que la dirección de la billetera receptora esté en la red %s para evitar perder sus tokens</string>
|
||||
<string name="send_recipient_address_footer_highlighted_part">enviar a %s</string>
|
||||
<string name="send_recipient_address_footer_v2">Asegúrese de que %s dirección de red, ya que los errores pueden resultar en transferencias perdidas</string>
|
||||
<string name="send_recipient_label">Destinatario</string>
|
||||
<string name="send_recipient_memo_footer">Un Memo/ID de destino es un ID único para diferenciar transacciones enviadas al mismo destinatario en la misma red. Precaución: Omitir un memo puede llevar a la pérdida de fondos</string>
|
||||
<string name="send_recipient_memo_footer_v2">Memo / Destination Tag es un código que separa las transacciones dirigidas a un destinatario compartido en una red de criptomonedas.</string>
|
||||
|
|
@ -846,6 +896,7 @@
|
|||
<string name="send_summary_transaction_description_suffix_fee_covered">La tarifa de red se cubrirá al usar %1$s energía</string>
|
||||
<string name="send_summary_transaction_description_suffix_fee_reduced">La tarifa de red se reducirá al usar %1$s energía</string>
|
||||
<string name="send_summary_transaction_description_suffix_including">incluida una tarifa de red de %1$s</string>
|
||||
<string name="send_to_address">A la dirección</string>
|
||||
<string name="send_transaction_success">La transacción se firmó con éxito y se envió al nodo blockchain. El saldo de la billetera se actualizará después de un tiempo.</string>
|
||||
<string name="send_tron_account_activation_error">%1$s es un activo en la red Tron. Para calcular la tarifa y realizar una transacción, deba depositar algo de Tron (TRX) en su cuenta.</string>
|
||||
<string name="send_validation_amount_exceeds_balance">El monto excede el saldo</string>
|
||||
|
|
@ -854,6 +905,10 @@
|
|||
<string name="send_validation_invalid_amount">Cantidad no válida</string>
|
||||
<string name="send_validation_invalid_fee">La tarifa excede el saldo</string>
|
||||
<string name="send_validation_invalid_total">El monto total excede el saldo</string>
|
||||
<string name="send_with_swap_notification_text">Envía cualquier token y lo convertiremos en el camino. Su destinatario obtiene exactamente lo que necesita, sin problemas.</string>
|
||||
<string name="send_with_swap_recipient_amount_text">Se enviará un destinatario</string>
|
||||
<string name="send_with_swap_recipient_amount_title">Cantidad a recibir</string>
|
||||
<string name="send_with_swap_title">Enviar con swap</string>
|
||||
<string name="sent_transaction_sent_title">Transacción enviada</string>
|
||||
<string name="settings_card_settings_footer">Escanee la tarjeta/anillo que quiere configurar</string>
|
||||
<string name="settings_forget_wallet">Olvidar la billetera</string>
|
||||
|
|
@ -872,7 +927,7 @@
|
|||
<string name="staking_details_auto_claiming_rewards_daily_text">Las recompensas se acumulan automáticamente en su saldo de apustaking esta cada día.</string>
|
||||
<string name="staking_details_available">Disponible</string>
|
||||
<string name="staking_details_average_reward_rate">Tasa de recompensa promedio</string>
|
||||
<string name="staking_details_banner_text">¿Qué es el Staking?</string>
|
||||
<string name="staking_details_banner_text">¿Cómo funciona el staking?</string>
|
||||
<string name="staking_details_estimated_profit">%s beneficio estimado</string>
|
||||
<string name="staking_details_market_rating">Calificación de mercado</string>
|
||||
<string name="staking_details_metrics_block_header">Métrica</string>
|
||||
|
|
@ -901,14 +956,14 @@
|
|||
<string name="staking_notification_additional_ada_deposit_title">Detalles del staking de ADA</string>
|
||||
<string name="staking_notification_claim_rewards_text">Recomp. won se le enviará y estará disponible para su uso inmediato</string>
|
||||
<string name="staking_notification_earn_rewards_text">Haga staking de forma segura y comience a ganar recompensas</string>
|
||||
<string name="staking_notification_earn_rewards_text_daily">Haga de forma segura y comience a ganar recompensas diarias</string>
|
||||
<string name="staking_notification_earn_rewards_text_hourly">Haga staking de forma segura y comience a ganar recompensas por hora</string>
|
||||
<string name="staking_notification_earn_rewards_text_monthly">Haga staking de forma segura y comience a ganar recompensas mensuales</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_day">El staking le permite ganar %1$s. Sus recompensas por apostar llegan todos los días.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_hour">El staking le permite ganar %1$s. Sus recompensas del staking llegan cada hora.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_month">El staking le permite ganar %1$s. Sus recompensas del staking llegan todos los meses.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_week">El staking le permite ganar %1$s. Sus recompensas por apostar llegan todas las semanas.</string>
|
||||
<string name="staking_notification_earn_rewards_text_weekly">Haga staking de forma segura y comience a ganar recompensas semanales</string>
|
||||
<string name="staking_notification_earn_rewards_text_daily">Tangem permite a los usuarios hacer staking de sus criptomonedas</string>
|
||||
<string name="staking_notification_earn_rewards_text_hourly">Tangem permite a los usuarios hacer staking de sus criptomonedas</string>
|
||||
<string name="staking_notification_earn_rewards_text_monthly">Tangem permite a los usuarios hacer staking de sus criptomonedas</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_day">El staking le permite ganar %1$s. Sus recompensas por staking llegan cada día.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_hour">El staking le permite ganar %1$s. Sus recompensas por staking llegan cada hora.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_month">El staking le permite ganar %1$s. Sus recompensas por staking llegan cada mes.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_week">El staking le permite ganar %1$s. Sus recompensas por staking llegan cada semana.</string>
|
||||
<string name="staking_notification_earn_rewards_text_weekly">Tangem permite a los usuarios hacer staking de sus criptomonedas</string>
|
||||
<string name="staking_notification_earn_rewards_title">Gane recompensas por staking</string>
|
||||
<string name="staking_notification_low_staked_balance_text">Su saldo de staking será demasiado bajo para cancelar el staking. Necesitará hacer staking más para alcanzar la cantidad mínima para cancelarlo.</string>
|
||||
<string name="staking_notification_low_staked_balance_title">Saldo de staking bajo</string>
|
||||
|
|
@ -994,6 +1049,7 @@
|
|||
<string name="story_web3_title">Compatible con Web 3.0</string>
|
||||
<string name="sui_not_enough_coin_for_fee_description">Se requiere una transacción entrante de al menos %1$s para proceder</string>
|
||||
<string name="sui_not_enough_coin_for_fee_title">Fondos insuficientes</string>
|
||||
<string name="swap_fixed_rate">Tasa fija</string>
|
||||
<string name="swap_give_permission_fee_footer">La red cobrará una tarifa de aprobación de token para verificar que está autorizando el uso de su token para el swap.</string>
|
||||
<string name="swap_promo_text">Intercambie más tokens a mejores tasas directamente en su billetera.</string>
|
||||
<string name="swap_promo_title">¡Nuevo proveedor de intercambio disponible!</string>
|
||||
|
|
@ -1007,6 +1063,7 @@
|
|||
<string name="swap_story_second_title">Tarifas inmejorables</string>
|
||||
<string name="swap_story_third_subtitle">Sencillo e intuitivo, permite cambiar tokens con solo unos cuantos toques</string>
|
||||
<string name="swap_story_third_title">Simplemente cómodo</string>
|
||||
<string name="swap_via_provider">Intercambio a través del proveedor</string>
|
||||
<string name="swapping_alert_cex_description">La cantidad incluye:\n• Tarifas del proveedor de servicios\n• Tarifas de red por enviar %s desde el intercambio a la dirección del usuario.</string>
|
||||
<string name="swapping_alert_cex_description_with_slippage">El monto incluye:\n• tarifas del proveedor de servicios\n• tarifas de red por enviar %1$s desde el intercambio a la dirección del usuario. \n\nEl slippage del proveedor puede alcanzar el %2$s</string>
|
||||
<string name="swapping_alert_dex_description">El importe incluye los honorarios del proveedor de servicios.</string>
|
||||
|
|
@ -1067,8 +1124,12 @@
|
|||
<string name="transaction_history_transaction_from_address">desde: %s</string>
|
||||
<string name="transaction_history_transaction_to_address">a: %s</string>
|
||||
<string name="transaction_history_transaction_validator">validador: %s</string>
|
||||
<string name="transaction_notifications_warning_description">Las notificaciones están activadas, pero no funcionarán hasta que permitas las notificaciones en la configuración de tu dispositivo.</string>
|
||||
<string name="transaction_notifications_warning_title">Notificaciones de transacciones</string>
|
||||
<string name="transfer_min_amount_error">Mínimo %s</string>
|
||||
<string name="transfer_notification_invalid_minimum_transaction_amount_text">El monto mínimo para realizar esta transacción es %1$s.</string>
|
||||
<string name="tron_will_be_send_token_fee_description">Las tarifas de la red Tron para tokens populares pueden ser más altas. Hacer staking de TRX puede ayudar a reducir los costos de transacción.</string>
|
||||
<string name="tron_will_be_send_token_fee_title">Ahorra en las tarifas de la red Tron</string>
|
||||
<string name="try_to_load_data_again_button_title">Inténtelo de nuevo</string>
|
||||
<string name="twin_error_same_card">Ha escaneado la misma tarjeta. Para crear una billetera gemela, necesite escanear la tarjeta con no. %d</string>
|
||||
<string name="twin_error_wrong_twin">Ha escaneado la tarjeta gemela incorrecta. Por favor, intente con otra</string>
|
||||
|
|
@ -1081,14 +1142,17 @@
|
|||
<string name="twins_recreate_toolbar">Tangem Twin</string>
|
||||
<string name="twins_recreate_warning">Esta acción es irreversible. No tendrá acceso la billetera antigua.</string>
|
||||
<string name="twins_scan_twin_with_number">Toque la tarjeta gemela con el número %s y no la retira hasta el final de la operación</string>
|
||||
<string name="unexpected_error_description">Por favor, inténtelo de nuevo más tarde. Si el problema persiste, póngase en contacto con el servicio de asistencia.</string>
|
||||
<string name="unexpected_error_title">¡Algo salió mal!</string>
|
||||
<string name="universal_error">Hemos encontrado un error. Código de error: %s. Póngase en contacto con nuestro servicio de asistencia.</string>
|
||||
<string name="unlock_wallet_description_full">Use %s o escanee una tarjeta/anillo para tener acceso a su billetera</string>
|
||||
<string name="unsupported_wc_version">Error de conexión: Esta dApp utiliza la versión 1.0 de Wallet Connect, que no es compatible. Asegúrese de que la dApp sea compatible con la versión 2.0 de Wallet Connect para conectarse correctamente.</string>
|
||||
<string name="user_push_notification_agreement_argument_one">Manténgase actualizado con las últimas funciones y noticias</string>
|
||||
<string name="user_push_notification_agreement_argument_three">Recibir notificaciones de las transacciones entrantes</string>
|
||||
<string name="user_push_notification_agreement_argument_two">Sea el primero en enterarte de nuevas promociones</string>
|
||||
<string name="user_push_notification_agreement_header">¿Quiere utilizar\nnotificaciones push?</string>
|
||||
<string name="user_wallet_list_add_button">Agregar una nueva billetera</string>
|
||||
<string name="user_wallet_list_delete_prompt">¿Está seguro(a) de que quieres eliminar esta billetera?</string>
|
||||
<string name="user_wallet_list_delete_prompt">¿Estás seguro de que deseas olvidar esta billetera?</string>
|
||||
<string name="user_wallet_list_error_unable_to_unlock">Ha ocurrido un error, por favor escanee su tarjeta o anillo para iniciar sesión</string>
|
||||
<string name="user_wallet_list_error_wallet_already_saved">Esta billetera ya se ha guardado, puede agregar otro</string>
|
||||
<string name="user_wallet_list_rename_popup_error_already_exists">La billetera con el nombre %s ya existe</string>
|
||||
|
|
@ -1131,6 +1195,10 @@
|
|||
<string name="wallet_connect_subtitle">Conectar a dApps</string>
|
||||
<string name="wallet_connect_title">WalletConnect</string>
|
||||
<string name="wallet_connect_toast_awaiting_session_proposal">La conexión puede tardar unos segundos.</string>
|
||||
<string name="wallet_import_seed_navtitle">Importar billetera</string>
|
||||
<string name="wallet_import_success_description">La copia de seguridad de tu billetera se realizó con éxito.</string>
|
||||
<string name="wallet_import_success_navtitle">Importar billetera</string>
|
||||
<string name="wallet_import_success_title">Importación completada</string>
|
||||
<string name="wallet_marketplace_block_title">%s Precio de mercado</string>
|
||||
<string name="wallet_marketprice_block_update_time">últimas 24h</string>
|
||||
<string name="wallet_network_group_title">%s red</string>
|
||||
|
|
@ -1139,6 +1207,8 @@
|
|||
<string name="wallet_promo_banner_button_title">Consíguelo ahora con un 10 % de descuento</string>
|
||||
<string name="wallet_promo_banner_description">Accede a más de 13 000 criptomonedas. Compra, vende, intercambia y realiza staking con un solo toque.\nVincula hasta tres tarjetas para hacer copias de seguridad.</string>
|
||||
<string name="wallet_promo_banner_title">Descubre Tangem Wallet</string>
|
||||
<string name="wallet_settings_push_notifications_description">Manténteinformado sobre las transacciones entrantes de la billetera y las actualizaciones de Tangem.</string>
|
||||
<string name="wallet_settings_push_notifications_title">Notificaciones de transacciones</string>
|
||||
<string name="wallet_settings_title">Ajustes de la wallet</string>
|
||||
<string name="wallet_title">Tangem</string>
|
||||
<string name="warning_access_denied_message">Use %s o escanee una tarjeta/anillo para desbloquear el acceso a su billetera</string>
|
||||
|
|
@ -1166,7 +1236,7 @@
|
|||
<string name="warning_express_approval_in_progress_message">La aprobación del intercambio está en progreso y se completará en breve</string>
|
||||
<string name="warning_express_approval_in_progress_title">Aprobación en proceso</string>
|
||||
<string name="warning_express_dust_message">El monto mínimo de transacción es %1$s. Asegúrese de que el saldo restante después del canje no sea inferior a %2$s.</string>
|
||||
<string name="warning_express_no_exchangeable_coins_description">No tiene %s monedas negociables en su lista</string>
|
||||
<string name="warning_express_no_exchangeable_coins_description">No tienes tokens en tu portafolio a los que puedas intercambiar %s. Por favor, añade otro token para realizar el intercambio.</string>
|
||||
<string name="warning_express_no_exchangeable_coins_title">No hay tokens disponibles para intercambiar</string>
|
||||
<string name="warning_express_not_enough_fee_for_token_tx_description">Para realizar una transacción necesita depositar %1$s %2$s</string>
|
||||
<string name="warning_express_not_enough_fee_for_token_tx_title">No se pueden cubrir %s tarifa</string>
|
||||
|
|
@ -1209,6 +1279,7 @@
|
|||
<string name="warning_rate_app_message">Su opinión nos motiva a hacer Tangem Wallet aún mejor</string>
|
||||
<string name="warning_rate_app_title">¿Disfrutando de Tangem?</string>
|
||||
<string name="warning_receive_blocked_hedera_token_association_required_message">Deba asociar su token antes de recibir tokens</string>
|
||||
<string name="warning_receive_blocked_token_trustline_required_message">Debes abrir una línea de confianza para tu token antes de recibirlo</string>
|
||||
<string name="warning_rent_fee_title">Se requiere tarifa de alquiler de red</string>
|
||||
<string name="warning_seedphrase_action_required_title">Acción requerida</string>
|
||||
<string name="warning_seedphrase_contacted_support">¿Te pusiste en contacto con el servicio de asistencia a través de la aplicación o por correo electrónico en los 7 días posteriores a la creación de la billetera? Si lo hiciste o no estás seguro, sigue y completa las instrucciones.</string>
|
||||
|
|
@ -1224,9 +1295,58 @@
|
|||
<string name="warning_some_networks_unreachable_message">Deslice hacia abajo para refrescar o inténtelo de nuevo más tarde.</string>
|
||||
<string name="warning_some_networks_unreachable_title">Algunas redes no están disponibles</string>
|
||||
<string name="warning_some_token_balances_not_updated">No se han podido actualizar los saldos de algunos tokens</string>
|
||||
<string name="warning_stellar_token_trustline_not_enough_xlm">No hay suficiente %s. Recarga tu cuenta XLM para asociar este token</string>
|
||||
<string name="warning_testnet_card_message">Esta es una tarjeta de Testnet. No puede procesar transacciones y solo deba usarse para pruebas y desarrollo.</string>
|
||||
<string name="warning_testnet_card_title">Solo para fines de prueba</string>
|
||||
<string name="warning_token_balance_not_updated">El saldo podría estar desactualizado. Actualice la página.</string>
|
||||
<string name="warning_token_required_min_coin_reserve">No hay suficiente %1$s. Recarga tu %2$s cuenta para asociar este token</string>
|
||||
<string name="warning_token_trustline_button_title">Habilitar línea de confianza</string>
|
||||
<string name="warning_token_trustline_subtitle">Una línea de confianza debe estar habilitada para recibir este token. La red requiere un %1$s %2$s reserva.</string>
|
||||
<string name="warning_token_trustline_title">Se requiere línea de confianza</string>
|
||||
<string name="wc_alert_audit_malicious_domain">Dominio malicioso</string>
|
||||
<string name="wc_alert_audit_unknown_domain">Dominio desconocido</string>
|
||||
<string name="wc_alert_domain_issues_description">Este dominio no puede ser verificado. Compruebe cuidadosamente la solicitud de aprobación.</string>
|
||||
<string name="wc_alert_session_disconnected_description">Vuelva a su navegador y vuelva a conectarse a través de WalletConnect.</string>
|
||||
<string name="wc_alert_sign_anyway">Firmar de todos modos</string>
|
||||
<string name="wc_alert_unknown_error_description">Código de error: %s. Si el problema persiste - no dude en ponerse en contacto con nuestro soporte.</string>
|
||||
<string name="wc_alert_unknown_error_description_no_error_code">Si el problema persiste, no dudes en ponerte en contacto con nuestro soporte.</string>
|
||||
<string name="wc_alert_unsupported_method_description">Código de error: 8 005. Si el problema persiste, no dudes en contactar con nuestro soporte.</string>
|
||||
<string name="wc_alert_unsupported_method_title">Hemos encontrado un error desconocido</string>
|
||||
<string name="wc_alert_unsupported_networks_description">Actualmente, Tangem no es compatible con una red requerida por %s.</string>
|
||||
<string name="wc_alert_verified_domain_description">Tangem soporta una red requerida por %s</string>
|
||||
<string name="wc_alert_wrong_card_description">Se seleccionó una tarjeta o un anillo incorrectos en la app</string>
|
||||
<string name="wc_allow_to_spend">Permitir gastar</string>
|
||||
<string name="wc_common_address">Dirección</string>
|
||||
<string name="wc_common_connect">Conectar</string>
|
||||
<string name="wc_common_loading">Cargando</string>
|
||||
<string name="wc_common_network">Red</string>
|
||||
<string name="wc_common_networks">Redes</string>
|
||||
<string name="wc_common_unlimited">Ilimitado</string>
|
||||
<string name="wc_common_wallet">Billetera</string>
|
||||
<string name="wc_connected_networks">Redes conectadas</string>
|
||||
<string name="wc_connection_reqeust_can_view_balance">Ver el saldo y la actividad de su billetera</string>
|
||||
<string name="wc_connection_reqeust_cant_sign">Firmar transacciones sin previo aviso</string>
|
||||
<string name="wc_connection_reqeust_request_approval">Solicitar aprobación para transacciones</string>
|
||||
<string name="wc_connection_reqeust_will_not">No podrá</string>
|
||||
<string name="wc_connection_reqeust_would_like">Me gustaría</string>
|
||||
<string name="wc_connection_request">Solicitud de conexión</string>
|
||||
<string name="wc_contents">Contenido</string>
|
||||
<string name="wc_copy_data_button_text">Copiar datos</string>
|
||||
<string name="wc_custom_allowance_title">Asignación personalizada</string>
|
||||
<string name="wc_estimated_wallet_changes">Cambios estimados en la billetera</string>
|
||||
<string name="wc_estimated_wallet_changes_not_simulated">La transacción no ha podido ser simulada. Por favor, proceda con precaución.</string>
|
||||
<string name="wc_malicious_transaction">Transacción maliciosa</string>
|
||||
<string name="wc_missing_required_network_description">Añada la red %s a su perfil para esta billetera</string>
|
||||
<string name="wc_missing_required_network_title">La billetera no tiene las redes requeridas</string>
|
||||
<string name="wc_notification_security_risk_subtitle">Este dominio está marcado como no seguro por varios proveedores de seguridad. Salga de inmediato para proteger sus activos</string>
|
||||
<string name="wc_notification_security_risk_title">Riesgo de seguridad conocido</string>
|
||||
<string name="wc_request_from">Solicitud de</string>
|
||||
<string name="wc_signature_type">Tipo de firma</string>
|
||||
<string name="wc_transaction_info_to_title">A</string>
|
||||
<string name="wc_transaction_request">Solicitud de transacción</string>
|
||||
<string name="wc_transaction_request_title">Solicitud de transacción</string>
|
||||
<string name="wc_unlimited_amount">Cantidad ilimitada</string>
|
||||
<string name="wc_wallet_connect">Wallet connect</string>
|
||||
<string name="welcome_interrupted_backup_alert_discard">Ignorar</string>
|
||||
<string name="welcome_interrupted_backup_alert_message">Tiene un backup interrumpido. ¿Quiere reanudarlo?</string>
|
||||
<string name="welcome_interrupted_backup_alert_resume">Sí, reanudar</string>
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
<string name="app_settings_theme_mode_system">Par défaut du système</string>
|
||||
<string name="app_settings_theme_selector_title">Thème</string>
|
||||
<string name="app_settings_title">Paramètres de l\'application</string>
|
||||
<string name="backup_complete_seed_description">Ces mots ne peuvent pas être récupérés en cas de perte. Assurez-vous de les conserver en lieu sûr.</string>
|
||||
<string name="balance_hidden_description">Pour masquer ou afficher vos soldes, il suffit de retourner l\'écran de votre appareil vers le bas ou de le désactiver dans les paramètres</string>
|
||||
<string name="balance_hidden_do_not_show_button">Ne plus afficher</string>
|
||||
<string name="balance_hidden_got_it_button">Compris</string>
|
||||
|
|
@ -116,6 +117,7 @@
|
|||
<string name="common_close">Fermer</string>
|
||||
<string name="common_confirm">Confirmez</string>
|
||||
<string name="common_continue">Continuer</string>
|
||||
<string name="common_convert">Convertir</string>
|
||||
<string name="common_copy">Copier</string>
|
||||
<string name="common_copy_address">Copier l\'adresse</string>
|
||||
<string name="common_create">Créer</string>
|
||||
|
|
@ -216,6 +218,7 @@
|
|||
<string name="common_unreachable">Inaccessible</string>
|
||||
<string name="common_unstake">Unstakez</string>
|
||||
<string name="common_utxo_validate_withdrawal_message_warning">En raison d\'une limitations sur les %1$s, seuls les %2$d UTXO peuvent s\'intégrer dans une seule transaction. Ce qui signifie vous ne pouvez envoyer que %3$s ou moins. Réduisez le montant.</string>
|
||||
<string name="common_value_copied">Valeur copiée</string>
|
||||
<string name="common_week">semaine</string>
|
||||
<string name="common_with">avec</string>
|
||||
<string name="common_yes">Oui</string>
|
||||
|
|
@ -699,6 +702,12 @@
|
|||
<string name="organize_tokens_sort_by_balance">Par solde</string>
|
||||
<string name="organize_tokens_title">Organiser les jetons</string>
|
||||
<string name="organize_tokens_ungroup">Dégrouper</string>
|
||||
<string name="push_notifications_more_info">Plus d\'infos</string>
|
||||
<string name="push_notifications_permission_alert_description">Vous pouvez activer les notifications pour Tangem dans les paramètres.</string>
|
||||
<string name="push_notifications_permission_alert_negative_button">Activer plus tard</string>
|
||||
<string name="push_notifications_permission_alert_positive_button">Paramètres</string>
|
||||
<string name="push_notifications_permission_alert_title">Activer les notifications</string>
|
||||
<string name="push_transactions_notifications_description">Recevez des alertes pour les transactions entrantes sur les réseaux pris en charge.</string>
|
||||
<string name="qr_scanner_camera_denied_gallery_button">Sélectionnez dans la galerie</string>
|
||||
<string name="qr_scanner_camera_denied_settings_button">Paramètres</string>
|
||||
<string name="qr_scanner_camera_denied_text">Vous n\'avez pas donné accès à votre caméra</string>
|
||||
|
|
@ -771,7 +780,9 @@
|
|||
<string name="send_alert_fee_too_low_text">Vous avez spécifié une commission inférieure au montant recommandé, ce qui pourrait entraîner un retard dans votre transaction. Continuer?</string>
|
||||
<string name="send_alert_transaction_failed_text">Raison : %1$s\nCode : %2$s</string>
|
||||
<string name="send_alert_transaction_failed_title">La transaction est incomplète</string>
|
||||
<string name="send_amount_convert_to_another_token">Convertir en un autre jeton</string>
|
||||
<string name="send_amount_label">Montant</string>
|
||||
<string name="send_amount_receive_token_subtitle">Sera envoyé au destinataire</string>
|
||||
<string name="send_bitcoin_custom_fee_footer">Vous pouvez définir vos frais de transaction en ajustant la valeur dans le champ Satoshi par vByte.</string>
|
||||
<string name="send_custom_amount_fee_footer">Les frais qui seront facturés pour votre transaction. Vous pouvez définir votre propre valeur.</string>
|
||||
<string name="send_custom_evm_max_fee">Frais maximum</string>
|
||||
|
|
@ -795,6 +806,7 @@
|
|||
<string name="send_fee_unreachable_error_text">Vérifiez votre connexion réseau</string>
|
||||
<string name="send_fee_unreachable_error_title">Informations sur les frais de réseau inaccessibles</string>
|
||||
<string name="send_from_wallet_android">De</string>
|
||||
<string name="send_from_wallet_name">De %s</string>
|
||||
<string name="send_gas_limit">Limite de gaz</string>
|
||||
<string name="send_gas_limit_footer">Il s\'agit du montant maximal de gaz qui sera dépensé pour effectuer une transaction ou un contrat. Une limite de gaz empêche des frais imprévus ou illimités lors de l\'exécution d\'une transaction.</string>
|
||||
<string name="send_gas_price">Prix du gaz</string>
|
||||
|
|
@ -802,6 +814,7 @@
|
|||
<string name="send_max_amount">Max</string>
|
||||
<string name="send_max_amount_label">Somme maximale</string>
|
||||
<string name="send_max_fee">Frais jusqu\'à</string>
|
||||
<string name="send_memo">Mémo : %s</string>
|
||||
<string name="send_memo_destination_tag_error">Mémo invalide</string>
|
||||
<string name="send_network_fee_warning_title">Couverture des frais de réseau</string>
|
||||
<string name="send_notification_exceed_balance_text">Fonds insuffisants pour le transfert, car le total des frais et du montant du transfert dépasse le solde existant</string>
|
||||
|
|
@ -812,6 +825,7 @@
|
|||
<string name="send_notification_fee_too_high_title">Les frais de douane sont élevés</string>
|
||||
<string name="send_notification_high_fee_text">En raison des particularités du réseau %1$s, les frais de transfert de la totalité du solde sont plus élevés. Pour réduire la commission, vous pouvez en laisser %2$s.</string>
|
||||
<string name="send_notification_high_fee_title">Les frais sont plus élevés</string>
|
||||
<string name="send_notification_invalid_amount_rent_destination">Le compte du bénéficiaire n\'est pas activé. Le montant minimum du transfert doit être égal ou supérieur au solde exonéré de loyer : %1$s.</string>
|
||||
<string name="send_notification_invalid_amount_rent_fee">Le solde de votre compte ne peut pas être inférieur au montant du loyer. Veuillez conserver au moins %1$s sur votre compte ou retirer tous les fonds.</string>
|
||||
<string name="send_notification_invalid_amount_text">La commission incluse dépasse le montant du transfert, ce qui entraîne une valeur négative</string>
|
||||
<string name="send_notification_invalid_amount_title">Montant invalide</string>
|
||||
|
|
@ -831,6 +845,7 @@
|
|||
<string name="send_recipient">Destinataire</string>
|
||||
<string name="send_recipient_address_error">Adresse non valide</string>
|
||||
<string name="send_recipient_address_footer">Assurez-vous que l\'adresse du portefeuille de réception est sur le réseau %s pour éviter de perdre vos jetons</string>
|
||||
<string name="send_recipient_address_footer_highlighted_part">envoyer à %s</string>
|
||||
<string name="send_recipient_label">Envoyer à</string>
|
||||
<string name="send_recipient_memo_footer">Un Memo/Destination Tag est un identifiant unique permettant de différencier les transactions envoyées au même destinataire sur le même réseau. Attention : L\'omission d\'un mémo peut entraîner des fonds mal placés</string>
|
||||
<string name="send_recipient_memo_footer_v2">Le Memo / Destination Tag est un code qui distingue les transactions envoyées à un destinataire partagé sur un réseau crypto.</string>
|
||||
|
|
@ -847,6 +862,7 @@
|
|||
<string name="send_summary_transaction_description_suffix_fee_covered">les frais de réseau seront couverts en utilisant %1$s énergie</string>
|
||||
<string name="send_summary_transaction_description_suffix_fee_reduced">les frais de réseau seront réduits en utilisant %1$s énergie</string>
|
||||
<string name="send_summary_transaction_description_suffix_including">y compris des frais de réseau de %1$s</string>
|
||||
<string name="send_to_address">À l\'adresse</string>
|
||||
<string name="send_transaction_success">La transaction a été signée avec succès et envoyée au nœud de blockchain. Le solde du portefeuille sera mis à jour après un certain temps</string>
|
||||
<string name="send_tron_account_activation_error">%1$s est un actif du réseau Tron. Pour calculer les frais et effectuer une transaction, déposez du Tron (TRX) sur votre compte.</string>
|
||||
<string name="send_validation_amount_exceeds_balance">Le montant dépasse le solde</string>
|
||||
|
|
@ -855,6 +871,8 @@
|
|||
<string name="send_validation_invalid_amount">Montant invalide</string>
|
||||
<string name="send_validation_invalid_fee">Les frais de commissions dépassent le solde</string>
|
||||
<string name="send_validation_invalid_total">Le total dépasse le solde</string>
|
||||
<string name="send_with_swap_confirm_title">Échanger et envoyer</string>
|
||||
<string name="send_with_swap_recipient_amount_title">Montant à recevoir</string>
|
||||
<string name="sent_transaction_sent_title">Transaction envoyée</string>
|
||||
<string name="settings_card_settings_footer">Scannez la carte/ bague que vous souhaitez configurer.</string>
|
||||
<string name="settings_forget_wallet">Oublier le portefeuille</string>
|
||||
|
|
@ -1008,6 +1026,7 @@
|
|||
<string name="swap_story_second_title">Meilleurs Tarifs</string>
|
||||
<string name="swap_story_third_subtitle">Simple et intuitif, vous permettant d\'échanger des jetons en quelques clics</string>
|
||||
<string name="swap_story_third_title">Plus Simple Que Jamais</string>
|
||||
<string name="swap_via_provider">Échange via le fournisseur</string>
|
||||
<string name="swapping_alert_cex_description">Le montant comprend :\n• les frais du fournisseur de services\n• les frais de réseau pour l\'envoi de %s depuis l\'échange vers l\'adresse de l\'utilisateur.</string>
|
||||
<string name="swapping_alert_cex_description_with_slippage">Le montant comprend :\n• les frais du fournisseur de services\n• les frais de réseau pour l\'envoi de %1$s depuis l\'échange vers l\'adresse de l\'utilisateur. \n\nLe slippage du fournisseur peut atteindre %2$s</string>
|
||||
<string name="swapping_alert_dex_description">Le montant comprend les frais du fournisseur de services.</string>
|
||||
|
|
@ -1085,6 +1104,7 @@
|
|||
<string name="unlock_wallet_description_full">Utilisez %s ou scannez une carte/bague pour avoir accès à votre portefeuille</string>
|
||||
<string name="unsupported_wc_version">Échec de la connexion : Cette dApp utilise Wallet Connect version1.0, qui n\'est pas prise en charge. Veuillez vous assurer que la dApp prend en charge Wallet Connect version2.0 pour réussir la connexion.</string>
|
||||
<string name="user_push_notification_agreement_argument_one">Restez à jour avec les dernières fonctionnalités et actualités</string>
|
||||
<string name="user_push_notification_agreement_argument_three">Recevez des notifications des transactions entrantes</string>
|
||||
<string name="user_push_notification_agreement_argument_two">Soyez le premier informé des nouvelles promotions</string>
|
||||
<string name="user_push_notification_agreement_header">Souhaitez-vous utiliser les\nnotifications push?</string>
|
||||
<string name="user_wallet_list_add_button">Ajouter un nouveau portefeuille</string>
|
||||
|
|
@ -1157,6 +1177,8 @@
|
|||
<string name="wallet_promo_banner_button_title">Obtenez-le maintenant avec 10 % de réduction</string>
|
||||
<string name="wallet_promo_banner_description">Accédez à plus de 13 000 cryptomonnaies. Achetez, vendez, échangez et stakez en un seul clic.\nAssociez jusqu\'à trois cartes pour une sauvegarde.</string>
|
||||
<string name="wallet_promo_banner_title">Découvrez le Portefeuille Tangem</string>
|
||||
<string name="wallet_settings_push_notifications_description">Recevez des notifications sur les transactions entrantes du portefeuille et les mises à jour de Tangem.</string>
|
||||
<string name="wallet_settings_push_notifications_title">Notifications de transaction</string>
|
||||
<string name="wallet_settings_title">Paramètres du portefeuille</string>
|
||||
<string name="wallet_title">Tangem</string>
|
||||
<string name="warning_access_denied_message">Utilisez %s ou scannez une carte/bague pour déverrouiller l\'accès à votre portefeuille</string>
|
||||
|
|
@ -1184,7 +1206,7 @@
|
|||
<string name="warning_express_approval_in_progress_message">L\'approbation de l\'échange est en cours et sera achevée sous peu</string>
|
||||
<string name="warning_express_approval_in_progress_title">Approbation en cours</string>
|
||||
<string name="warning_express_dust_message">Le montant minimum d\'échange est de %1$s. Veuillez vous assurer que le solde restant après l\'échange ne sera pas inférieur à %2$s.</string>
|
||||
<string name="warning_express_no_exchangeable_coins_description">Vous n\'avez pas de pièces échangeables %s dans votre liste</string>
|
||||
<string name="warning_express_no_exchangeable_coins_description">Vous n’avez aucun jeton dans votre portefeuille pouvant recevoir %s en échange. Veuillez ajouter un autre jeton pour effectuer l’échange.</string>
|
||||
<string name="warning_express_no_exchangeable_coins_title">Aucun jeton disponible à échanger</string>
|
||||
<string name="warning_express_not_enough_fee_for_token_tx_description">Pour effectuer une transaction, vous devez déposer %1$s %2$s</string>
|
||||
<string name="warning_express_not_enough_fee_for_token_tx_title">Impossible de couvrir %s frais</string>
|
||||
|
|
@ -1243,9 +1265,14 @@
|
|||
<string name="warning_some_networks_unreachable_message">Certains réseaux sont actuellement inaccessibles. Veuillez réessayer plus tard.</string>
|
||||
<string name="warning_some_networks_unreachable_title">Certains réseaux sont inaccessibles</string>
|
||||
<string name="warning_some_token_balances_not_updated">Certains soldes de jetons n\'ont pas pu être mis à jour</string>
|
||||
<string name="warning_stellar_token_trustline_not_enough_xlm">Pas assez de %s. Rechargez votre compte XLM pour associer ce jeton</string>
|
||||
<string name="warning_testnet_card_message">Il s\'agit d\'une carte Testnet. Elle ne peut pas traiter les transactions et ne doit être utilisée qu\'à des fins de test et de développement.</string>
|
||||
<string name="warning_testnet_card_title">À des fins de test uniquement</string>
|
||||
<string name="warning_token_balance_not_updated">Le solde peut être obsolète. Rafraîchissez la page.</string>
|
||||
<string name="wc_alert_audit_malicious_domain">Domaine malveillant</string>
|
||||
<string name="wc_alert_unknown_error_description_no_error_code">Si le problème persiste, n’hésitez pas à contacter notre support.</string>
|
||||
<string name="wc_alert_unsupported_dapps_description">Le portefeuille Tangem ne prend actuellement pas en charge %ss</string>
|
||||
<string name="wc_alert_unsupported_dapps_title">dApp non prise en charge</string>
|
||||
<string name="wc_connections">Connexions</string>
|
||||
<string name="wc_disconnect_all">Déconnecter tout</string>
|
||||
<string name="wc_disconnect_all_alert_desc">Texte sur la déconnexion de toutes les dApps</string>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@
|
|||
<string name="access_code_alert_skip_ok">とにかくスキップ</string>
|
||||
<string name="access_code_alert_skip_title">アクセスコードが設定されていません</string>
|
||||
<string name="access_code_check_title">アクセスコードを入力</string>
|
||||
<string name="access_code_check_warining_delete">アクセスコードが間違っています。あと%s回間違えるとホットウォレットが削除されます。</string>
|
||||
<string name="access_code_check_warining_lock">アクセスコードが間違っています。あと%s回入力エラーが発生するとアプリがロックされます。</string>
|
||||
<string name="access_code_check_warining_wait">アクセスコードが間違っています。 \n %s秒待ってから再試行してください。</string>
|
||||
<string name="access_code_confirm_description">続行するには、以前に入力したコードを確認してください</string>
|
||||
<string name="access_code_confirm_title">アクセスコードを再入力</string>
|
||||
<string name="access_code_create_description">ウォレットのロックを解除するには、 %s桁のアクセスコードを設定します。</string>
|
||||
|
|
@ -59,6 +62,7 @@
|
|||
<string name="app_settings_theme_selector_title">テーマ</string>
|
||||
<string name="app_settings_title">アプリ設定</string>
|
||||
<string name="backup_complete_description">ウォレットのバックアップが正常に完了しました。</string>
|
||||
<string name="backup_complete_seed_description">これらの単語は紛失した場合、復元できません。必ず安全な場所に保管してください。</string>
|
||||
<string name="backup_complete_title">バックアップが完了しました</string>
|
||||
<string name="backup_info_description">シークレットリカバリーフレーズは、ウォレットへのアクセスと復元のために使用される%sのランダムな単語のセットです。</string>
|
||||
<string name="backup_info_keep_description">これらの単語は紛失した場合、復元できません。必ず安全な場所に保管してください。</string>
|
||||
|
|
@ -408,6 +412,10 @@
|
|||
<string name="home_button_scan">Tangemをスキャン</string>
|
||||
<string name="hot_crypto_add_token_subtitle">%sへ</string>
|
||||
<string name="hot_crypto_token_network">%sネットワーク</string>
|
||||
<string name="hw_activation_need_backup">今すぐバックアップ</string>
|
||||
<string name="hw_activation_need_description">セットアップを完了するには、ウォレットをバックアップし、アクセスコードを使用してアプリへのアクセスを保護します。</string>
|
||||
<string name="hw_activation_need_finish">今すぐ実施</string>
|
||||
<string name="hw_activation_need_title">ウォレットのアクティベーションを完了する</string>
|
||||
<string name="hw_backup_icloud_description">iCloudバックアップに保存されている既存のウォレットを復元する</string>
|
||||
<string name="hw_backup_icloud_title">iCloudバックアップ</string>
|
||||
<string name="hw_backup_need_action">バックアップへ移動</string>
|
||||
|
|
@ -650,7 +658,7 @@
|
|||
<string name="onboarding_button_continue_wallet">ウォレットへ進む</string>
|
||||
<string name="onboarding_button_finalize_backup">バックアップを完了する</string>
|
||||
<string name="onboarding_button_receive_crypto">暗号資産を受け取る</string>
|
||||
<string name="onboarding_button_scan_origin_card">プライマリカードをスキャン</string>
|
||||
<string name="onboarding_button_scan_origin_card">メインカードまたはリングをスキャン</string>
|
||||
<string name="onboarding_button_skip_backup">スキップする</string>
|
||||
<string name="onboarding_button_what_does_it_mean">どのように機能しますか?</string>
|
||||
<string name="onboarding_create_wallet_body">すべての秘密鍵をカードまたはリング内で生成し、安全なウォレットを作成しましょう</string>
|
||||
|
|
@ -701,7 +709,7 @@
|
|||
<string name="onboarding_title_no_backup_cards">バックアップデバイスなし</string>
|
||||
<string name="onboarding_title_notifications">通知</string>
|
||||
<string name="onboarding_title_one_backup_card">バックアップデバイスが1つ追加されました</string>
|
||||
<string name="onboarding_title_scan_origin_card">カードを準備してください</string>
|
||||
<string name="onboarding_title_scan_origin_card">カードまたはリングを用意してください</string>
|
||||
<string name="onboarding_title_two_backup_cards">バックアップデバイス2つが追加されました</string>
|
||||
<string name="onboarding_top_up_body">始めるには、ウォレットに任意の金額を入金するだけです</string>
|
||||
<string name="onboarding_top_up_body_no_account_error">始めるには、ウォレットに%1$s %2$s以上入金するだけです</string>
|
||||
|
|
@ -919,6 +927,7 @@
|
|||
<string name="send_validation_invalid_amount">無効な金額</string>
|
||||
<string name="send_validation_invalid_fee">手数料が残高を超えています</string>
|
||||
<string name="send_validation_invalid_total">合計金額が残高を超えています</string>
|
||||
<string name="send_with_swap_confirm_title">スワップして送信</string>
|
||||
<string name="send_with_swap_notification_text">トークンを送信すれば、送信中に変換されます。受信者は必要なものをシームレスに受け取ります。</string>
|
||||
<string name="send_with_swap_recipient_amount_text">受信者に送信されます</string>
|
||||
<string name="send_with_swap_recipient_amount_title">受取金額</string>
|
||||
|
|
@ -1156,6 +1165,7 @@
|
|||
<string name="twins_recreate_toolbar">Tangemツイン</string>
|
||||
<string name="twins_recreate_warning">この操作は元に戻せません。古いウォレットにはアクセスできなくなります。</string>
|
||||
<string name="twins_scan_twin_with_number">番号%sのツインカードをタップし、操作が終了するまで取り外さないでください。</string>
|
||||
<string name="unexpected_error_description">しばらくしてからもう一度お試しください。問題が解決しない場合は、サポートにお問い合わせください。</string>
|
||||
<string name="unexpected_error_title">問題が発生しました。</string>
|
||||
<string name="universal_error">エラーが発生しました。エラー コード:%s。サポートにお問い合わせください。</string>
|
||||
<string name="unlock_wallet_description_full">%sを使用するか、カード / リングをスキャンしてウォレットにアクセスしてください</string>
|
||||
|
|
@ -1290,8 +1300,8 @@
|
|||
<string name="warning_express_approval_in_progress_message">スワップ承認は現在進行中で、まもなく完了する予定です。</string>
|
||||
<string name="warning_express_approval_in_progress_title">承認が進行中</string>
|
||||
<string name="warning_express_dust_message">最低のスワップ金額は%1$s です。スワップ後の残金が%2$s を下回らないようにしてください。</string>
|
||||
<string name="warning_express_no_exchangeable_coins_description">あなたのリストには、交換可能な %s トークンがありません。</string>
|
||||
<string name="warning_express_no_exchangeable_coins_title">スワップ可能なトークンがありません</string>
|
||||
<string name="warning_express_no_exchangeable_coins_description">ポートフォリオ内に%sとスワップ可能なトークンがありません。交換を有効にするには、別のトークンを追加してください。</string>
|
||||
<string name="warning_express_no_exchangeable_coins_title">互換性のあるトークンが追加されていません</string>
|
||||
<string name="warning_express_not_enough_fee_for_token_tx_description">取引を行うには、 %1$s %2$sを入金する必要があります。</string>
|
||||
<string name="warning_express_not_enough_fee_for_token_tx_title">%s 手数料を支払えません</string>
|
||||
<string name="warning_express_notification_invalid_reserve_amount_title">受け取る金額は、 %s 以上である必要があります。</string>
|
||||
|
|
@ -1349,17 +1359,15 @@
|
|||
<string name="warning_some_networks_unreachable_message">下にスワイプして更新するか、後でもう一度お試しください。</string>
|
||||
<string name="warning_some_networks_unreachable_title">一部のネットワークにアクセスできません</string>
|
||||
<string name="warning_some_token_balances_not_updated">一部のトークン残高を更新できませんでした</string>
|
||||
<string name="warning_stellar_token_trustline_button_title">トラストラインを有効にする</string>
|
||||
<string name="warning_stellar_token_trustline_not_enough_xlm">%sが足りません。このトークンを関連付けるには、XLMアカウントに資金を追加してください。</string>
|
||||
<string name="warning_stellar_token_trustline_subtitle">このトークンを受け取るには、トラストラインを有効にする必要があります。ネットワークには 0.5 XLMの準備金が必要です。</string>
|
||||
<string name="warning_stellar_token_trustline_title">トラストラインが必要</string>
|
||||
<string name="warning_testnet_card_message">これはテストネットカードです。取引処理はできませんので、テストおよび開発目的でのみご利用ください。</string>
|
||||
<string name="warning_testnet_card_title">テスト目的のみ</string>
|
||||
<string name="warning_token_balance_not_updated">残高が古い可能性があります。ページを更新してください。</string>
|
||||
<string name="warning_token_required_min_coin_reserve">%1$sが足りません。このトークンを関連付けるには、XLMアカウントにチャージしてください。</string>
|
||||
<string name="warning_token_required_min_coin_reserve">%1$sが足りません。このトークンを関連付けるには、 %2$sアカウントにチャージしてください。</string>
|
||||
<string name="warning_token_trustline_button_title">トラストラインを有効にする</string>
|
||||
<string name="warning_token_trustline_subtitle">このトークンを受け取るには、トラストラインを有効にする必要があります。ネットワークには%1$s %2$s予備金が必要です。</string>
|
||||
<string name="warning_token_trustline_title">トラストラインが必要</string>
|
||||
<string name="wc_alert_audit_malicious_domain">悪意のあるドメイン</string>
|
||||
<string name="wc_alert_audit_unknown_domain">不明なドメイン</string>
|
||||
<string name="wc_alert_connect_anyway">とにかく接続する</string>
|
||||
<string name="wc_alert_connection_timeout_description">タイムアウトエラーが発生しました。しばらくしてからもう一度お試しください。</string>
|
||||
|
|
@ -1371,6 +1379,8 @@
|
|||
<string name="wc_alert_unknown_error_description">エラーコード: %s 。問題が解決しない場合は、お気軽にサポートまでお問い合わせください。</string>
|
||||
<string name="wc_alert_unknown_error_description_no_error_code">問題が解決しない場合は、お気軽にサポートまでお問い合わせください。</string>
|
||||
<string name="wc_alert_unknown_error_title">不明なエラーが発生しました</string>
|
||||
<string name="wc_alert_unsupported_dapps_description">Tangemウォレットは、現在%sをサポートしていません。</string>
|
||||
<string name="wc_alert_unsupported_dapps_title">サポートされていないdApp</string>
|
||||
<string name="wc_alert_unsupported_method_description">エラーコード: 8 005。問題が解決しない場合は、お気軽にサポートまでお問い合わせください。</string>
|
||||
<string name="wc_alert_unsupported_method_title">不明なエラーが発生しました</string>
|
||||
<string name="wc_alert_unsupported_networks_description">Tangemは現在%sで必要なネットワークをサポートしていません。</string>
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@
|
|||
<string name="common_locked">Заблокирован</string>
|
||||
<string name="common_main_network">Основная сеть</string>
|
||||
<string name="common_month">месяц</string>
|
||||
<string name="common_network_fee_title">Сетевая комиссия</string>
|
||||
<string name="common_network_fee_title">Комиссия сети</string>
|
||||
<string name="common_network_fee_warning_content">Сумма отправки будет уменьшена на %1$s (%2$s) для покрытия выбранного уровня комиссии</string>
|
||||
<string name="common_next">Далее</string>
|
||||
<string name="common_nft">NFT</string>
|
||||
|
|
@ -532,7 +532,7 @@
|
|||
<string name="markets_tooltip_message">Потяните вверх или коснитесь поисковой строки, чтобы добавить токены напрямую из рынка</string>
|
||||
<string name="markets_tooltip_title">Добавить токены</string>
|
||||
<string name="nfc_error_unavailable">Функция NFC недоступна на вашем устройстве</string>
|
||||
<string name="nft_about_title">Об NFT</string>
|
||||
<string name="nft_about_title">О NFT</string>
|
||||
<string name="nft_asset">NFT</string>
|
||||
<plurals name="nft_collections_count">
|
||||
<item quantity="one">%d NFT</item>
|
||||
|
|
@ -620,7 +620,7 @@
|
|||
<string name="onboarding_button_continue_wallet">Перейти к моему кошельку</string>
|
||||
<string name="onboarding_button_finalize_backup">Завершение бэкапа</string>
|
||||
<string name="onboarding_button_receive_crypto">Получить криптовалюту</string>
|
||||
<string name="onboarding_button_scan_origin_card">Сканировать основную карту</string>
|
||||
<string name="onboarding_button_scan_origin_card">Сканировать основную карту или кольцо</string>
|
||||
<string name="onboarding_button_skip_backup">Пропустить</string>
|
||||
<string name="onboarding_button_what_does_it_mean">Как это работает?</string>
|
||||
<string name="onboarding_create_wallet_body">Давайте сгенерируем все ключи на вашей карте или кольце и создадим безопасный кошелек</string>
|
||||
|
|
@ -676,7 +676,7 @@
|
|||
<string name="onboarding_title_no_backup_cards">Нет резервных устройств</string>
|
||||
<string name="onboarding_title_notifications">Уведомления</string>
|
||||
<string name="onboarding_title_one_backup_card">Добавлено одно резервное устройство</string>
|
||||
<string name="onboarding_title_scan_origin_card">Подготовьте свою карту</string>
|
||||
<string name="onboarding_title_scan_origin_card">Подготовьте свою карту или кольцо</string>
|
||||
<string name="onboarding_title_two_backup_cards">Добавлены два резервных девайса</string>
|
||||
<string name="onboarding_top_up_body">Пополните кошелек на любую сумму, чтобы начать пользоваться картой</string>
|
||||
<string name="onboarding_top_up_body_no_account_error">Пополните кошелек более чем на %1$s %2$s, чтобы начать пользоваться картой</string>
|
||||
|
|
@ -723,7 +723,12 @@
|
|||
<string name="organize_tokens_title">Упорядочить токены</string>
|
||||
<string name="organize_tokens_ungroup">Список</string>
|
||||
<string name="push_notifications_more_info">Подробнее</string>
|
||||
<string name="push_notifications_permission_alert_description">Вы можете включить нотификации в настройках</string>
|
||||
<string name="push_notifications_permission_alert_negative_button">Включить позже</string>
|
||||
<string name="push_notifications_permission_alert_positive_button">Настройки</string>
|
||||
<string name="push_notifications_permission_alert_title">Подключить нотификации</string>
|
||||
<string name="push_transactions_notifications_description">Получайте уведомления о входящих транзакциях в поддерживаемых сетях.</string>
|
||||
<string name="push_transactions_notifications_title">Уведомления о транзакциях</string>
|
||||
<string name="qr_scanner_camera_denied_gallery_button">Выбрать из галереи</string>
|
||||
<string name="qr_scanner_camera_denied_settings_button">Настройки</string>
|
||||
<string name="qr_scanner_camera_denied_text">Вы не предоставили доступ к вашей камере</string>
|
||||
|
|
@ -1119,6 +1124,7 @@
|
|||
<string name="unlock_wallet_description_full">Используйте %s или отсканируйте карту/кольцо, чтобы получить доступ к своему кошельку</string>
|
||||
<string name="unsupported_wc_version">Соединение не удалось: это dApp использует Wallet Connect версии 1.0, которая не поддерживается. Убедитесь, что dApp поддерживает Wallet Connect версии 2.0 для успешного подключения.</string>
|
||||
<string name="user_push_notification_agreement_argument_one">Будьте в курсе новых функций и новостей</string>
|
||||
<string name="user_push_notification_agreement_argument_three">Получайте уведомления о входящих транзакциях</string>
|
||||
<string name="user_push_notification_agreement_argument_two">Узнавайте первым о новых акциях</string>
|
||||
<string name="user_push_notification_agreement_header">Хотите использовать Push-уведомления?</string>
|
||||
<string name="user_wallet_list_add_button">Добавить новый кошелек</string>
|
||||
|
|
@ -1174,6 +1180,7 @@
|
|||
<string name="wallet_promo_banner_button_title">Получить с 10% скидкой</string>
|
||||
<string name="wallet_promo_banner_description">Получите доступ к более чем 13 000 криптовалют. Покупайте, продавайте, обменивайте и стейкайте в один клик. Свяжите до трех карт для резервного копирования. </string>
|
||||
<string name="wallet_promo_banner_title">Откройте Tangem Wallet</string>
|
||||
<string name="wallet_settings_push_notifications_description">Получайте уведомления о входящих транзакциях в кошельке и обновлениях Tangem.</string>
|
||||
<string name="wallet_settings_push_notifications_title">Уведомления о транзакциях</string>
|
||||
<string name="wallet_settings_title">Настройки кошелька</string>
|
||||
<string name="wallet_title">Tangem</string>
|
||||
|
|
@ -1202,7 +1209,7 @@
|
|||
<string name="warning_express_approval_in_progress_message">Разрешение обмена в процессе и будет скоро завершено</string>
|
||||
<string name="warning_express_approval_in_progress_title">Разрешение в процессе</string>
|
||||
<string name="warning_express_dust_message">Минимальная сумма обмена - %1$s. Пожалуйста, убедитесь, что остаток после обмена также не будет меньше %2$s.</string>
|
||||
<string name="warning_express_no_exchangeable_coins_description">У вас в списке нет монет доступных для обмена с %s</string>
|
||||
<string name="warning_express_no_exchangeable_coins_description">У вас нет токенов в портфеле, на которые можно обменять %s. Пожалуйста, добавьте другой токен, чтобы выполнить обмен.</string>
|
||||
<string name="warning_express_no_exchangeable_coins_title">Нет доступных для обмена токенов</string>
|
||||
<string name="warning_express_not_enough_fee_for_token_tx_description">Чтобы совершить транзакцию, вам необходимо внести немного %1$s %2$s</string>
|
||||
<string name="warning_express_not_enough_fee_for_token_tx_title">Невозможно покрыть комиссию %s</string>
|
||||
|
|
@ -1246,6 +1253,7 @@
|
|||
<string name="warning_rate_app_message">Ваш отзыв мотивирует нас сделать кошелек Tangem еще лучше</string>
|
||||
<string name="warning_rate_app_title">Нравится Tangem?</string>
|
||||
<string name="warning_receive_blocked_hedera_token_association_required_message">Вам необходимо провести ассоциацию токена для того, чтобы иметь возможность принимать его</string>
|
||||
<string name="warning_receive_blocked_token_trustline_required_message">Вы должны открыть трастлайн для своего токена, чтобы иметь возможность получить на него средства.</string>
|
||||
<string name="warning_rent_fee_title">Необходима плата за аренду сети</string>
|
||||
<string name="warning_seedphrase_action_required_title">Требуется действие</string>
|
||||
<string name="warning_seedphrase_contacted_support">Вы обращались в службу поддержки через приложение в течение 7 дней после создания кошелька? Если обращались или не уверены, нажмите «Да» и следуйте инструкциям.</string>
|
||||
|
|
@ -1261,28 +1269,71 @@
|
|||
<string name="warning_some_networks_unreachable_message">Свайпните вниз для обновления или попробуйте позже.</string>
|
||||
<string name="warning_some_networks_unreachable_title">Некоторые сети недоступны</string>
|
||||
<string name="warning_some_token_balances_not_updated">Некоторые балансы токенов не удалось обновить</string>
|
||||
<string name="warning_stellar_token_trustline_button_title">Открыть Trustline</string>
|
||||
<string name="warning_stellar_token_trustline_subtitle">Чтобы получить этот токен, необходимо открыть Trustline. Сеть удержит резерв в размере 0.5 XLM.</string>
|
||||
<string name="warning_stellar_token_trustline_title">Откройте Trustline</string>
|
||||
<string name="warning_stellar_token_trustline_not_enough_xlm">Недостаточно %s. Пополните свой XLM аккаунт чтобы открыть трастлайн.</string>
|
||||
<string name="warning_testnet_card_message">Это Testnet карта. Он не может обрабатывать транзакции и используется только в целях тестирования и разработки.</string>
|
||||
<string name="warning_testnet_card_title">Только для целей тестирования</string>
|
||||
<string name="warning_token_balance_not_updated">Возможно, баланс устарел. Обновите страницу.</string>
|
||||
<string name="warning_token_trustline_button_title">Открыть Trustline</string>
|
||||
<string name="warning_token_trustline_subtitle">Согласно правилам сети, чтобы пополнить этот токен, необходимо сначала открыть Trustline — это позволит вашему кошельку принимать и хранить этот актив.</string>
|
||||
<string name="warning_token_trustline_title">Откройте Trustline</string>
|
||||
<string name="warning_token_trustline_title">Требуется трастлайн</string>
|
||||
<string name="wc_alert_audit_malicious_domain">Вредоносный домен</string>
|
||||
<string name="wc_alert_audit_unknown_domain">Неизвестный домен</string>
|
||||
<string name="wc_alert_connect_anyway">Подключиться всё равно</string>
|
||||
<string name="wc_alert_connection_timeout_description">Ошибка тайм-аута. Пожалуйста, попробуйте позже.</string>
|
||||
<string name="wc_alert_connection_timeout_title">Не удалось подключиться через Wallet Connect</string>
|
||||
<string name="wc_alert_domain_issues_description">Этот домен не может быть верифицирован. Внимательно проверьте запрос перед одобрением.</string>
|
||||
<string name="wc_alert_session_disconnected_description">Пожалуйста, вернитесь в браузер и выполните повторное подключение через WalletConnect.</string>
|
||||
<string name="wc_alert_session_disconnected_title">Сессия Wallet Connect была завершена</string>
|
||||
<string name="wc_alert_sign_anyway">Подписать всё равно</string>
|
||||
<string name="wc_alert_unknown_error_description">Код ошибки: %s. Если проблема сохраняется, обратитесь в нашу службу поддержки.</string>
|
||||
<string name="wc_alert_unknown_error_description_no_error_code">Если проблема сохраняется, обратитесь в нашу службу поддержки</string>
|
||||
<string name="wc_alert_unknown_error_title">Мы обнаружили неизвестную ошибку</string>
|
||||
<string name="wc_alert_unsupported_dapps_description">Кошелек Tangem.в настоящий момент не поддерживает %s</string>
|
||||
<string name="wc_alert_unsupported_dapps_title">Неподдерживаемый dApp</string>
|
||||
<string name="wc_alert_unsupported_method_title">Мы обнаружили неизвестную ошибку</string>
|
||||
<string name="wc_alert_unsupported_networks_description">Tangem в настоящее время не поддерживает необходимую сеть для %s</string>
|
||||
<string name="wc_alert_unsupported_networks_title">Неподдерживаемые сети</string>
|
||||
<string name="wc_alert_verified_domain_description">Tangem поддерживает сеть, необходимую для %s</string>
|
||||
<string name="wc_alert_verified_domain_title">Верифицированный домен</string>
|
||||
<string name="wc_alert_wrong_card_description">Выбрана не верная карта или кольцо</string>
|
||||
<string name="wc_alert_wrong_card_title">Похоже, возникла проблема</string>
|
||||
<string name="wc_allow_to_spend">Разрешение на использование</string>
|
||||
<string name="wc_common_address">Адрес</string>
|
||||
<string name="wc_common_connect">Подключение</string>
|
||||
<string name="wc_common_loading">Загрузка</string>
|
||||
<string name="wc_common_network">Сеть</string>
|
||||
<string name="wc_common_networks">Сети</string>
|
||||
<string name="wc_common_unlimited">Безлимитно</string>
|
||||
<string name="wc_common_wallet">Кошелек</string>
|
||||
<string name="wc_connected_networks">Подключенные сети</string>
|
||||
<string name="wc_connection_reqeust_can_view_balance">Посмотреть баланс кошелька и его активность</string>
|
||||
<string name="wc_connection_reqeust_cant_sign">Подписать транзакцию без вашего участия</string>
|
||||
<string name="wc_connection_reqeust_request_approval">Запрос разрешения на транзакцию</string>
|
||||
<string name="wc_connection_reqeust_will_not">Не имеет возможности</string>
|
||||
<string name="wc_connection_reqeust_would_like">Хотели бы</string>
|
||||
<string name="wc_connection_request">Запрос на подключение</string>
|
||||
<string name="wc_connections">Подключения</string>
|
||||
<string name="wc_contents">Вложение</string>
|
||||
<string name="wc_copy_data_button_text">Копировать данные</string>
|
||||
<string name="wc_custom_allowance_title">Настраиваемый лимит</string>
|
||||
<string name="wc_disconnect_all">Отключить все</string>
|
||||
<string name="wc_disconnect_all_alert_title">Отключить все dApp</string>
|
||||
<string name="wc_estimated_wallet_changes">Предварительные изменения</string>
|
||||
<string name="wc_estimated_wallet_changes_not_simulated">Не удалось выполнить симуляцию транзакции. Пожалуйста, действуйте с осторожностью.</string>
|
||||
<string name="wc_malicious_transaction">Вредоносная транзакция</string>
|
||||
<string name="wc_missing_required_network_description">Добавьте сеть %s в ваш портфель для выбранного кошелька</string>
|
||||
<string name="wc_missing_required_network_title">В кошельке не добавлены необходимые сети</string>
|
||||
<string name="wc_new_connection">Новое подключение</string>
|
||||
<string name="wc_no_sessions_desc">Подключите свой кошелек к различным dApp</string>
|
||||
<string name="wc_no_sessions_title">Нет подключений</string>
|
||||
<string name="wc_notification_security_risk_subtitle">Этот домен помечен как небезопасный несколькими поставщиками систем безопасности. Немедленно покиньте его, чтобы защитить свои активы.</string>
|
||||
<string name="wc_notification_security_risk_title">Известный риск безопасности</string>
|
||||
<string name="wc_request_from">Запрос от</string>
|
||||
<string name="wc_signature_type">Тип подписи</string>
|
||||
<string name="wc_transaction_info_to_title">На</string>
|
||||
<string name="wc_transaction_request">Запрос транзакции</string>
|
||||
<string name="wc_transaction_request_title">Запрос транзакции</string>
|
||||
<string name="wc_unlimited_amount">Безлимитное количество</string>
|
||||
<string name="wc_wallet_connect">Подключение кошелька</string>
|
||||
<string name="welcome_interrupted_backup_alert_discard">Отказаться</string>
|
||||
<string name="welcome_interrupted_backup_alert_message">Вы не закончили резервное копирование. Хотите продолжить?</string>
|
||||
|
|
|
|||
|
|
@ -316,6 +316,7 @@
|
|||
<string name="express_exchange_status_receiving">Очікуємо на депозит</string>
|
||||
<string name="express_exchange_status_receiving_active">Очікуємо на депозит...</string>
|
||||
<string name="express_exchange_status_refunded">Повернено</string>
|
||||
<string name="express_exchange_status_refunding">Повернення коштів</string>
|
||||
<string name="express_exchange_status_sending">Надсилаємо вам</string>
|
||||
<string name="express_exchange_status_sending_active">Надсилаємо вам...</string>
|
||||
<string name="express_exchange_status_sent">Надіслано</string>
|
||||
|
|
@ -370,6 +371,7 @@
|
|||
<string name="give_permission_swap_subtitle" formatted="false">Щоб продовжити, вам потрібно дозволити смарт-контракту %1s використовувати ваш %2s</string>
|
||||
<string name="give_permission_title">Надати дозвіл</string>
|
||||
<string name="give_permission_unlimited">Необмежено</string>
|
||||
<string name="home_button_create_new_wallet">Створити новий гаманець</string>
|
||||
<string name="home_button_order">Купити</string>
|
||||
<string name="home_button_scan">Сканувати</string>
|
||||
<string name="hot_crypto_add_token_subtitle">в %s</string>
|
||||
|
|
@ -572,10 +574,11 @@
|
|||
<string name="nft_receive_unsupported_types_description">cNFT і pNFT наразі не підтримуються. Будь ласка, не надсилайте їх на свій гаманець.</string>
|
||||
<string name="nft_send">Надіслати NFT</string>
|
||||
<string name="nft_traits_title">Риси</string>
|
||||
<string name="nft_untitled_collection">Безіменна колекція</string>
|
||||
<string name="nft_wallet_count">NFTs в %2$d колекціях%1$d</string>
|
||||
<plurals name="nft_wallet_count_android">
|
||||
<item quantity="one">%1$d NFT в %2$d колекціi</item>
|
||||
<item quantity="few"></item>
|
||||
<item quantity="few">%1$d NFTs в %2$d колекціях</item>
|
||||
<item quantity="many"></item>
|
||||
<item quantity="other">%1$d NFTs в %2$d колекціях</item>
|
||||
</plurals>
|
||||
|
|
@ -719,7 +722,11 @@
|
|||
<string name="organize_tokens_title">Сортування токенів</string>
|
||||
<string name="organize_tokens_ungroup">Список</string>
|
||||
<string name="push_notifications_more_info">Більше</string>
|
||||
<string name="push_notifications_permission_alert_description">Ви можете ввімкнути сповіщення у налаштуваннях</string>
|
||||
<string name="push_notifications_permission_alert_negative_button">Увімкнути пізніше</string>
|
||||
<string name="push_notifications_permission_alert_positive_button">Налаштування</string>
|
||||
<string name="push_notifications_permission_alert_title">Увімкнути сповіщення</string>
|
||||
<string name="push_transactions_notifications_description">Отримуйте сповіщення про вхідні транзакції в підтримуваних мережах.</string>
|
||||
<string name="qr_scanner_camera_denied_gallery_button">Виберіть з галереї</string>
|
||||
<string name="qr_scanner_camera_denied_settings_button">Налаштування</string>
|
||||
<string name="qr_scanner_camera_denied_text">Ви не надали доступ до своєї камери</string>
|
||||
|
|
@ -829,6 +836,9 @@
|
|||
<string name="send_max_fee">Комісія може сягати до</string>
|
||||
<string name="send_memo_destination_tag_error">Недопустимий Memo</string>
|
||||
<string name="send_network_fee_warning_title">Покриття мережевої комісії</string>
|
||||
<string name="send_nonce">Nonce</string>
|
||||
<string name="send_nonce_footer">Унікальний номер транзакції. Змініть його, щоб пришвидшити відправку або скасувати завислу операцію.</string>
|
||||
<string name="send_nonce_hint">Введіть nonce…</string>
|
||||
<string name="send_notification_exceed_balance_text">Недостатньо коштів для здійснення переказу, оскільки загальна сума комісії та переказу перевищує наявний баланс</string>
|
||||
<string name="send_notification_exceed_balance_title">Сума перевищує баланс</string>
|
||||
<string name="send_notification_existential_deposit_text">Для збереження вашого акаунту у блокчейні та захисту від можливих ризиків необхідний баланс не менше %s. Ця сума залишиться на вашому рахунку та не може бути знята.</string>
|
||||
|
|
@ -837,6 +847,7 @@
|
|||
<string name="send_notification_fee_too_high_title">Встановлена комісія завелика</string>
|
||||
<string name="send_notification_high_fee_text">Через особливості мережі %1$s комісія за переказ всього балансу вища. Щоб зменшити комісію, Ви можете залишити %2$s.</string>
|
||||
<string name="send_notification_high_fee_title">Підвищена комісія</string>
|
||||
<string name="send_notification_invalid_amount_rent_destination">Рахунок одержувача не активований. Мінімальна сума переказу повинна бути не менша балансу, необхідного для покриття арендної плати: %1$s.</string>
|
||||
<string name="send_notification_invalid_amount_rent_fee">Баланс вашого рахунку не може бути меншим за орендну плату. Будь ласка, залиште на рахунку не менше %1$s або виведіть всі кошти.</string>
|
||||
<string name="send_notification_invalid_amount_text">Включена комісія перевищує суму переказу, що призводить до від’ємного значення</string>
|
||||
<string name="send_notification_invalid_amount_title">Недопустима сума</string>
|
||||
|
|
@ -1095,6 +1106,8 @@
|
|||
<string name="transaction_history_transaction_validator">валідатор: %s</string>
|
||||
<string name="transfer_min_amount_error">Мінімум %s</string>
|
||||
<string name="transfer_notification_invalid_minimum_transaction_amount_text">Мінімальна сума транзакції становить %1$s.</string>
|
||||
<string name="tron_will_be_send_token_fee_description">Комісії мережі Tron для популярних токенів можуть бути вищими. Стейкінг TRX може допомогти знизити транзакційні витрати.</string>
|
||||
<string name="tron_will_be_send_token_fee_title">Заощаджуйте на Tron комісіях</string>
|
||||
<string name="try_to_load_data_again_button_title">Спробуйте знову</string>
|
||||
<string name="twin_error_same_card">Ви відсканували одну й ту саму картку. Для створення twin-гаманця вам потрібно відсканувати картку з номером %d</string>
|
||||
<string name="twin_error_wrong_twin">Ви відсканували не ту twin-картку. Будь ласка, спробуйте відсканувати іншу</string>
|
||||
|
|
@ -1111,6 +1124,7 @@
|
|||
<string name="unlock_wallet_description_full">Використовуйте %s або відскануйте картку/кільце, щоб отримати доступ до свого гаманця</string>
|
||||
<string name="unsupported_wc_version">Не вдалося встановити з\'єднання: Цей dApp використовує Wallet Connect версії 1.0, яка не підтримується. Будь ласка, переконайтеся, що dApp підтримує Wallet Connect версії 2.0 для успішного підключення.</string>
|
||||
<string name="user_push_notification_agreement_argument_one">Будьте в курсі останніх функцій та новин</string>
|
||||
<string name="user_push_notification_agreement_argument_three">Отримуйте сповіщення про вхідні транзакції</string>
|
||||
<string name="user_push_notification_agreement_argument_two">Дізнавайтеся першими про нові акції</string>
|
||||
<string name="user_push_notification_agreement_header">Бажаєте використовувати Push-повідомлення?</string>
|
||||
<string name="user_wallet_list_add_button">Додати новий гаманець</string>
|
||||
|
|
@ -1122,6 +1136,7 @@
|
|||
<string name="user_wallet_list_rename_popup_title">Перейменування гаманця</string>
|
||||
<string name="user_wallet_list_unlock_all">Розблокувати все</string>
|
||||
<string name="user_wallet_list_unlock_all_with">Розблокувати все з %s</string>
|
||||
<string name="visa_onboarding_pin_not_accepted">PIN-код не прийнятий. Спробуйте ще раз або введіть інший код.</string>
|
||||
<string name="wallet_balance_blockchain_unreachable_try_later">Блокчейн недоступний. Спробуйте пізніше</string>
|
||||
<string name="wallet_balance_missing_derivation">Відскануйте картку або кільце</string>
|
||||
<string name="wallet_been_activated_message">Цей гаманець вже був активований раніше.\nЯкщо це було зроблено не вами, зверніться до служби підтримки.\nTangem ніколи не продає гаманці разом з попередньо згенерованим кодом доступу.</string>
|
||||
|
|
@ -1165,6 +1180,7 @@
|
|||
<string name="wallet_promo_banner_button_title">Отримати з 10% знижкою</string>
|
||||
<string name="wallet_promo_banner_description">Доступ до 13,000+ криптовалют. Купуйте, продавайте, обмінюйте та стейкайте одним дотиком.\nЗ’єднайте до трьох карток для бекапу.</string>
|
||||
<string name="wallet_promo_banner_title">Відкрийте Tangem Wallet</string>
|
||||
<string name="wallet_settings_push_notifications_description">Отримуйте сповіщення про вхідні транзакції в гаманці та оновлення Tangem.</string>
|
||||
<string name="wallet_settings_push_notifications_title">Сповіщення про транзакції</string>
|
||||
<string name="wallet_settings_title">Налаштування гаманця</string>
|
||||
<string name="wallet_title">Tangem</string>
|
||||
|
|
@ -1193,7 +1209,7 @@
|
|||
<string name="warning_express_approval_in_progress_message">Дозвіл обміну триває і незабаром буде завершено</string>
|
||||
<string name="warning_express_approval_in_progress_title">Затвердження в процесі</string>
|
||||
<string name="warning_express_dust_message">Мінімальна сума обміну становить - %1$s. Будь ласка, переконайтеся, що залишок на рахунку після обміну буде не менше за %2$s.</string>
|
||||
<string name="warning_express_no_exchangeable_coins_description">У вашому списку немає доступних монет для обміну %s</string>
|
||||
<string name="warning_express_no_exchangeable_coins_description">У вашому портфелі немає токенів, на які можна обміняти %s. Будь ласка, додайте інший токен, щоб виконати обмін.</string>
|
||||
<string name="warning_express_no_exchangeable_coins_title">Немає доступних токенів для обміну</string>
|
||||
<string name="warning_express_not_enough_fee_for_token_tx_description">Щоб здійснити транзакцію, вам потрібно внести трохи %1$s %2$s</string>
|
||||
<string name="warning_express_not_enough_fee_for_token_tx_title">Неможливо покрити комісію %s</string>
|
||||
|
|
@ -1257,8 +1273,23 @@
|
|||
<string name="warning_testnet_card_message">Це картка Testnet. Вона не може обробляти транзакції і повинна використовуватися лише для тестування та розробки.</string>
|
||||
<string name="warning_testnet_card_title">Лише для цілей тестування</string>
|
||||
<string name="warning_token_balance_not_updated">Баланс може бути застарілим. Оновіть сторінку.</string>
|
||||
<string name="warning_token_trustline_button_title">Відкрити Trustline</string>
|
||||
<string name="warning_token_trustline_subtitle">Відповідно до правил мережі, щоб поповнити цей токен, спочатку потрібно відкрити Trustline — це дозволить вашому гаманцю приймати та зберігати цей актив.</string>
|
||||
<string name="warning_token_trustline_title">Відкрийте Trustline</string>
|
||||
<string name="wc_alert_session_disconnected_description">Будь ласка, поверніться до браузеру і повторно підключіться через WalletConnect.</string>
|
||||
<string name="wc_alert_wrong_card_description">Обрана не вірна картка або кільце</string>
|
||||
<string name="wc_common_address">Адреса</string>
|
||||
<string name="wc_common_connect">Підключення</string>
|
||||
<string name="wc_connection_reqeust_can_view_balance">Переглянути баланс гаманця та активність</string>
|
||||
<string name="wc_connection_request">Запит на підключення</string>
|
||||
<string name="wc_contents">Вміст</string>
|
||||
<string name="wc_copy_data_button_text">Копіювати дані</string>
|
||||
<string name="wc_request_from">Запит від</string>
|
||||
<string name="wc_signature_type">Тип підпису</string>
|
||||
<string name="wc_transaction_info_to_title">До</string>
|
||||
<string name="wc_transaction_request">Запит транзакції</string>
|
||||
<string name="wc_transaction_request_title">Запит транзакції</string>
|
||||
<string name="wc_wallet_connect">Підключення гаманця</string>
|
||||
<string name="welcome_interrupted_backup_alert_discard">Відмовитися</string>
|
||||
<string name="welcome_interrupted_backup_alert_message">Ви не завершили резервне копіювання. Бажаєте продовжити?</string>
|
||||
<string name="welcome_interrupted_backup_alert_resume">Так, поновити</string>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@
|
|||
<string name="access_code_alert_skip_ok">Skip anyway</string>
|
||||
<string name="access_code_alert_skip_title">Access Code not set</string>
|
||||
<string name="access_code_check_title">Enter Access Code</string>
|
||||
<string name="access_code_check_warining_delete">Wrong access code. Your hot wallet will be deleted after %s more incorrect attempts.</string>
|
||||
<string name="access_code_check_warining_lock">Wrong access code. App will be locked with %s more input errors</string>
|
||||
<string name="access_code_check_warining_wait">Wrong access code.\nPlease wait %s seconds and try again.</string>
|
||||
<string name="access_code_confirm_description">Confirm your previously entered code to continue</string>
|
||||
<string name="access_code_confirm_title">Re-enter Access Code</string>
|
||||
<string name="access_code_create_description">Set a %s-digit Access Code to unlock your wallet.</string>
|
||||
|
|
@ -58,14 +61,19 @@
|
|||
<string name="app_settings_theme_mode_system">System default</string>
|
||||
<string name="app_settings_theme_selector_title">Theme</string>
|
||||
<string name="app_settings_title">App settings</string>
|
||||
<string name="auth_info_add_wallet_title">Add Wallet</string>
|
||||
<string name="auth_info_subtitle">Select a wallet to log in</string>
|
||||
<string name="auth_info_title">Welcome back!</string>
|
||||
<string name="backup_complete_description">You successfully backed up your wallet.</string>
|
||||
<string name="backup_complete_seed_description">These words can’t be recovered if lost. Make sure to keep it somewhere secure.</string>
|
||||
<string name="backup_complete_title">Backup Completed</string>
|
||||
<string name="backup_info_description">Your Secret Recovery Phrase is a fixed set of %s random words used to access and recover your wallet.</string>
|
||||
<string name="backup_info_keep_description">These words can’t be recovered if lost. Make sure to keep it somewhere secure.</string>
|
||||
<string name="backup_info_keep_description">These words can’t be recovered if lost. Make sure to keep it somewhere secure.</string>
|
||||
<string name="backup_info_keep_title">Keep It Safe</string>
|
||||
<string name="backup_info_save_description">Save these %s words in a secure location, such as a password manager, and never share them with anyone.</string>
|
||||
<string name="backup_info_save_title">No Recovery Possible</string>
|
||||
<string name="backup_info_title">Recovery phrase</string>
|
||||
<string name="backup_seed_caution">Never share these words. Anyone who learns them can steal all of your crypto. Tangem will never ask you for them. The %s words below are your wallet\'s recovery phrase. This phrase lets you recover your wallet if you lose your device.</string>
|
||||
<string name="backup_seed_description">Write down these %s words in order and keep them safe and private</string>
|
||||
<string name="backup_seed_responsibility">Full responsibility for the security and backup of the wallet and recovery phrase lies with the user, not with Tangem.</string>
|
||||
<string name="backup_seed_title">Recovery phrase</string>
|
||||
|
|
@ -417,8 +425,8 @@
|
|||
<string name="hw_activation_need_description">To complete setup, back up your wallet and secure app access with a Access Code.</string>
|
||||
<string name="hw_activation_need_finish">Finish Now</string>
|
||||
<string name="hw_activation_need_title">Finish Wallet Activation</string>
|
||||
<string name="hw_backup_icloud_description">Recover an existing wallet stored in your iCloud backup</string>
|
||||
<string name="hw_backup_icloud_title">iCloud backup</string>
|
||||
<string name="hw_backup_google_drive_description">Recover an existing wallet stored in your Google Drive backup</string>
|
||||
<string name="hw_backup_google_drive_title">Google Drive Backup</string>
|
||||
<string name="hw_backup_need_action">Go to backup</string>
|
||||
<string name="hw_backup_need_description">To secure your wallet with a Access Code, complete the backup first.</string>
|
||||
<string name="hw_backup_need_title">Finish Backup First</string>
|
||||
|
|
@ -430,6 +438,7 @@
|
|||
<string name="hw_create_seed_description">Stay up to date with the latest features and news</string>
|
||||
<string name="hw_create_seed_title">Seed phrase backup</string>
|
||||
<string name="hw_create_title">Create Mobile Wallet</string>
|
||||
<string name="hw_mobile_wallet">Mobile Wallet</string>
|
||||
<string name="information_generated_with_ai">This information was generated with AI.\nTap here, if you find any errors.</string>
|
||||
<string name="initial_message_change_access_code_body">To change the access code tap the card or ring as shown above and do not remove until the end of the operation</string>
|
||||
<string name="initial_message_change_passcode_body">To change the passcode tap the card as shown above and do not remove until the end of the operation</string>
|
||||
|
|
@ -664,7 +673,7 @@
|
|||
<string name="onboarding_button_continue_wallet">Continue to my wallet</string>
|
||||
<string name="onboarding_button_finalize_backup">Finalize backup</string>
|
||||
<string name="onboarding_button_receive_crypto">Receive crypto</string>
|
||||
<string name="onboarding_button_scan_origin_card">Scan primary card</string>
|
||||
<string name="onboarding_button_scan_origin_card">Scan primary card or ring</string>
|
||||
<string name="onboarding_button_skip_backup">Skip for later</string>
|
||||
<string name="onboarding_button_what_does_it_mean">How does it work?</string>
|
||||
<string name="onboarding_create_wallet_body">Let\'s generate all the keys on your card or ring and create a secure wallet</string>
|
||||
|
|
@ -717,7 +726,7 @@
|
|||
<string name="onboarding_title_no_backup_cards">No backup devices</string>
|
||||
<string name="onboarding_title_notifications">Notifications</string>
|
||||
<string name="onboarding_title_one_backup_card">One backup device added</string>
|
||||
<string name="onboarding_title_scan_origin_card">Prepare your card</string>
|
||||
<string name="onboarding_title_scan_origin_card">Prepare your card or ring</string>
|
||||
<string name="onboarding_title_two_backup_cards">Two backup devices added</string>
|
||||
<string name="onboarding_top_up_body">To get started, simply top up the wallet with any amount</string>
|
||||
<string name="onboarding_top_up_body_no_account_error">To get started, simply top up the wallet with more than %1$s %2$s</string>
|
||||
|
|
@ -937,7 +946,9 @@
|
|||
<string name="send_validation_invalid_amount">Invalid amount</string>
|
||||
<string name="send_validation_invalid_fee">Fee exceeds balance</string>
|
||||
<string name="send_validation_invalid_total">Total amount exceeds balance</string>
|
||||
<string name="send_with_swap_confirm_title">Swap and send</string>
|
||||
<string name="send_with_swap_notification_text">Send any token, and we’ll convert it on the way. Your recipient gets exactly what they need—seamlessly.</string>
|
||||
<string name="send_with_swap_recipient_amount_success_title">Recipient will receive</string>
|
||||
<string name="send_with_swap_recipient_amount_text">Will be sent a recipient</string>
|
||||
<string name="send_with_swap_recipient_amount_title">Amount to receive</string>
|
||||
<string name="send_with_swap_title">Send with swap</string>
|
||||
|
|
@ -1357,8 +1368,8 @@
|
|||
<string name="warning_express_approval_in_progress_message">Swap approval is underway and will be completed shortly</string>
|
||||
<string name="warning_express_approval_in_progress_title">Approval in progress</string>
|
||||
<string name="warning_express_dust_message">The minimum swapping amount is %1$s. Please ensure that the remaining balance after the swap will not be less than %2$s.</string>
|
||||
<string name="warning_express_no_exchangeable_coins_description">You do not have any %s exchangeable coins in your list</string>
|
||||
<string name="warning_express_no_exchangeable_coins_title">No available tokens to swap</string>
|
||||
<string name="warning_express_no_exchangeable_coins_description">You don’t have any tokens in your portfolio that %s can be swapped to. Please add another token to enable the exchange.</string>
|
||||
<string name="warning_express_no_exchangeable_coins_title">No compatible tokens added</string>
|
||||
<string name="warning_express_not_enough_fee_for_token_tx_description">To make a transaction you need to deposit some %1$s %2$s</string>
|
||||
<string name="warning_express_not_enough_fee_for_token_tx_title">Unable to cover %s fee</string>
|
||||
<string name="warning_express_notification_invalid_reserve_amount_title">The amount to receive must be at least %s</string>
|
||||
|
|
@ -1417,10 +1428,7 @@
|
|||
<string name="warning_some_networks_unreachable_message">Swipe down to refresh or try again later.</string>
|
||||
<string name="warning_some_networks_unreachable_title">Some networks are unreachable</string>
|
||||
<string name="warning_some_token_balances_not_updated">Some token balances could not be updated</string>
|
||||
<string name="warning_stellar_token_trustline_button_title">Enable Trustline</string>
|
||||
<string name="warning_stellar_token_trustline_not_enough_xlm">Not enough %s. Top up your XLM account to associate this token</string>
|
||||
<string name="warning_stellar_token_trustline_subtitle">A Trustline must be enabled to receive this token. The network requires a 0.5 XLM reserve.</string>
|
||||
<string name="warning_stellar_token_trustline_title">Trustline Required</string>
|
||||
<string name="warning_stellar_token_trustline_not_enough_xlm">Not enough %s. Top up your XLM account to open trustline.</string>
|
||||
<string name="warning_testnet_card_message">This is a Testnet card. It cannot process transactions and should only be used for testing and development purposes.</string>
|
||||
<string name="warning_testnet_card_title">For testing purposes only</string>
|
||||
<string name="warning_token_balance_not_updated">Balance may be outdated. Refresh the page.</string>
|
||||
|
|
@ -1440,6 +1448,8 @@
|
|||
<string name="wc_alert_unknown_error_description">Error code: %s. If the problem persists — feel free to contact our support.</string>
|
||||
<string name="wc_alert_unknown_error_description_no_error_code">If the problem persists — feel free to contact our support.</string>
|
||||
<string name="wc_alert_unknown_error_title">We\'ve encountered unknown error</string>
|
||||
<string name="wc_alert_unsupported_dapps_description">Tangem Wallet currently doesn’t support %s.</string>
|
||||
<string name="wc_alert_unsupported_dapps_title">Unsupported dApp</string>
|
||||
<string name="wc_alert_unsupported_method_description">Error code: 8 005. If the problem persists — feel free to contact our support.</string>
|
||||
<string name="wc_alert_unsupported_method_title">We\'ve encountered unknown error</string>
|
||||
<string name="wc_alert_unsupported_networks_description">Tangem does not currently support a required network by %s.</string>
|
||||
|
|
@ -1473,7 +1483,7 @@
|
|||
<string name="wc_estimated_wallet_changes">Estimated wallet changes</string>
|
||||
<string name="wc_estimated_wallet_changes_not_simulated">The transaction couldn\'t be simulated. Please proceed with caution.</string>
|
||||
<string name="wc_malicious_transaction">Malicious transaction</string>
|
||||
<string name="wc_missing_required_network_description">Add the %s network to your profile for this wallet</string>
|
||||
<string name="wc_missing_required_network_description">Add the %s network to your portfolio for this wallet</string>
|
||||
<string name="wc_missing_required_network_title">The wallet has no required networks</string>
|
||||
<string name="wc_new_connection">New connection</string>
|
||||
<string name="wc_no_sessions_desc">Connect your wallet to a different dApps</string>
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import androidx.compose.runtime.Immutable
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
|
|
@ -29,6 +30,7 @@ import com.tangem.core.ui.components.fields.SimpleDialogTextField
|
|||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.DialogTestTags
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
|
@ -216,7 +218,8 @@ private fun TangemDialog(
|
|||
shape = TangemTheme.shapes.roundedCornersLarge,
|
||||
color = TangemTheme.colors.background.primary,
|
||||
)
|
||||
.padding(vertical = TangemTheme.dimens.spacing24),
|
||||
.padding(vertical = TangemTheme.dimens.spacing24)
|
||||
.testTag(DialogTestTags.DIALOG_CONTAINER),
|
||||
) {
|
||||
if (title != null) {
|
||||
Text(
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.core.ui.components
|
|||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.PixelFormat
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import androidx.compose.runtime.*
|
||||
|
|
@ -18,7 +19,12 @@ import androidx.savedstate.setViewTreeSavedStateRegistryOwner
|
|||
import java.util.UUID
|
||||
|
||||
@Composable
|
||||
fun FullScreen(notTouchable: Boolean = false, content: @Composable (() -> Unit) -> Unit) {
|
||||
fun FullScreen(
|
||||
notTouchable: Boolean = false,
|
||||
focusable: Boolean = false,
|
||||
onBackClick: () -> Unit = {},
|
||||
content: @Composable (() -> Unit) -> Unit,
|
||||
) {
|
||||
val view = LocalView.current
|
||||
val parentComposition = rememberCompositionContext()
|
||||
val currentContent by rememberUpdatedState(content)
|
||||
|
|
@ -27,7 +33,9 @@ fun FullScreen(notTouchable: Boolean = false, content: @Composable (() -> Unit)
|
|||
val fullScreenLayout = remember {
|
||||
FullScreenLayout(
|
||||
notTouchable = notTouchable,
|
||||
focusable = focusable,
|
||||
composeView = view,
|
||||
onBackClick = onBackClick,
|
||||
uniqueId = id,
|
||||
).apply {
|
||||
setContent(parentComposition) {
|
||||
|
|
@ -45,7 +53,9 @@ fun FullScreen(notTouchable: Boolean = false, content: @Composable (() -> Unit)
|
|||
@SuppressLint("ViewConstructor", "ClickableViewAccessibility")
|
||||
private class FullScreenLayout(
|
||||
private val notTouchable: Boolean,
|
||||
private val focusable: Boolean,
|
||||
private val composeView: View,
|
||||
private val onBackClick: () -> Unit,
|
||||
uniqueId: UUID,
|
||||
) : AbstractComposeView(composeView.context) {
|
||||
|
||||
|
|
@ -97,7 +107,10 @@ private class FullScreenLayout(
|
|||
fun show() {
|
||||
if (viewShowing) dismiss()
|
||||
windowManager.addView(this, params)
|
||||
params.flags = params.flags or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
|
||||
if (focusable.not()) {
|
||||
params.flags = params.flags or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
}
|
||||
|
||||
if (notTouchable) {
|
||||
params.flags = params.flags or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
|
||||
|
|
@ -107,6 +120,19 @@ private class FullScreenLayout(
|
|||
viewShowing = true
|
||||
}
|
||||
|
||||
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
||||
if (focusable.not()) {
|
||||
return super.dispatchKeyEvent(event)
|
||||
}
|
||||
|
||||
if (event.keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) {
|
||||
onBackClick()
|
||||
return true
|
||||
}
|
||||
|
||||
return super.dispatchKeyEvent(event)
|
||||
}
|
||||
|
||||
fun dismiss() {
|
||||
if (!viewShowing) return
|
||||
disposeComposition()
|
||||
|
|
|
|||
|
|
@ -3,10 +3,7 @@ package com.tangem.core.ui.components.bottomsheets.modal
|
|||
import android.content.res.Configuration
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
|
|
@ -20,6 +17,7 @@ import com.tangem.core.ui.components.buttons.small.TangemIconButton
|
|||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
||||
|
|
@ -30,6 +28,7 @@ import com.tangem.core.ui.res.TangemThemePreview
|
|||
fun TangemModalBottomSheetTitle(
|
||||
modifier: Modifier = Modifier,
|
||||
title: TextReference? = null,
|
||||
subtitle: TextReference? = null,
|
||||
@DrawableRes startIconRes: Int? = null,
|
||||
onStartClick: (() -> Unit)? = null,
|
||||
@DrawableRes endIconRes: Int? = null,
|
||||
|
|
@ -49,13 +48,23 @@ fun TangemModalBottomSheetTitle(
|
|||
.align(Alignment.CenterStart),
|
||||
)
|
||||
}
|
||||
if (title != null) {
|
||||
Text(
|
||||
text = title.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
)
|
||||
Column(modifier = Modifier.align(Alignment.Center)) {
|
||||
if (title != null) {
|
||||
Text(
|
||||
text = title.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||
)
|
||||
}
|
||||
if (subtitle != null) {
|
||||
Text(
|
||||
text = subtitle.resolveReference(),
|
||||
style = TangemTheme.typography.caption1,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||
)
|
||||
}
|
||||
}
|
||||
if (endIconRes != null && onEndClick != null) {
|
||||
TangemIconButton(
|
||||
|
|
@ -79,6 +88,7 @@ private fun Preview_TangemModalBottomSheetTitle(
|
|||
TangemThemePreview {
|
||||
TangemModalBottomSheetTitle(
|
||||
title = params.title,
|
||||
subtitle = params.subtitle,
|
||||
startIconRes = params.startIconRes,
|
||||
onStartClick = params.onStartClick,
|
||||
endIconRes = params.endIconRes,
|
||||
|
|
@ -90,6 +100,7 @@ private fun Preview_TangemModalBottomSheetTitle(
|
|||
|
||||
private data class TangemModalBottomSheetTitleData(
|
||||
val title: TextReference?,
|
||||
val subtitle: TextReference?,
|
||||
val startIconRes: Int?,
|
||||
val onStartClick: (() -> Unit)?,
|
||||
val endIconRes: Int?,
|
||||
|
|
@ -104,6 +115,7 @@ private class TangemModalBottomSheetTitleProvider : PreviewParameterProvider<Tan
|
|||
onStartClick = {},
|
||||
endIconRes = null,
|
||||
onEndClick = null,
|
||||
subtitle = null,
|
||||
),
|
||||
TangemModalBottomSheetTitleData(
|
||||
title = resourceReference(R.string.wallet_title),
|
||||
|
|
@ -111,6 +123,7 @@ private class TangemModalBottomSheetTitleProvider : PreviewParameterProvider<Tan
|
|||
onStartClick = null,
|
||||
endIconRes = R.drawable.ic_close_24,
|
||||
onEndClick = {},
|
||||
subtitle = null,
|
||||
),
|
||||
TangemModalBottomSheetTitleData(
|
||||
title = resourceReference(R.string.wallet_title),
|
||||
|
|
@ -118,6 +131,23 @@ private class TangemModalBottomSheetTitleProvider : PreviewParameterProvider<Tan
|
|||
onStartClick = {},
|
||||
endIconRes = R.drawable.ic_close_24,
|
||||
onEndClick = {},
|
||||
subtitle = null,
|
||||
),
|
||||
TangemModalBottomSheetTitleData(
|
||||
title = resourceReference(R.string.wallet_title),
|
||||
subtitle = stringReference("Today • 2:37 PM"),
|
||||
startIconRes = R.drawable.ic_back_24,
|
||||
onStartClick = {},
|
||||
endIconRes = R.drawable.ic_close_24,
|
||||
onEndClick = {},
|
||||
),
|
||||
TangemModalBottomSheetTitleData(
|
||||
title = null,
|
||||
subtitle = stringReference("Today • 2:37 PM"),
|
||||
startIconRes = R.drawable.ic_back_24,
|
||||
onStartClick = {},
|
||||
endIconRes = R.drawable.ic_close_24,
|
||||
onEndClick = {},
|
||||
),
|
||||
TangemModalBottomSheetTitleData(
|
||||
title = null,
|
||||
|
|
@ -125,6 +155,7 @@ private class TangemModalBottomSheetTitleProvider : PreviewParameterProvider<Tan
|
|||
onStartClick = {},
|
||||
endIconRes = R.drawable.ic_close_24,
|
||||
onEndClick = {},
|
||||
subtitle = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
|
|
@ -24,6 +25,7 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.compose.ui.unit.sp
|
||||
import com.tangem.core.ui.components.ResizableText
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.test.DialogTestTags
|
||||
import com.tangem.core.ui.utils.MultipleClickPreventer
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
|
|
@ -47,7 +49,9 @@ fun TangemButton(
|
|||
val multipleClickPreventer = remember { MultipleClickPreventer.get() }
|
||||
|
||||
Button(
|
||||
modifier = modifier.heightIn(min = size.toHeightDp()),
|
||||
modifier = modifier
|
||||
.heightIn(min = size.toHeightDp())
|
||||
.testTag(DialogTestTags.BUTTON),
|
||||
onClick = {
|
||||
multipleClickPreventer.processEvent { if (!showProgress) onClick() }
|
||||
},
|
||||
|
|
|
|||
|
|
@ -7,12 +7,14 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.PopUpMenuTestTags
|
||||
|
||||
@Suppress("ComposableEventParameterNaming")
|
||||
@Composable
|
||||
|
|
@ -23,7 +25,8 @@ fun TangemDropdownItem(item: TangemDropdownMenuItem, dismissParent: () -> Unit,
|
|||
dismissParent()
|
||||
item.onClick()
|
||||
}
|
||||
.padding(vertical = TangemTheme.dimens.spacing8, horizontal = TangemTheme.dimens.spacing16),
|
||||
.padding(vertical = TangemTheme.dimens.spacing8, horizontal = TangemTheme.dimens.spacing16)
|
||||
.testTag(PopUpMenuTestTags.BUTTON),
|
||||
text = item.title.resolveReference(),
|
||||
style = TangemTheme.typography.button.copy(color = item.textColorProvider()),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -17,11 +17,13 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.graphics.TransformOrigin
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.unit.*
|
||||
import androidx.compose.ui.window.Popup
|
||||
import androidx.compose.ui.window.PopupPositionProvider
|
||||
import androidx.compose.ui.window.PopupProperties
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.test.PopUpMenuTestTags
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
|
|
@ -138,7 +140,8 @@ private fun DropdownMenuContent(
|
|||
Column(
|
||||
modifier = modifier
|
||||
.width(IntrinsicSize.Max)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
.verticalScroll(rememberScrollState())
|
||||
.testTag(PopUpMenuTestTags.CONTAINER),
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ fun PinTextField(
|
|||
isPasswordVisual: Boolean,
|
||||
onValueChange: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
wrongCode: Boolean = false,
|
||||
) {
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val textFieldValue = remember(value) {
|
||||
|
|
@ -71,6 +72,7 @@ fun PinTextField(
|
|||
CellDecoration(
|
||||
length = length,
|
||||
isPasswordVisual = isPasswordVisual,
|
||||
wrongCode = wrongCode,
|
||||
value = value,
|
||||
)
|
||||
},
|
||||
|
|
@ -86,6 +88,7 @@ fun PinTextField(
|
|||
@Composable
|
||||
private fun CellDecoration(
|
||||
length: Int,
|
||||
wrongCode: Boolean,
|
||||
value: String,
|
||||
modifier: Modifier = Modifier,
|
||||
isPasswordVisual: Boolean = false,
|
||||
|
|
@ -107,7 +110,7 @@ private fun CellDecoration(
|
|||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors.background.tertiary,
|
||||
color = TangemTheme.colors.field.primary,
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
),
|
||||
contentAlignment = Alignment.Center,
|
||||
|
|
@ -128,7 +131,11 @@ private fun CellDecoration(
|
|||
modifier = Modifier.sizeIn(minWidth = width.size.width.dp + 8.dp, minHeight = 48.dp),
|
||||
text = text,
|
||||
style = TangemTheme.typography.h3,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
color = if (wrongCode) {
|
||||
TangemTheme.colors.text.warning
|
||||
} else {
|
||||
TangemTheme.colors.text.primary1
|
||||
},
|
||||
textAlign = TextAlign.Center,
|
||||
lineHeight = 48.sp,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,26 +5,32 @@ import androidx.compose.foundation.background
|
|||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.inputrow.inner.DividerContainer
|
||||
import com.tangem.core.ui.components.fields.SimpleTextField
|
||||
import com.tangem.core.ui.components.inputrow.inner.DividerContainer
|
||||
import com.tangem.core.ui.components.tooltip.TangemTooltip
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
||||
/**
|
||||
* [InputRowEnter](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=2100-799&mode=design&t=IQ5lBJEkFGU4WSvi-4)
|
||||
|
|
@ -51,6 +57,7 @@ fun InputRowEnter(
|
|||
onValueChange: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
placeholder: TextReference? = null,
|
||||
description: TextReference? = null,
|
||||
titleColor: Color = TangemTheme.colors.text.secondary,
|
||||
textColor: Color = TangemTheme.colors.text.primary1,
|
||||
isSingleLine: Boolean = false,
|
||||
|
|
@ -71,11 +78,32 @@ fun InputRowEnter(
|
|||
.padding(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = title.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = titleColor,
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||
) {
|
||||
Text(
|
||||
text = title.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = titleColor,
|
||||
)
|
||||
if (description != null) {
|
||||
TangemTooltip(
|
||||
modifier = Modifier
|
||||
.size(16.dp)
|
||||
.clip(CircleShape),
|
||||
text = description.resolveReference(),
|
||||
content = { contentModifier ->
|
||||
Icon(
|
||||
modifier = contentModifier.size(16.dp),
|
||||
painter = painterResource(R.drawable.ic_token_info_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
SimpleTextField(
|
||||
value = text,
|
||||
onValueChange = onValueChange,
|
||||
|
|
@ -121,6 +149,7 @@ private fun InputRowEnterPreview(
|
|||
text = data.text,
|
||||
iconRes = data.iconRes,
|
||||
showDivider = data.showDivider,
|
||||
description = stringReference(""),
|
||||
onValueChange = {},
|
||||
modifier = Modifier.background(TangemTheme.colors.background.action),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.core.ui.components.inputrow
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.Icon
|
||||
|
|
@ -8,6 +9,7 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -15,6 +17,7 @@ import com.tangem.core.ui.R
|
|||
import com.tangem.core.ui.components.fields.AmountTextField
|
||||
import com.tangem.core.ui.components.fields.visualtransformations.AmountVisualTransformation
|
||||
import com.tangem.core.ui.components.inputrow.inner.DividerContainer
|
||||
import com.tangem.core.ui.components.tooltip.TangemTooltip
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -118,6 +121,7 @@ fun InputRowEnterInfoAmountV2(
|
|||
modifier: Modifier = Modifier,
|
||||
symbol: String? = null,
|
||||
info: TextReference? = null,
|
||||
description: TextReference? = null,
|
||||
titleColor: Color = TangemTheme.colors.text.secondary,
|
||||
textColor: Color = TangemTheme.colors.text.primary1,
|
||||
infoColor: Color = TangemTheme.colors.text.tertiary,
|
||||
|
|
@ -133,7 +137,9 @@ fun InputRowEnterInfoAmountV2(
|
|||
paddingValues = PaddingValues(horizontal = 12.dp),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth().padding(16.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(2.dp)) {
|
||||
Text(
|
||||
|
|
@ -141,12 +147,22 @@ fun InputRowEnterInfoAmountV2(
|
|||
style = TangemTheme.typography.subtitle2,
|
||||
color = titleColor,
|
||||
)
|
||||
Icon(
|
||||
modifier = Modifier.size(16.dp),
|
||||
painter = painterResource(R.drawable.ic_token_info_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
if (description != null) {
|
||||
TangemTooltip(
|
||||
modifier = Modifier
|
||||
.size(16.dp)
|
||||
.clip(CircleShape),
|
||||
text = description.resolveReference(),
|
||||
content = { contentModifier ->
|
||||
Icon(
|
||||
modifier = contentModifier.size(16.dp),
|
||||
painter = painterResource(R.drawable.ic_token_info_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
Row {
|
||||
AmountTextField(
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ fun InputRowRecipient(
|
|||
) {
|
||||
if (isRedesignEnabled) {
|
||||
QrButton(
|
||||
visible = !singleLine,
|
||||
visible = value.isBlank(),
|
||||
onQrCodeClick = onQrCodeClick,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
|||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
|
|
@ -33,6 +34,8 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
private const val DISABLED_ICON_ALPHA = 0.4f
|
||||
|
||||
/**
|
||||
* Provider Row - Choose Crypto component
|
||||
*
|
||||
|
|
@ -43,14 +46,17 @@ import kotlinx.collections.immutable.persistentListOf
|
|||
* @see <a href="https://www.figma.com/design/JJuqr3gX9IC4WBv3C95uqj/Android--Copy-?node-id=3467-2643&t=Tg0OOJTQK9H4KBWL-4">Figma</a>
|
||||
*/
|
||||
@Composable
|
||||
@Suppress("DestructuringDeclarationWithTooManyEntries")
|
||||
@Suppress("DestructuringDeclarationWithTooManyEntries", "LongMethod")
|
||||
fun ProviderChooseCrypto(providerChooseUM: ProviderChooseUM, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
ConstraintLayout(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.clip(RoundedCornerShape(14.dp))
|
||||
.selectedBorder(isSelected = providerChooseUM.isSelected)
|
||||
.clickable(enabled = true, onClick = onClick)
|
||||
.clickable(
|
||||
enabled = !providerChooseUM.hasError(),
|
||||
onClick = onClick,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.padding(all = 12.dp),
|
||||
) {
|
||||
|
|
@ -60,6 +66,13 @@ fun ProviderChooseCrypto(providerChooseUM: ProviderChooseUM, onClick: () -> Unit
|
|||
IconContent(
|
||||
iconUrl = providerChooseUM.iconUrl,
|
||||
modifier = Modifier
|
||||
.alpha(
|
||||
if (providerChooseUM.hasError()) {
|
||||
DISABLED_ICON_ALPHA
|
||||
} else {
|
||||
1.0f
|
||||
},
|
||||
)
|
||||
.constrainAs(iconRef) {
|
||||
start.linkTo(parent.start)
|
||||
top.linkTo(parent.top)
|
||||
|
|
@ -140,7 +153,11 @@ private fun TitleContent(providerChooseUM: ProviderChooseUM, modifier: Modifier
|
|||
Text(
|
||||
text = providerChooseUM.title.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
color = if (providerChooseUM.hasError()) {
|
||||
TangemTheme.colors.text.secondary
|
||||
} else {
|
||||
TangemTheme.colors.text.primary1
|
||||
},
|
||||
maxLines = 1,
|
||||
)
|
||||
Text(
|
||||
|
|
|
|||
|
|
@ -52,4 +52,6 @@ data class ProviderChooseUM(
|
|||
val text: TextReference,
|
||||
) : ExtraUM()
|
||||
}
|
||||
|
||||
fun hasError(): Boolean = extraUM is ExtraUM.Error
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ import androidx.compose.ui.layout.Measurable
|
|||
import androidx.compose.ui.layout.Placeable
|
||||
import androidx.compose.ui.layout.layoutId
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
|
|
@ -30,6 +31,7 @@ import com.tangem.core.ui.extensions.stringReference
|
|||
import com.tangem.core.ui.res.TangemColorPalette
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.TokenElementsTestTags
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import org.burnoutcrew.reorderable.ReorderableLazyListState
|
||||
import java.util.UUID
|
||||
|
|
@ -100,7 +102,8 @@ fun TokenItem(
|
|||
state = state.iconState,
|
||||
modifier = Modifier
|
||||
.layoutId(layoutId = LayoutId.ICON)
|
||||
.padding(end = TangemTheme.dimens.spacing8),
|
||||
.padding(end = TangemTheme.dimens.spacing8)
|
||||
.testTag(TokenElementsTestTags.TOKEN_ICON),
|
||||
)
|
||||
|
||||
TokenTitle(
|
||||
|
|
@ -108,7 +111,8 @@ fun TokenItem(
|
|||
modifier = Modifier
|
||||
.layoutId(layoutId = LayoutId.TITLE)
|
||||
.padding(end = TangemTheme.dimens.spacing8)
|
||||
.padding(bottom = betweenRowsMargin),
|
||||
.padding(bottom = betweenRowsMargin)
|
||||
.testTag(TokenElementsTestTags.TOKEN_TITLE),
|
||||
)
|
||||
|
||||
TokenFiatAmount(
|
||||
|
|
@ -116,26 +120,32 @@ fun TokenItem(
|
|||
isBalanceHidden = isBalanceHidden,
|
||||
modifier = Modifier
|
||||
.layoutId(layoutId = LayoutId.FIAT_AMOUNT)
|
||||
.padding(bottom = betweenRowsMargin),
|
||||
.padding(bottom = betweenRowsMargin)
|
||||
.testTag(TokenElementsTestTags.TOKEN_FIAT_AMOUNT),
|
||||
)
|
||||
|
||||
TokenPrice(
|
||||
state = state.subtitleState,
|
||||
modifier = Modifier
|
||||
.layoutId(layoutId = LayoutId.CRYPTO_PRICE)
|
||||
.padding(end = TangemTheme.dimens.spacing8),
|
||||
.padding(end = TangemTheme.dimens.spacing8)
|
||||
.testTag(TokenElementsTestTags.TOKEN_PRICE),
|
||||
)
|
||||
|
||||
TokenCryptoAmount(
|
||||
state = state.subtitle2State,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
modifier = Modifier.layoutId(layoutId = LayoutId.CRYPTO_AMOUNT),
|
||||
modifier = Modifier
|
||||
.layoutId(layoutId = LayoutId.CRYPTO_AMOUNT)
|
||||
.testTag(TokenElementsTestTags.TOKEN_CRYPTO_AMOUNT),
|
||||
)
|
||||
|
||||
NonFiatContentBlock(
|
||||
state = state,
|
||||
reorderableTokenListState = reorderableTokenListState,
|
||||
modifier = Modifier.layoutId(layoutId = LayoutId.NON_FIAT_CONTENT),
|
||||
modifier = Modifier
|
||||
.layoutId(layoutId = LayoutId.NON_FIAT_CONTENT)
|
||||
.testTag(TokenElementsTestTags.TOKEN_NON_FIAT_BLOCK),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,12 +9,14 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.test.OrganizeTokensScreenTestTags
|
||||
import org.burnoutcrew.reorderable.ReorderableLazyListState
|
||||
import org.burnoutcrew.reorderable.detectReorder
|
||||
|
||||
|
|
@ -53,7 +55,8 @@ private fun DraggableImage(reorderableTokenListState: ReorderableLazyListState?)
|
|||
} else {
|
||||
Modifier
|
||||
},
|
||||
),
|
||||
)
|
||||
.testTag(OrganizeTokensScreenTestTags.DRAGGABLE_IMAGE),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
package com.tangem.core.ui.components.tooltip
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.extensions.clickableSingle
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun TangemTooltip(text: String, content: @Composable (Modifier) -> Unit, modifier: Modifier = Modifier) {
|
||||
val tooltipState = rememberTooltipState(isPersistent = true)
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
TooltipBox(
|
||||
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(spacingBetweenTooltipAndAnchor = 8.dp),
|
||||
state = tooltipState,
|
||||
modifier = modifier,
|
||||
tooltip = {
|
||||
PlainTooltip(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 12.dp),
|
||||
caretSize = DpSize(width = 14.dp, height = 8.dp),
|
||||
contentColor = TangemTheme.colors.text.primary2,
|
||||
containerColor = TangemTheme.colors.icon.secondary,
|
||||
content = {
|
||||
Text(
|
||||
modifier = Modifier.background(TangemTheme.colors.icon.secondary),
|
||||
text = text,
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.primary2,
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
content = {
|
||||
content(
|
||||
Modifier.clickableSingle(
|
||||
onClick = { coroutineScope.launch { tooltipState.show() } },
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun TangemTooltip_Preview() {
|
||||
TangemThemePreview {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(500.dp)
|
||||
.background(TangemTheme.colors.background.secondary),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
TangemTooltip(
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors.background.secondary)
|
||||
.size(64.dp),
|
||||
text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed venenatis.",
|
||||
content = { contentModifier ->
|
||||
Icon(
|
||||
modifier = contentModifier.size(64.dp),
|
||||
painter = painterResource(R.drawable.ic_token_info_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,4 +9,13 @@ fun interface ComposableContentComponent {
|
|||
|
||||
@Composable
|
||||
fun Content(modifier: Modifier)
|
||||
}
|
||||
|
||||
fun getEmptyComposableContentComponent() = EmptyComposableContentComponent
|
||||
|
||||
object EmptyComposableContentComponent : ComposableContentComponent {
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
/* no-op */
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object DetailsScreenTestTags {
|
||||
const val SCREEN_CONTAINER = "DETAILS_SCREEN_CONTAINER"
|
||||
const val SCREEN_ITEM = "DETAILS_SCREEN_ITEM"
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object DialogTestTags {
|
||||
const val DIALOG_CONTAINER = "DIALOG_CONTAINER"
|
||||
const val BUTTON = "DIALOG_BUTTON"
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object DisclaimerScreenTestTags {
|
||||
const val SCREEN_CONTAINER = "DISCLAIMER_SCREEN_CONTAINER"
|
||||
const val ACCEPT_BUTTON = "DISCLAIMER_SCREEN_ACCEPT_BUTTON"
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object MainScreenTestTags {
|
||||
const val SCREEN_CONTAINER = "MAIN_SCREEN_CONTAINER"
|
||||
const val MORE_BUTTON = "MAIN_SCREEN_MORE_BUTTON"
|
||||
const val TOP_BAR = "MAIN_SCREEN_TOP_BAR"
|
||||
const val TOKEN_LIST_ITEM = "MAIN_SCREEN_TOKEN_LIST_ITEM"
|
||||
const val WALLET_BALANCE = "MAIN_SCREEN_WALLET_BALANCE"
|
||||
const val WALLET_LIST_ITEM = "MAIN_SCREEN_WALLET_LIST_ITEM"
|
||||
const val ORGANIZE_TOKENS_BUTTON = "MAIN_SCREEN_ORGANIZE_TOKENS_BUTTON"
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object MarketTooltipTestTags {
|
||||
const val CONTAINER = "MARKETS_TOOLTIP_CONTAINER"
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object OrganizeTokensScreenTestTags {
|
||||
// region OrganizeTokensTopBar
|
||||
const val GROUP_BUTTON = "ORGANIZE_TOKENS_GROUP_BUTTON"
|
||||
const val SORT_BY_BALANCE_BUTTON = "SORT_BY_BALANCE_BUTTON"
|
||||
// endregion OrganizeTokensTopBar
|
||||
|
||||
const val TOKENS_LAZY_LIST = "ORGANIZE_TOKENS_TOKENS_LAZY_LIST"
|
||||
const val TOKEN_LIST_ITEM = "ORGANIZE_TOKENS_TOKEN_LIST_ITEM"
|
||||
const val GROUP_TITLE_ITEM = "ORGANIZE_TOKENS_GROUP_TITLE_ITEM"
|
||||
const val DRAGGABLE_IMAGE = "ORGANIZE_TOKENS_DRAGGABLE_IMAGE"
|
||||
|
||||
const val APPLY_BUTTON = "ORGANIZE_TOKENS_APPLY_BUTTON"
|
||||
const val CANCEL_BUTTON = "ORGANIZE_TOKENS_CANCEL_BUTTON"
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object PopUpMenuTestTags {
|
||||
const val CONTAINER = "POP_UP_MENU_CONTAINER"
|
||||
const val BUTTON = "POP_UP_MENU_BUTTON"
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object StoriesScreenTestTags {
|
||||
const val SCREEN_CONTAINER = "STORIES_SCREEN_CONTAINER"
|
||||
const val SCAN_BUTTON = "STORIES_SCREEN_SCAN_BUTTON"
|
||||
const val ORDER_BUTTON = "STORIES_SCREEN_ORDER_BUTTON"
|
||||
const val CREATE_NEW_WALLET_BUTTON = "STORIES_SCREEN_CREATE_NEW_WALLET_BUTTON"
|
||||
const val ADD_EXISTING_WALLET_BUTTON = "STORIES_SCREEN_ADD_EXISTING_WALLET_BUTTON"
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object TestTags {
|
||||
const val STORIES_SCREEN = "STORIES_SCREEN_CONTAINER"
|
||||
const val STORIES_SCREEN_SCAN_BUTTON = "STORIES_SCREEN_SCAN_BUTTON"
|
||||
const val STORIES_SCREEN_ORDER_BUTTON = "STORIES_SCREEN_ORDER_BUTTON"
|
||||
const val STORIES_SCREEN_CREATE_NEW_WALLET_BUTTON = "STORIES_SCREEN_CREATE_NEW_WALLET_BUTTON"
|
||||
const val STORIES_SCREEN_ADD_EXISTING_WALLET_BUTTON = "STORIES_SCREEN_ADD_EXISTING_WALLET_BUTTON"
|
||||
|
||||
const val MAIN_SCREEN = "MAIN_SCREEN_CONTAINER"
|
||||
const val MAIN_SCREEN_MORE_BUTTON = "MAIN_SCREEN_MORE_BUTTON"
|
||||
const val MAIN_SCREEN_TOP_BAR = "MAIN_SCREEN_TOP_BAR"
|
||||
|
||||
const val DETAILS_SCREEN = "DETAILS_SCREEN_CONTAINER"
|
||||
const val DETAILS_SCREEN_ITEM = "DETAILS_SCREEN_ITEM"
|
||||
|
||||
const val WALLET_SETTINGS_SCREEN = "WALLET_SETTINGS_SCREEN"
|
||||
const val WALLET_SETTINGS_SCREEN_ITEM = "WALLET_SETTINGS_SCREEN_ITEM"
|
||||
|
||||
const val DISCLAIMER_SCREEN_CONTAINER = "DISCLAIMER_SCREEN_CONTAINER"
|
||||
const val DISCLAIMER_SCREEN_ACCEPT_BUTTON = "DISCLAIMER_SCREEN_ACCEPT_BUTTON"
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object TokenDetailsScreenTestTags {
|
||||
const val SCREEN_CONTAINER = "TOKEN_DETAILS_SCREEN_CONTAINER"
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object TokenDetailsTopBarTestTags {
|
||||
const val MORE_BUTTON = "TOKEN_DETAILS_TOP_BAR_MORE_BUTTON"
|
||||
const val BACK_BUTTON = "TOKEN_DETAILS_TOP_BAR_BACK_BUTTON"
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object TokenElementsTestTags {
|
||||
const val TOKEN_TITLE = "TOKEN_TITLE"
|
||||
const val TOKEN_ICON = "TOKEN_ICON"
|
||||
const val TOKEN_PRICE = "TOKEN_PRICE"
|
||||
const val TOKEN_FIAT_AMOUNT = "TOKEN_FIAT_AMOUNT"
|
||||
const val TOKEN_CRYPTO_AMOUNT = "TOKEN_CRYPTO_AMOUNT"
|
||||
const val TOKEN_NON_FIAT_BLOCK = "TOKEN_NON_FIAT_BLOCK"
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object WalletSettingsScreenTestTags {
|
||||
const val SCREEN_CONTAINER = "WALLET_SETTINGS_SCREEN_CONTAINER"
|
||||
const val SCREEN_ITEM = "WALLET_SETTINGS_SCREEN_ITEM"
|
||||
}
|
||||
29
core/ui/src/main/java/com/tangem/core/ui/utils/LazyList.kt
Normal file
29
core/ui/src/main/java/com/tangem/core/ui/utils/LazyList.kt
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
package com.tangem.core.ui.utils
|
||||
|
||||
import androidx.compose.ui.semantics.SemanticsPropertyKey
|
||||
import androidx.compose.ui.semantics.SemanticsPropertyReceiver
|
||||
|
||||
/**
|
||||
* Semantics key for storing and retrieving the visual position
|
||||
* of an item within a LazyList. Used to verify item ordering
|
||||
* and visibility in UI tests.
|
||||
*/
|
||||
val LazyListItemPositionSemantics = SemanticsPropertyKey<Int>("LazyListItemPosition")
|
||||
|
||||
/**
|
||||
* Provides access to the item position semantics property
|
||||
* through semantics property receiver DSL.
|
||||
*/
|
||||
var SemanticsPropertyReceiver.lazyListItemPosition by LazyListItemPositionSemantics
|
||||
|
||||
/**
|
||||
* Semantics key for storing the total length/count of items
|
||||
* in a LazyList. Enables validation of list completeness in tests.
|
||||
*/
|
||||
val LazyListLengthSemantics = SemanticsPropertyKey<Int>("LazyListLength")
|
||||
|
||||
/**
|
||||
* Provides access to the list length semantics property
|
||||
* through semantics property receiver DSL.
|
||||
*/
|
||||
var SemanticsPropertyReceiver.lazyListLength by LazyListLengthSemantics
|
||||
10
core/ui/src/main/res/drawable/ic_airplane_mode_24.xml
Normal file
10
core/ui/src/main/res/drawable/ic_airplane_mode_24.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M4.429,11.919L7.745,9.916C8.302,9.581 8.58,9.413 8.772,9.183C9.603,8.187 8.871,6.715 8.995,5.557C9.124,4.358 10.276,2.718 11.437,2.21C11.795,2.053 12.205,2.053 12.562,2.21C13.724,2.718 14.876,4.358 15.005,5.557C15.128,6.715 14.396,8.187 15.228,9.183C15.42,9.413 15.698,9.581 16.255,9.916L19.571,11.919C20.587,12.532 20.986,13.186 20.986,14.417C20.986,15.087 20.688,15.267 20.085,15.13L14.269,13.81L14.008,16.077C13.913,16.9 13.866,17.311 14.003,17.687C14.324,18.569 15.412,19.292 16.077,19.933C16.445,20.288 16.846,21.307 16.427,21.771C16.168,22.057 15.748,21.825 15.459,21.713L12.674,20.639C12.341,20.51 12.175,20.446 12,20.446C11.825,20.446 11.659,20.51 11.326,20.639L8.541,21.713C8.252,21.825 7.832,22.057 7.573,21.771C7.154,21.307 7.555,20.288 7.923,19.933C8.587,19.292 9.676,18.569 9.997,17.687C10.134,17.311 10.086,16.9 9.992,16.077L9.731,13.81L3.915,15.13C3.312,15.267 3.013,15.087 3.014,14.417C3.014,13.186 3.414,12.532 4.429,11.919Z"
|
||||
android:fillColor="#ffffff"
|
||||
android:fillAlpha="0.8" />
|
||||
</vector>
|
||||
14
core/ui/src/main/res/drawable/ic_beach_24.xml
Normal file
14
core/ui/src/main/res/drawable/ic_beach_24.xml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M11.718,2.094C12.233,1.947 12.77,2.245 12.917,2.76C12.999,3.048 13.063,3.105 13.364,3.07C16.263,2.731 19.023,4.362 20.32,7.038C20.445,7.294 20.576,7.552 20.651,7.829C20.743,8.166 20.761,8.598 20.489,9.02C20.196,9.475 19.751,9.617 19.409,9.656C19.142,9.687 18.826,9.666 18.559,9.649L17.175,9.56C17.04,9.551 17.038,9.552 16.927,9.63L15.723,10.481C15.616,10.556 15.563,10.594 15.542,10.651C15.522,10.708 15.538,10.771 15.572,10.897L16.59,14.713C16.669,15.01 16.708,15.158 16.627,15.238C16.545,15.317 16.398,15.273 16.103,15.184L14.898,14.822C14.594,14.73 14.585,14.721 14.503,14.414L13.764,11.642C13.729,11.512 13.712,11.448 13.663,11.408C13.615,11.368 13.548,11.364 13.414,11.355L11.372,11.224C11.236,11.215 11.234,11.216 11.123,11.295L9.987,12.097C9.758,12.259 9.492,12.448 9.244,12.564C8.931,12.711 8.458,12.832 7.956,12.572C7.49,12.331 7.297,11.922 7.22,11.577C7.156,11.289 7.151,10.991 7.141,10.697C7.043,7.885 8.482,5.191 10.885,3.866C11.176,3.706 11.139,3.596 11.052,3.293C10.905,2.778 11.203,2.241 11.718,2.094Z"
|
||||
android:fillColor="#ffffff"
|
||||
android:fillAlpha="0.8" />
|
||||
<path
|
||||
android:pathData="M6.859,14.906C9.254,14.906 11.414,15.554 13.548,16.193C15.658,16.826 17.769,17.458 20.228,17.594C20.697,17.62 20.932,17.633 21.065,17.773C21.197,17.913 21.197,18.139 21.197,18.59V20.724C21.197,21.181 21.197,21.41 21.055,21.552C20.913,21.694 20.685,21.694 20.228,21.694H2.772C2.315,21.694 2.087,21.694 1.945,21.552C1.803,21.41 1.803,21.181 1.803,20.724V16.328C1.803,15.989 1.803,15.82 1.902,15.69C2.001,15.56 2.155,15.517 2.463,15.432C2.694,15.367 2.928,15.311 3.166,15.261C4.024,15.082 5.271,14.906 6.859,14.906Z"
|
||||
android:fillColor="#ffffff"
|
||||
android:fillAlpha="0.8" />
|
||||
</vector>
|
||||
10
core/ui/src/main/res/drawable/ic_bookmark_24.xml
Normal file
10
core/ui/src/main/res/drawable/ic_bookmark_24.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M11.943,2.75C13.661,2.75 15.145,2.75 16.217,2.898C17.326,3.051 18.228,3.375 18.939,4.106C19.647,4.833 19.959,5.753 20.106,6.882C20.25,7.98 20.25,9.383 20.25,11.153L20.25,16.542V16.543C20.25,17.643 20.25,18.552 20.152,19.233C20.052,19.927 19.82,20.621 19.147,21.004C18.534,21.353 17.86,21.279 17.302,21.099C16.74,20.916 16.172,20.587 15.656,20.24L13.085,18.313C12.71,18.072 12.476,17.923 12.288,17.829C12.018,17.674 11.791,17.764 11.712,17.829C11.524,17.923 11.29,18.072 10.915,18.313L8.344,20.24C7.828,20.587 7.26,20.916 6.698,21.099C6.14,21.279 5.466,21.353 4.853,21.004C4.18,20.621 3.948,19.927 3.848,19.233C3.75,18.552 3.75,17.643 3.75,16.543V16.542L3.75,11.153C3.75,9.383 3.75,7.98 3.894,6.882C4.041,5.753 4.353,4.833 5.061,4.106C5.772,3.375 6.674,3.051 7.783,2.898C8.855,2.75 10.224,2.75 11.943,2.75H11.943Z"
|
||||
android:fillColor="#ffffff"
|
||||
android:fillAlpha="0.8" />
|
||||
</vector>
|
||||
11
core/ui/src/main/res/drawable/ic_circle_clock_24.xml
Normal file
11
core/ui/src/main/res/drawable/ic_circle_clock_24.xml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M12,2.459C17.269,2.459 21.541,6.731 21.541,12C21.541,17.269 17.269,21.541 12,21.541C6.731,21.541 2.459,17.269 2.459,12C2.459,6.731 6.731,2.459 12,2.459ZM12,7.562C11.51,7.562 11.113,7.96 11.113,8.45V12C11.113,12.235 11.206,12.461 11.373,12.627L13.148,14.403C13.494,14.749 14.056,14.749 14.403,14.403C14.749,14.056 14.749,13.494 14.403,13.148L12.887,11.632V8.45C12.887,7.96 12.49,7.562 12,7.562Z"
|
||||
android:fillColor="#ffffff"
|
||||
android:fillAlpha="0.8"
|
||||
android:fillType="evenOdd" />
|
||||
</vector>
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue