Updated on 2026-08-14

This commit is contained in:
Tangem 2022-06-29 16:42:56 +03:00
commit 7593bd0c36
94 changed files with 1146 additions and 974 deletions

View file

@ -93,7 +93,7 @@ dependencies {
implementation 'com.google.android.play:core-ktx:1.8.1'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
implementation 'com.tangem:blockchain:develop-88'
implementation 'com.tangem:blockchain:develop-93'
// implementation 'com.tangem:blockchain:0.0.1'
implementation 'com.tangem.tangem-sdk-kotlin:core:develop-154'
implementation 'com.tangem.tangem-sdk-kotlin:android:develop-154'
@ -118,8 +118,9 @@ dependencies {
// Camera
implementation 'com.otaliastudios:cameraview:2.6.3'
// Image Loading - Picasso
implementation 'com.squareup.picasso:picasso:2.71828'
// Image Loading
implementation 'io.coil-kt:coil:2.1.0'
implementation 'io.coil-kt:coil-compose:2.1.0'
// Firebase
implementation platform('com.google.firebase:firebase-bom:26.0.0')
@ -172,9 +173,6 @@ dependencies {
implementation "com.github.skydoves:androidveil:1.1.2"
implementation("io.coil-kt:coil:2.0.0-rc01")
implementation("io.coil-kt:coil-compose:2.0.0-rc01")
implementation 'com.github.kirich1409:viewbindingpropertydelegate-noreflection:1.5.6'
testImplementation 'junit:junit:4.13.2'

View file

@ -1,6 +1,8 @@
package com.tangem.tap
import android.app.Application
import coil.ImageLoader
import coil.ImageLoaderFactory
import com.appsflyer.AppsFlyerLib
import com.google.firebase.ktx.Firebase
import com.google.firebase.remoteconfig.ktx.remoteConfig
@ -10,7 +12,7 @@ import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder
import com.tangem.domain.DomainLayer
import com.tangem.network.common.MoshiConverter
import com.tangem.tap.common.analytics.GlobalAnalyticsHandler
import com.tangem.tap.common.images.PicassoHelper
import com.tangem.tap.common.images.createCoilImageLoader
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.appReducer
import com.tangem.tap.common.redux.global.GlobalAction
@ -43,7 +45,7 @@ lateinit var currenciesRepository: CurrenciesRepository
lateinit var walletConnectRepository: WalletConnectRepository
lateinit var shopService: TangemShopService
class TapApplication : Application() {
class TapApplication : Application(), ImageLoaderFactory {
override fun onCreate() {
super.onCreate()
@ -61,7 +63,6 @@ class TapApplication : Application() {
NetworkConnectivity.createInstance(store, this)
preferencesStorage = PreferencesStorage(this)
PicassoHelper.initPicassoWithCaching(this)
currenciesRepository = CurrenciesRepository(
this, store.state.domainNetworks.tangemTechService
)
@ -75,6 +76,10 @@ class TapApplication : Application() {
initAppsFlyer()
}
override fun newImageLoader(): ImageLoader {
return createCoilImageLoader(context = this)
}
private fun loadConfigs() {
val moshi = MoshiConverter.defaultMoshi()
val localLoader = FeaturesLocalLoader(this, moshi)
@ -108,11 +113,6 @@ class TapApplication : Application() {
}
data class LogConfig(
// disables both [internal, http]
val picasso: Boolean = BuildConfig.DEBUG,
val picassoInternal: Boolean = true,
val picassoHttp: Boolean = true,
val coil: Boolean = BuildConfig.DEBUG,
val storeAction: Boolean = BuildConfig.DEBUG,
)

View file

@ -4,13 +4,10 @@ import android.app.Dialog
import android.content.Context
import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.common.redux.global.GlobalState
import com.tangem.tap.common.ui.SimpleAlertDialog
import com.tangem.tap.common.ui.SimpleCancelableAlertDialog
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectDialog
import com.tangem.tap.features.details.ui.walletconnect.dialogs.ApproveWcSessionDialog
import com.tangem.tap.features.details.ui.walletconnect.dialogs.BnbTransactionDialog
import com.tangem.tap.features.details.ui.walletconnect.dialogs.ClipboardOrScanQrDialog
import com.tangem.tap.features.details.ui.walletconnect.dialogs.PersonalSignDialog
import com.tangem.tap.features.details.ui.walletconnect.dialogs.SimpleAlertDialog
import com.tangem.tap.features.details.ui.walletconnect.dialogs.TransactionDialog
import com.tangem.tap.features.details.ui.walletconnect.dialogs.*
import com.tangem.tap.features.onboarding.AddressInfoBottomSheetDialog
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
import com.tangem.tap.features.onboarding.products.twins.ui.dialog.CreateWalletInterruptDialog
@ -19,12 +16,8 @@ import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.AddMoreBack
import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.BackupInProgressDialog
import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.ConfirmDiscardingBackupDialog
import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.UnfinishedBackupFoundDialog
import com.tangem.tap.features.wallet.redux.WalletDialog
import com.tangem.tap.features.wallet.ui.dialogs.AmountToSendBottomSheetDialog
import com.tangem.tap.features.wallet.ui.dialogs.ChooseTradeActionBottomSheetDialog
import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog
import com.tangem.tap.features.wallet.ui.dialogs.SignedHashesWarningDialog
import com.tangem.tap.features.wallet.ui.dialogs.SimpleOkDialog
import com.tangem.tap.features.wallet.redux.models.WalletDialog
import com.tangem.tap.features.wallet.ui.dialogs.*
import com.tangem.tap.features.wallet.ui.wallet.CurrencySelectionDialog
import com.tangem.tap.store
import com.tangem.wallet.R
@ -72,6 +65,15 @@ class DialogManager : StoreSubscriber<GlobalState> {
messageRes = R.string.wallet_connect_scanner_error_no_ethereum_wallet,
context = context
)
is WalletConnectDialog.AddNetwork ->
SimpleAlertDialog.create(
titleRes = R.string.wallet_connect,
message = context.getString(
R.string.wallet_connect_network_not_found_format,
state.dialog.network
),
context = context
)
is WalletConnectDialog.OpeningSessionRejected -> {
SimpleAlertDialog.create(
titleRes = R.string.wallet_connect,
@ -115,6 +117,20 @@ class DialogManager : StoreSubscriber<GlobalState> {
AmountToSendBottomSheetDialog(context, state.dialog)
is WalletDialog.SignedHashesMultiWalletDialog ->
SignedHashesWarningDialog.create(context)
is WalletDialog.TokensAreLinkedDialog -> SimpleAlertDialog.create(
title = context.getString(state.dialog.titleRes, state.dialog.currencySymbol),
message = context.getString(
state.dialog.messageRes, state.dialog.currencySymbol, state.dialog.currencyTitle
),
context = context,
)
is WalletDialog.RemoveWalletDialog -> SimpleCancelableAlertDialog.create(
title = context.getString(state.dialog.titleRes, state.dialog.currencyTitle),
messageRes = state.dialog.messageRes,
context = context,
primaryButtonRes = state.dialog.primaryButtonRes,
primaryButtonAction = state.dialog.action
)
else -> null
}
dialog?.show()

View file

@ -62,6 +62,7 @@ fun Blockchain.getNetworkName(): String {
Blockchain.Ethereum, Blockchain.EthereumTestnet -> "ERC20"
Blockchain.BSC, Blockchain.BSCTestnet -> "BEP20"
Blockchain.Binance, Blockchain.BinanceTestnet -> "BEP2"
Blockchain.Tron, Blockchain.TronTestnet -> "TRC20"
else -> ""
}
}

View file

@ -1,135 +0,0 @@
package com.tangem.tap.common.extensions
import android.graphics.*
import android.widget.TextView
import androidx.constraintlayout.utils.widget.ImageFilterView
import com.squareup.picasso.Callback
import com.squareup.picasso.Picasso
import com.squareup.picasso.Transformation
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.IconsUtil
import com.tangem.blockchain.common.Token
import com.tangem.domain.common.extensions.toNetworkId
import com.tangem.tap.domain.extensions.getCustomIconUrl
import com.tangem.tap.domain.tokens.getIconUrl
import com.tangem.wallet.R
fun Picasso.loadCurrenciesIcon(
imageView: ImageFilterView,
textView: TextView,
token: Token? = null,
blockchain: Blockchain,
) {
val url: String? = if (token != null) {
token.id?.let { getIconUrl(it) } ?: token.getCustomIconUrl()
?: IconsUtil.getTokenIconUri(blockchain, token)?.toString()
} else {
getIconUrl(blockchain.toNetworkId())
}
imageView.setImageDrawable(null)
imageView.colorFilter = null
textView.text = null
when {
token?.symbol == QCX -> {
this.load(R.drawable.ic_qcx)?.into(imageView)
}
token?.symbol == VOYR -> {
this.load(R.drawable.ic_voyr)?.into(imageView)
}
url != null -> {
if (token != null) {
setTokenImage(imageView, textView, token, blockchain)
}
this.load(url)
.transform(RoundedCornersTransform())
.noPlaceholder()
?.into(imageView,
object : Callback {
override fun onError(e: Exception?) {
setOfflineCurrencyImage(imageView, textView, token, blockchain)
}
override fun onSuccess() {
if (token != null) {
imageView.colorFilter = null
textView.text = null
}
if (blockchain.isTestnet()) imageView.saturation = 0f
}
})
}
else -> {
setOfflineCurrencyImage(imageView, textView, token, blockchain)
}
}
}
private const val QCX = "QCX"
private const val VOYR = "VOYRME"
private fun setOfflineCurrencyImage(
imageView: ImageFilterView,
textView: TextView,
token: Token?,
blockchain: Blockchain,
) {
when (token) {
null -> setBlockchainImage(imageView, textView, blockchain)
else -> setTokenImage(imageView, textView, token, blockchain)
}
}
private fun setBlockchainImage(
imageView: ImageFilterView,
textView: TextView,
blockchain: Blockchain,
) {
imageView.setImageResource(blockchain.getRoundIconRes())
imageView.colorFilter = null
if (blockchain.isTestnet()) imageView.saturation = 0f
textView.text = null
}
private fun setTokenImage(
imageView: ImageFilterView,
textView: TextView,
token: Token,
tokenBlockchain: Blockchain
) {
imageView.setImageResource(R.drawable.shape_circle)
if (tokenBlockchain.isTestnet()) {
imageView.saturation = 0f
} else {
imageView.setColorFilter(token.getColor())
}
textView.text = token.symbol.take(1)
}
private class RoundedCornersTransform : Transformation {
override fun transform(source: Bitmap): Bitmap {
val size = source.width.coerceAtMost(source.height)
val x = (source.width - size) / 2
val y = (source.height - size) / 2
val squaredBitmap = Bitmap.createBitmap(source, x, y, size, size)
if (squaredBitmap != source) source.recycle()
val paint = Paint().apply {
shader = BitmapShader(squaredBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
isAntiAlias = true
}
val rectF = RectF(0f, 0f, source.width.toFloat(), source.height.toFloat())
val radius = size / 8f
val bitmap = Bitmap.createBitmap(size, size, source.config)
Canvas(bitmap).drawRoundRect(rectF, radius, radius, paint)
squaredBitmap.recycle()
return bitmap
}
override fun key(): String = "rounded_corners"
}

View file

@ -53,8 +53,7 @@ fun BigDecimal.toFiatString(
fiatCurrencyName: String,
formatWithSpaces: Boolean = false
): String {
var fiatValue = rateValue.multiply(this)
fiatValue = fiatValue.setScale(2, RoundingMode.HALF_UP)
val fiatValue = rateValue.multiply(this)
return fiatValue.toFormattedFiatValue(fiatCurrencyName, formatWithSpaces)
}
@ -67,7 +66,8 @@ fun BigDecimal.toFormattedFiatValue(
fiatCurrencyName: String,
formatWithSpaces: Boolean = false
): String {
val fiatValue = if (formatWithSpaces) this.formatWithSpaces() else this
val fiatValue = this.setScale(2, RoundingMode.HALF_UP)
.let { if (formatWithSpaces) it.formatWithSpaces() else it }
return "${fiatValue} $fiatCurrencyName"
}

View file

@ -1,6 +1,8 @@
package com.tangem.tap.common.extensions
import android.graphics.Color
import androidx.annotation.ColorInt
import androidx.core.graphics.luminance
import androidx.core.graphics.toColorInt
import com.tangem.blockchain.common.Token
import com.tangem.wallet.R
@ -9,8 +11,13 @@ import com.tangem.wallet.R
fun Token.getColor(): Int {
return try {
("#" + this.contractAddress.subSequence(2..7).toString())
.toColorInt()
.toColorInt()
} catch (exception: Exception) {
R.color.lightGray4
}
}
@ColorInt
fun Token.getTextColor(): Int {
return if (this.getColor().luminance > 0.5) Color.BLACK else Color.WHITE
}

View file

@ -0,0 +1,50 @@
package com.tangem.tap.common.images
import android.content.Context
import android.util.Log
import coil.ImageLoader
import coil.util.Logger
import com.tangem.tap.logConfig
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import timber.log.Timber
private const val COIL_LOG_TAG = "COIL"
fun createCoilImageLoader(
context: Context
): ImageLoader {
return ImageLoader.Builder(context)
.apply {
if (!logConfig.coil) return@apply
logger(CoilTimberLogger())
okHttpClient {
OkHttpClient.Builder()
.addNetworkInterceptor(
HttpLoggingInterceptor { message ->
Timber.tag(COIL_LOG_TAG).d(message)
}
.apply {
level = HttpLoggingInterceptor.Level.BODY
}
)
.build()
}
}
.build()
}
private class CoilTimberLogger : Logger {
override var level: Int = Log.DEBUG
override fun log(tag: String, priority: Int, message: String?, throwable: Throwable?) {
with(Timber.tag(COIL_LOG_TAG)) {
throwable?.let { e -> e(e, message) }
message?.let { msg -> d(msg) }
}
}
}

View file

@ -1,68 +0,0 @@
package com.tangem.tap.common.images
import android.app.Application
import com.squareup.picasso.OkHttp3Downloader
import com.squareup.picasso.Picasso
import com.tangem.tap.logConfig
import com.tangem.wallet.BuildConfig
import okhttp3.Cache
import okhttp3.CacheControl
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import timber.log.Timber
import java.io.File
import java.util.concurrent.TimeUnit
class PicassoHelper {
companion object {
private const val KEEP_CACHE_MAX_DAYS = 7
fun initPicassoWithCaching(application: Application) {
val picasso = Picasso.Builder(application)
.downloader(OkHttp3Downloader(getOkHttpForPicasso(application)))
.build()
picasso.isLoggingEnabled = logIsEnabled()
picasso.setIndicatorsEnabled(BuildConfig.DEBUG)
Picasso.setSingletonInstance(picasso)
}
private fun getOkHttpForPicasso(application: Application): OkHttpClient {
return OkHttpClient.Builder().apply {
cache(Cache(File(application.filesDir, "artworks"), Long.MAX_VALUE))
callTimeout(15000, TimeUnit.MILLISECONDS)
if (logIsEnabled()) {
addDebugInterceptors(this)
}
addInterceptor { chain ->
val cacheControl = CacheControl.Builder()
.maxStale(KEEP_CACHE_MAX_DAYS, TimeUnit.DAYS)
.build()
val origRequest = chain.request()
val neverExpireRequest = origRequest.newBuilder()
.cacheControl(cacheControl)
.build()
chain.proceed(neverExpireRequest)
}
}.build()
}
private fun addDebugInterceptors(okHttpBuilder: OkHttpClient.Builder) {
val picassoInterceptor = HttpLoggingInterceptor(PicassoOkHttpLogger()).apply {
level = HttpLoggingInterceptor.Level.BODY
}
okHttpBuilder.addInterceptor(picassoInterceptor)
}
private fun logIsEnabled(): Boolean = BuildConfig.DEBUG && logConfig.picasso
}
}
private class PicassoOkHttpLogger : HttpLoggingInterceptor.Logger {
override fun log(message: String) {
Timber.d(message)
}
}

View file

@ -2,8 +2,8 @@ package com.tangem.tap.common.redux
import com.tangem.common.extensions.VoidCallback
import com.tangem.tap.common.TestAction
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.redux.AddressData
import com.tangem.tap.features.wallet.redux.Currency
/**
[REDACTED_AUTHOR]

View file

@ -5,6 +5,7 @@ import android.content.Intent
import com.google.android.gms.wallet.PaymentData
import com.shopify.buy3.Storefront
import com.tangem.tap.common.analytics.AnalyticsHandler
import com.tangem.tap.common.extensions.filterNotNull
import com.tangem.tap.common.shop.data.ProductType
import com.tangem.tap.common.shop.data.TangemProduct
import com.tangem.tap.common.shop.data.TotalSum
@ -21,7 +22,6 @@ class TangemShopService(application: Application, shopifyShop: ShopifyShop) {
private val shopifyService = ShopifyService(application, shopifyShop)
private lateinit var product: Storefront.Product
private val checkouts = mutableMapOf<ProductType, Storefront.Checkout>()
private val variants = mutableMapOf<ProductType, Storefront.ProductVariant>()
@ -31,22 +31,19 @@ class TangemShopService(application: Application, shopifyShop: ShopifyShop) {
suspend fun getProducts(): Result<List<TangemProduct>> {
val result = shopifyService.getProducts()
result.onSuccess {
product = it.first {
val variantsSku = it.variants.edges.map { it.node.sku }
variantsSku.contains(ProductType.WALLET_2_CARDS.sku) && variantsSku.contains(
ProductType.WALLET_3_CARDS.sku
)
}
product.variants.edges.map { it.node }
.forEach { variant ->
if (variant.sku == ProductType.WALLET_2_CARDS.sku) {
variants[ProductType.WALLET_2_CARDS] = variant
} else if (variant.sku == ProductType.WALLET_3_CARDS.sku) {
variants[ProductType.WALLET_3_CARDS] = variant
}
}
return result.mapCatching { product ->
val availableVariants = product
.flatMap { it.variants.edges.map { it.node } }
.filter { SKUS_TO_DISPLAY.contains(it.sku) }
.associateBy { ProductType.fromSku(it.sku) }
.filterNotNull()
variants.putAll(availableVariants)
if (variants.size < SKUS_TO_DISPLAY.size) {
return Result.failure(Exception(
"Shopify: products are missing, " +
"\nproducts available: ${variants.keys.map { it.sku }}"))
}
val twoCardsProduct = TangemProduct(
type = ProductType.WALLET_2_CARDS,
@ -65,7 +62,6 @@ class TangemShopService(application: Application, shopifyShop: ShopifyShop) {
createCheckouts()
return Result.success(listOf(twoCardsProduct, threeCardsProduct))
}
return Result.failure(result.exceptionOrNull()!!)
}
private suspend fun createCheckouts() {
@ -214,6 +210,7 @@ class TangemShopService(application: Application, shopifyShop: ShopifyShop) {
companion object {
const val TANGEM_WALLET_2_CARDS_SKU = "TG115x2"
const val TANGEM_WALLET_3_CARDS_SKU = "TG115x3"
val SKUS_TO_DISPLAY = listOf(TANGEM_WALLET_2_CARDS_SKU, TANGEM_WALLET_3_CARDS_SKU)
}
}

View file

@ -4,5 +4,15 @@ import com.tangem.tap.common.shop.TangemShopService
enum class ProductType(val sku: String) {
WALLET_2_CARDS(TangemShopService.TANGEM_WALLET_2_CARDS_SKU),
WALLET_3_CARDS(TangemShopService.TANGEM_WALLET_3_CARDS_SKU)
WALLET_3_CARDS(TangemShopService.TANGEM_WALLET_3_CARDS_SKU);
companion object {
fun fromSku(sku: String): ProductType? {
return when (sku) {
WALLET_2_CARDS.sku -> WALLET_2_CARDS
WALLET_3_CARDS.sku -> WALLET_3_CARDS
else -> null
}
}
}
}

View file

@ -0,0 +1,59 @@
package com.tangem.tap.common.ui
import android.content.Context
import androidx.appcompat.app.AlertDialog
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.store
import com.tangem.wallet.R
class SimpleAlertDialog {
companion object {
fun create(
titleRes: Int? = null,
messageRes: Int? = null,
title: String? = null,
message: String? = null,
primaryButtonRes: Int = R.string.common_ok,
context: Context,
): AlertDialog {
return SimpleCancelableAlertDialog.create(
titleRes = titleRes,
messageRes = messageRes,
title = title,
message = message,
primaryButtonRes = primaryButtonRes,
secondaryButtonRes = null,
context = context
)
}
}
}
class SimpleCancelableAlertDialog {
companion object {
fun create(
titleRes: Int? = null,
messageRes: Int? = null,
title: String? = null,
message: String? = null,
primaryButtonRes: Int = R.string.common_ok,
secondaryButtonRes: Int? = R.string.common_cancel,
primaryButtonAction: () -> Unit = {},
secondaryButtonAction: () -> Unit = {},
context: Context,
): AlertDialog {
return AlertDialog.Builder(context).apply {
setTitle(titleRes?.let { context.getString(it) } ?: title )
setMessage(messageRes?.let { context.getString(it) } ?: message)
setPositiveButton(context.getText(primaryButtonRes)) { _, _ -> primaryButtonAction() }
if (secondaryButtonRes != null) {
setNegativeButton(context.getText(secondaryButtonRes)) { _, _ -> secondaryButtonAction()}
}
setOnDismissListener {
store.dispatch(GlobalAction.HideDialog)
}
}.create()
}
}
}

View file

@ -3,7 +3,7 @@ package com.tangem.tap.domain
import com.tangem.common.services.Result
import com.tangem.domain.common.ThrottlerWithValues
import com.tangem.network.api.tangemTech.TangemTechService
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.store
import java.math.BigDecimal

View file

@ -26,7 +26,7 @@ import kotlinx.coroutines.withContext
class TapWalletManager {
val walletManagerFactory: WalletManagerFactory
by lazy { WalletManagerFactory(blockchainSdkConfig) }
by lazy { WalletManagerFactory(blockchainSdkConfig) }
val rates: RatesRepository = RatesRepository()
@ -34,10 +34,10 @@ class TapWalletManager {
store.state.globalState.configManager?.config?.blockchainSdkConfig ?: BlockchainSdkConfig()
}
private val walletManagersThrottler = ThrottlerWithValues<BlockchainNetwork, Result<Wallet>>(10000)
private val walletManagersThrottler =
ThrottlerWithValues<BlockchainNetwork, Result<Wallet>>(10000)
suspend fun loadWalletData(walletManager: WalletManager) {
val blockchain = walletManager.wallet.blockchain
val blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager)
val result = if (walletManagersThrottler.isStillThrottled(blockchainNetwork)) {
walletManagersThrottler.geValue(blockchainNetwork)!!
@ -54,17 +54,21 @@ class TapWalletManager {
is Result.Failure -> {
when (result.error) {
is TapError.WalletManager.NoAccountError -> {
dispatchOnMain(WalletAction.LoadWallet.NoAccount(
walletManager.wallet,
blockchainNetwork,
(result.error as TapError.WalletManager.NoAccountError).customMessage
))
dispatchOnMain(
WalletAction.LoadWallet.NoAccount(
walletManager.wallet,
blockchainNetwork,
(result.error as TapError.WalletManager.NoAccountError).customMessage
)
)
}
else -> {
dispatchOnMain(WalletAction.LoadWallet.Failure(
walletManager.wallet,
result.error.localizedMessage
))
dispatchOnMain(
WalletAction.LoadWallet.Failure(
walletManager.wallet,
result.error.localizedMessage
)
)
}
}
}
@ -109,6 +113,37 @@ class TapWalletManager {
return
}
if (data.card.isMultiwalletAllowed) {
loadMultiWalletData(data)
} else {
loadSingleWalletData(data)
}
dispatchOnMain(WalletAction.LoadWallet())
dispatchOnMain(WalletAction.LoadFiatRate())
}
private suspend fun loadMultiWalletData(
scanResponse: ScanResponse
) {
val savedCurrencies = currenciesRepository.loadSavedCurrencies(
scanResponse.card.cardId, scanResponse.card.settings.isHDWalletAllowed
)
if (savedCurrencies.isEmpty()) return
val walletManagers =
walletManagerFactory.makeWalletManagersForApp(scanResponse, savedCurrencies)
dispatchOnMain(
WalletAction.MultiWallet.AddBlockchains(savedCurrencies, walletManagers),
)
savedCurrencies.map {
if (it.tokens.isNotEmpty()) {
dispatchOnMain(WalletAction.MultiWallet.AddTokens(it.tokens, it))
}
}
}
private suspend fun loadSingleWalletData(data: ScanResponse) {
val blockchain = data.getBlockchain()
val primaryWalletManager = walletManagerFactory.makePrimaryWalletManager(data)
@ -120,87 +155,12 @@ class TapWalletManager {
primaryWalletManager.addToken(primaryToken)
dispatchOnMain(WalletAction.MultiWallet.SetPrimaryToken(primaryToken))
}
if (data.card.isMultiwalletAllowed) {
loadMultiWalletData(data, blockchain, primaryWalletManager)
} else {
dispatchOnMain(WalletAction.MultiWallet.AddBlockchains(
dispatchOnMain(
WalletAction.MultiWallet.AddBlockchains(
listOf(BlockchainNetwork.fromWalletManager(primaryWalletManager)),
listOf(primaryWalletManager)
))
}
} else {
if (data.card.isMultiwalletAllowed) {
loadMultiWalletData(data, blockchain, null)
}
}
dispatchOnMain(WalletAction.LoadWallet())
dispatchOnMain(WalletAction.LoadFiatRate())
}
private suspend fun loadMultiWalletData(
scanResponse: ScanResponse,
primaryBlockchain: Blockchain?,
primaryWalletManager: WalletManager?
) {
val primaryTokens = primaryWalletManager?.cardTokens?.toList() ?: emptyList()
val savedCurrencies = currenciesRepository.loadSavedCurrencies(
scanResponse.card.cardId, scanResponse.card.settings.isHDWalletAllowed
)
if (savedCurrencies.isEmpty()) {
if (primaryBlockchain != null && primaryWalletManager != null) {
val blockchainNetwork = BlockchainNetwork.fromWalletManager(primaryWalletManager)
dispatchOnMain(
WalletAction.MultiWallet.SaveCurrencies(listOf(blockchainNetwork)),
WalletAction.MultiWallet.AddBlockchains(
listOf(blockchainNetwork),
listOf(primaryWalletManager)
),
WalletAction.MultiWallet.AddTokens(primaryTokens.toList(), blockchainNetwork)
)
} else {
val blockchainNetworks = listOf(
BlockchainNetwork(Blockchain.Bitcoin, scanResponse.card),
BlockchainNetwork(Blockchain.Ethereum, scanResponse.card)
)
val walletManagers = walletManagerFactory.makeWalletManagersForApp(
scanResponse,
blockchainNetworks
)
dispatchOnMain(
WalletAction.MultiWallet.SaveCurrencies(blockchainNetworks),
WalletAction.MultiWallet.AddBlockchains(blockchainNetworks, walletManagers),
)
}
// dispatchOnMain(
// WalletAction.MultiWallet.FindBlockchainsInUse,
// WalletAction.MultiWallet.FindTokensInUse,
// )
} else {
val walletManagers = if (
primaryTokens.isNotEmpty() &&
primaryWalletManager != null &&
primaryBlockchain != null && primaryBlockchain != Blockchain.Unknown
) {
val blockchainsWithoutPrimary = savedCurrencies.filterNot { it.blockchain == primaryBlockchain }
walletManagerFactory.makeWalletManagersForApp(
scanResponse,
blockchainsWithoutPrimary
).plus(primaryWalletManager)
} else {
walletManagerFactory.makeWalletManagersForApp(scanResponse, savedCurrencies)
}
dispatchOnMain(
WalletAction.MultiWallet.AddBlockchains(savedCurrencies, walletManagers),
)
savedCurrencies.map {
if (it.tokens.isNotEmpty()) {
dispatchOnMain(WalletAction.MultiWallet.AddTokens(it.tokens, it))
}
}
}
}

View file

@ -1,49 +0,0 @@
package com.tangem.tap.domain
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.os.Handler
import android.os.Looper
import com.squareup.picasso.Picasso
import com.squareup.picasso.Target
import com.tangem.common.services.Result
/**
[REDACTED_AUTHOR]
*/
class UrlBitmapLoader {
private val mainHandler = Handler(Looper.getMainLooper())
fun loadBitmap(url: String, callback: (Result<Bitmap>) -> Unit) {
val target = DownloadTarget(callback)
protectedFromGarbageCollectorTargets.add(target)
mainHandler.post { Picasso.get().load(url).into(target) }
}
fun loadBitmap(url: String, target: DownloadTarget) {
protectedFromGarbageCollectorTargets.add(target)
Picasso.get().load(url).into(target)
}
}
private val protectedFromGarbageCollectorTargets = mutableListOf<Target>()
// It adds the ability to trigger multiple downloads with a unique callback.
open class DownloadTarget(
val callback: (Result<Bitmap>) -> Unit,
) : Target {
override fun onBitmapLoaded(bitmap: Bitmap, from: Picasso.LoadedFrom) {
callback(Result.Success(bitmap))
protectedFromGarbageCollectorTargets.remove(this)
}
override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) {
callback.invoke(Result.Failure(e ?: Exception("Unknown exception")))
protectedFromGarbageCollectorTargets.remove(this)
}
override fun onPrepareLoad(placeHolderDrawable: Drawable?) {
}
}

View file

@ -2,7 +2,7 @@ package com.tangem.tap.domain.extensions
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Blockchain.Arbitrum
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
import com.tangem.tap.network.exchangeServices.CurrencyExchangeStatus
import com.tangem.tap.store

View file

@ -11,7 +11,7 @@ import com.tangem.domain.common.ScanResponse
import com.tangem.domain.common.TapWorkarounds.isTestCard
import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.features.wallet.models.Currency
fun WalletManagerFactory.makeWalletManagerForApp(
scanResponse: ScanResponse,

View file

@ -112,31 +112,44 @@ private class CreateWalletTangemWallet : ProductCommandProcessor<CreateProductWa
this.card = card
val curves = card.getCurvesForNonCreatedWallets()
if (curves.isEmpty()) {
val createWalletResponses = card.wallets.map { CreateWalletResponse(card.cardId, it) }
proceedWithCreatedWallets(createWalletResponses, session, callback)
return
}
CreateWalletsTask(curves).run(session) { result ->
when (result) {
is CompletionResult.Success -> {
val createWalletResponses = result.data.createWalletResponses
when {
card.settings.isBackupAllowed -> {
linkPrimaryCard(createWalletResponses, session, callback)
}
card.settings.isHDWalletAllowed -> {
deriveKeys(createWalletResponses, session, callback)
}
else -> {
callback(
CompletionResult.Success(
CreateProductWalletTaskResponse(card = session.environment.card!!)
)
)
}
}
proceedWithCreatedWallets(result.data.createWalletResponses, session, callback)
}
is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
}
}
}
private fun proceedWithCreatedWallets(
createWalletResponses: List<CreateWalletResponse>,
session: CardSession,
callback: (result: CompletionResult<CreateProductWalletTaskResponse>) -> Unit,
) {
when {
card.settings.isBackupAllowed -> {
linkPrimaryCard(createWalletResponses, session, callback)
}
card.settings.isHDWalletAllowed -> {
deriveKeys(createWalletResponses, session, callback)
}
else -> {
callback(
CompletionResult.Success(
CreateProductWalletTaskResponse(card = session.environment.card!!)
)
)
}
}
}
private fun linkPrimaryCard(
createWalletResponse: List<CreateWalletResponse>,
session: CardSession,

View file

@ -185,8 +185,9 @@ class CurrenciesRepository(
tokens.map {
async {
tangemNetworkService.getTokens(
it.contractAddress,
it.blockchainDao.toBlockchain().toNetworkId()
contractAddress = it.contractAddress,
networkId = it.blockchainDao.toBlockchain().toNetworkId(),
active = true,
)
}
}.map { it.await() }

View file

@ -53,6 +53,7 @@ class LoadAvailableCoinsService(
val networkIds = supportedBlockchains.toSet().map { it.toNetworkId() }
return networkService.getListOfCoins(
networkIds = networkIds,
active = true,
offset = offset,
limit = LOAD_PER_PAGE,
searchText = searchInput

View file

@ -5,11 +5,15 @@ import android.content.Context
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonClass
import com.squareup.moshi.Types
import com.tangem.common.extensions.hexToBytes
import com.tangem.common.extensions.toHexString
import com.tangem.network.common.MoshiConverter
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession
import com.tangem.tap.features.details.redux.walletconnect.WalletForSession
import com.trustwallet.walletconnect.models.WCPeerMeta
import com.trustwallet.walletconnect.models.session.WCSession
import timber.log.Timber
import java.nio.charset.Charset
class WalletConnectRepository(val context: Application) {
private val moshi = MoshiConverter.defaultMoshi()
@ -35,17 +39,29 @@ class WalletConnectRepository(val context: Application) {
fun loadSavedSessions(): List<WalletConnectSession> {
return try {
val json = context.readFileText(FILE_NAME_PREFIX_SESSIONS)
.hexToUtf8()
walletConnectAdapter.fromJson(json)!!.map { it.toSession() }
} catch (exception: Exception) {
Timber.e(exception)
emptyList()
}
}
private fun saveSessions(sessions: List<WalletConnectSession>) {
val json = walletConnectAdapter.toJson(sessions.map { SessionDao.fromSession(it) })
.utf8ToHex() // convert to hex to solve problems with saving text with emojis
Timber.e("WC sessions, saving following json: $json")
context.rewriteFile(json, FILE_NAME_PREFIX_SESSIONS)
}
private fun String.utf8ToHex(): String {
return this.toByteArray().toHexString()
}
private fun String.hexToUtf8(): String {
return this.hexToBytes().toString(Charset.defaultCharset())
}
private fun Context.readFileText(fileName: String): String =
this.openFileInput(fileName).bufferedReader().readText()

View file

@ -5,7 +5,7 @@ import com.tangem.domain.common.ScanResponse
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.tap.domain.extensions.makePrimaryWalletManager
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.scope
import com.tangem.tap.store

View file

@ -1,12 +1,11 @@
package com.tangem.tap.features.details.redux.walletconnect
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.DerivationParams
import com.tangem.blockchain.common.WalletManager
import com.tangem.common.extensions.guard
import com.tangem.domain.common.ScanResponse
import com.tangem.domain.common.TapWorkarounds.derivationStyle
import com.tangem.domain.common.TapWorkarounds.isTestCard
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.getFromClipboard
import com.tangem.tap.common.redux.AppState
@ -21,8 +20,6 @@ import com.tangem.tap.domain.walletconnect.BnbHelper
import com.tangem.tap.domain.walletconnect.WalletConnectManager
import com.tangem.tap.domain.walletconnect.WalletConnectNetworkUtils
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.wallet.R
@ -192,40 +189,51 @@ class WalletConnectMiddleware {
return
}
val walletManager = getWalletManager(scanResponse, blockchain).guard {
store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(null))
return
scope.launch {
val walletManager = getWalletManager(scanResponse, blockchain).guard {
store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(session.session))
store.dispatchOnMain(
GlobalAction.ShowDialog(
WalletConnectDialog.AddNetwork(blockchain.fullName)
)
)
return@launch
}
val wallet = walletManager.wallet
val derivedKey =
if (wallet.publicKey.blockchainKey.contentEquals(wallet.publicKey.seedKey)) {
null
} else {
walletManager.wallet.publicKey.blockchainKey
}
val walletForSession = WalletForSession(
cardId = scanResponse.card.cardId,
walletPublicKey = wallet.publicKey.seedKey,
derivedPublicKey = derivedKey,
derivationPath = wallet.publicKey.derivationPath,
blockchain = wallet.blockchain
)
withMainContext {
val updatedSession = session.copy(wallet = walletForSession)
walletConnectManager.updateSession(updatedSession)
store.dispatch(WalletConnectAction.AddScanResponse(scanResponse))
store.dispatch(
GlobalAction.ShowDialog(
WalletConnectDialog.ApproveWcSession(
updatedSession
)
)
)
}
}
val wallet = walletManager.wallet
val derivedKey =
if (wallet.publicKey.blockchainKey.contentEquals(wallet.publicKey.seedKey)) {
null
} else {
walletManager.wallet.publicKey.blockchainKey
}
val walletForSession = WalletForSession(
cardId = scanResponse.card.cardId,
walletPublicKey = wallet.publicKey.seedKey,
derivedPublicKey = derivedKey,
derivationPath = wallet.publicKey.derivationPath,
blockchain = wallet.blockchain
)
val updatedSession = session.copy(wallet = walletForSession)
walletConnectManager.updateSession(updatedSession)
store.dispatchOnMain(WalletConnectAction.AddScanResponse(scanResponse))
store.dispatchOnMain(
GlobalAction.ShowDialog(
WalletConnectDialog.ApproveWcSession(
updatedSession
)
)
)
}
private fun getWalletManager(
private suspend fun getWalletManager(
scanResponse: ScanResponse, blockchain: Blockchain
): WalletManager? {
val card = scanResponse.card
@ -236,42 +244,25 @@ class WalletConnectMiddleware {
blockchain
}
val blockchainNetwork = BlockchainNetwork(
blockchain = blockchainToMake,
card = card
)
return if (store.state.globalState.scanResponse?.card?.cardId == card.cardId) {
val derivationPath = blockchainToMake.derivationPath(card.derivationStyle)?.rawPath
store.state.walletState.getWalletManager(
Currency.Blockchain(blockchainToMake, derivationPath)
)
?: factory.makeWalletManagerForApp(
scanResponse,
blockchainToMake,
card.derivationStyle?.let { DerivationParams.Default(it) }
)
?.also { walletManager ->
store.dispatch(
WalletAction.MultiWallet.AddBlockchain(
BlockchainNetwork.fromWalletManager(walletManager), walletManager
)
)
}
store.state.walletState.getWalletManager(blockchainNetwork)
} else {
val walletManager = factory.makeWalletManagerForApp(
scanResponse,
blockchainToMake,
card.derivationStyle?.let { DerivationParams.Default(it) }
)
scope.launch {
if (currenciesRepository.loadSavedCurrencies(card.cardId, card.settings.isHDWalletAllowed)
.find { it.blockchain == blockchainToMake } != null
) {
walletManager?.let {
currenciesRepository.saveUpdatedCurrency(
card.cardId,
BlockchainNetwork.fromWalletManager(walletManager)
)
}
}
if (currenciesRepository
.loadSavedCurrencies(card.cardId, card.settings.isHDWalletAllowed)
.contains(blockchainNetwork)
) {
factory.makeWalletManagerForApp(
scanResponse = scanResponse,
blockchainNetwork = blockchainNetwork
)
} else {
null
}
return walletManager
}
}
}

View file

@ -82,6 +82,7 @@ data class WalletForSession(
sealed class WalletConnectDialog : StateDialog {
data class ClipboardOrScanQr(val clipboardUri: String) : WalletConnectDialog()
object UnsupportedCard : WalletConnectDialog()
data class AddNetwork(val network: String) : WalletConnectDialog()
object OpeningSessionRejected : WalletConnectDialog()
object SessionTimeout : WalletConnectDialog()
data class ApproveWcSession(val session: WalletConnectSession) : WalletConnectDialog()

View file

@ -67,7 +67,7 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<Det
setState(state)
}
private fun setState(state: DetailsState) = with (binding){
private fun setState(state: DetailsState) = with (binding) {
if (state.cardInfo != null) {
val cardId = if (state.isTangemTwins) {

View file

@ -1,28 +0,0 @@
package com.tangem.tap.features.details.ui.walletconnect.dialogs
import android.content.Context
import androidx.appcompat.app.AlertDialog
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.store
import com.tangem.wallet.R
class SimpleAlertDialog {
companion object {
fun create(
titleRes: Int,
messageRes: Int,
buttonRes: Int = R.string.common_ok,
context: Context,
): AlertDialog {
return AlertDialog.Builder(context).apply {
setTitle(context.getString(titleRes))
setMessage(context.getText(messageRes))
setPositiveButton(context.getText(buttonRes)) { _, _ -> }
setOnDismissListener {
store.dispatch(GlobalAction.HideDialog)
}
}.create()
}
}
}

View file

@ -10,8 +10,8 @@ import com.tangem.tap.common.extensions.dispatchDialogHide
import com.tangem.tap.common.extensions.dispatchShare
import com.tangem.tap.common.extensions.dispatchToastNotification
import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.redux.AddressData
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.store
import com.tangem.wallet.R
import com.tangem.wallet.databinding.DialogOnboardingAddressInfoBinding

View file

@ -12,8 +12,8 @@ import com.tangem.tap.common.extensions.isPositive
import com.tangem.tap.common.extensions.safeUpdate
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.extensions.getOrLoadCardArtworkUrl
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.models.hasPendingTransactions
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.persistence.UsedCardsPrefStorage
import timber.log.Timber

View file

@ -10,7 +10,7 @@ import androidx.annotation.LayoutRes
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.view.isVisible
import androidx.transition.TransitionManager
import com.squareup.picasso.Picasso
import coil.load
import com.tangem.blockchain.common.Blockchain
import com.tangem.tangem_sdk_new.extensions.fadeIn
import com.tangem.tangem_sdk_new.extensions.fadeOut
@ -67,11 +67,11 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
override fun newState(state: OnboardingNoteState) {
if (activity == null || view == null) return
Picasso.get()
.load(state.cardArtworkUrl)
.error(R.drawable.card_placeholder_black)
.placeholder(R.drawable.card_placeholder_black)
?.into(binding.onboardingTopContainer.imvFrontCard)
binding.onboardingTopContainer.imvFrontCard.load(state.cardArtworkUrl) {
placeholder(R.drawable.card_placeholder_black)
error(R.drawable.card_placeholder_black)
fallback(R.drawable.card_placeholder_black)
}
pbBinding.pbState.max = state.steps.size - 1
pbBinding.pbState.progress = state.progress

View file

@ -15,7 +15,7 @@ import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.extensions.hasWallets
import com.tangem.tap.domain.extensions.makePrimaryWalletManager
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.scope
import com.tangem.tap.store

View file

@ -7,7 +7,7 @@ import androidx.annotation.LayoutRes
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.view.isVisible
import androidx.transition.TransitionManager
import com.squareup.picasso.Picasso
import coil.load
import com.tangem.tap.common.extensions.getDrawableCompat
import com.tangem.tap.common.redux.navigation.ShareElement
import com.tangem.tap.common.transitions.InternalNoteLayoutTransition
@ -54,11 +54,11 @@ class OnboardingOtherCardsFragment : BaseOnboardingFragment<OnboardingOtherCards
if (activity == null || view == null) return
if (state.currentStep == OnboardingOtherCardsStep.None) return
Picasso.get()
.load(state.cardArtworkUrl)
.error(R.drawable.card_placeholder_black)
.placeholder(R.drawable.card_placeholder_black)
?.into(binding.onboardingTopContainer.imvFrontCard)
binding.onboardingTopContainer.imvFrontCard.load(state.cardArtworkUrl) {
placeholder(R.drawable.card_placeholder_black)
error(R.drawable.card_placeholder_black)
fallback(R.drawable.card_placeholder_black)
}
pbBinding.pbState.max = state.steps.size - 1
pbBinding.pbState.progress = state.progress

View file

@ -1,5 +1,6 @@
package com.tangem.tap.features.onboarding.products.otherCards.redux
import com.tangem.blockchain.common.Blockchain
import com.tangem.common.CompletionResult
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE
@ -10,6 +11,8 @@ import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.extensions.hasWallets
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.tap.tangemSdkManager
@ -73,6 +76,21 @@ private fun handleOtherCardsAction(action: Action, dispatch: DispatchFunction) {
onboardingManager.scanResponse = updatedResponse
onboardingManager.activationStarted(updatedResponse.card.cardId)
val primaryBlockchain = updatedResponse.getBlockchain()
val blockchainNetworks = if (primaryBlockchain != Blockchain.Unknown) {
val primaryToken = updatedResponse.getPrimaryToken()
val blockchainNetwork = BlockchainNetwork(primaryBlockchain, updatedResponse.card).updateTokens(
listOfNotNull(primaryToken))
listOf(blockchainNetwork)
} else {
listOf(
BlockchainNetwork(Blockchain.Bitcoin, updatedResponse.card),
BlockchainNetwork(Blockchain.Ethereum, updatedResponse.card)
)
}
store.dispatch(WalletAction.MultiWallet.SaveCurrencies(blockchainNetworks))
delay(DELAY_SDK_DIALOG_CLOSE)
store.dispatch(OnboardingOtherCardsAction.SetStepOfScreen(OnboardingOtherCardsStep.Done))
}

View file

@ -16,7 +16,7 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.extensions.makePrimaryWalletManager
import com.tangem.tap.domain.twins.TwinCardsManager
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.preferencesStorage
import com.tangem.tap.scope

View file

@ -9,13 +9,18 @@ import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.view.isVisible
import androidx.transition.TransitionInflater
import androidx.transition.TransitionManager
import com.squareup.picasso.Picasso
import coil.load
import com.tangem.Message
import com.tangem.blockchain.common.Blockchain
import com.tangem.common.extensions.VoidCallback
import com.tangem.domain.common.TwinCardNumber
import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget
import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.extensions.beginDelayedTransition
import com.tangem.tap.common.extensions.getDrawableCompat
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.readAssetAsString
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.extensions.stripZeroPlainString
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.common.redux.navigation.ShareElement
@ -75,24 +80,28 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
resources.getValue(R.dimen.device_scale_factor_for_twins_welcome, typedValue, true)
val deviceScaleFactorForWelcomeState = typedValue.float
twinsWidget = TwinsCardWidget(LeapfrogWidget(binding.onboardingTopContainer.cardsContainer), deviceScaleFactorForWelcomeState) {
twinsWidget = TwinsCardWidget(
LeapfrogWidget(binding.onboardingTopContainer.cardsContainer),
deviceScaleFactorForWelcomeState
) {
285f * deviceScaleFactorForWelcomeState
}
btnRefreshBalanceWidget = RefreshBalanceWidget(binding.onboardingTopContainer.onboardingMainContainer)
btnRefreshBalanceWidget =
RefreshBalanceWidget(binding.onboardingTopContainer.onboardingMainContainer)
binding.toolbar.title = getText(R.string.twins_recreate_toolbar)
Picasso.get()
.load(Artwork.TWIN_CARD_1)
.error(R.drawable.card_placeholder_black)
.placeholder(R.drawable.card_placeholder_black)
?.into(binding.onboardingTopContainer.imvTwinFrontCard)
binding.onboardingTopContainer.imvTwinFrontCard.load(Artwork.TWIN_CARD_1) {
placeholder(R.drawable.card_placeholder_black)
error(R.drawable.card_placeholder_black)
fallback(R.drawable.card_placeholder_black)
}
Picasso.get()
.load(Artwork.TWIN_CARD_2)
.error(R.drawable.card_placeholder_white)
.placeholder(R.drawable.card_placeholder_white)
?.into(binding.onboardingTopContainer.imvTwinBackCard)
binding.onboardingTopContainer.imvTwinBackCard.load(Artwork.TWIN_CARD_2) {
placeholder(R.drawable.card_placeholder_white)
error(R.drawable.card_placeholder_white)
fallback(R.drawable.card_placeholder_white)
}
}
private fun reconfigureLayoutForTwins(containerBinding: LayoutOnboardingContainerTopBinding) =

View file

@ -1,5 +1,6 @@
package com.tangem.tap.features.onboarding.products.wallet.redux
import com.tangem.blockchain.common.Blockchain
import com.tangem.common.CompletionResult
import com.tangem.common.card.Card
import com.tangem.domain.common.ScanResponse
@ -12,9 +13,11 @@ import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.extensions.hasWallets
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.home.redux.HomeAction
import com.tangem.tap.features.wallet.redux.Artwork
import com.tangem.tap.features.wallet.redux.WalletAction
import kotlinx.coroutines.launch
import org.rekotlin.Action
import org.rekotlin.Middleware
@ -84,6 +87,11 @@ private fun handleWalletAction(action: Action) {
primaryCard = result.data.primaryCard
)
onboardingManager.scanResponse = updatedResponse
val blockchainNetworks = listOf(
BlockchainNetwork(Blockchain.Bitcoin, result.data.card),
BlockchainNetwork(Blockchain.Ethereum, result.data.card)
)
store.dispatchOnMain(WalletAction.MultiWallet.SaveCurrencies(blockchainNetworks))
onboardingManager.activationStarted(updatedResponse.card.cardId)
store.dispatch(OnboardingWalletAction.ProceedBackup)
}
@ -260,6 +268,11 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
backupService.proceedBackup { result ->
when (result) {
is CompletionResult.Success -> {
val blockchainNetworks = listOf(
BlockchainNetwork(Blockchain.Bitcoin, result.data),
BlockchainNetwork(Blockchain.Ethereum, result.data)
)
store.dispatchOnMain(WalletAction.MultiWallet.SaveCurrencies(blockchainNetworks))
if (backupService.currentState == BackupService.State.Finished) {
store.dispatchOnMain(BackupAction.FinishBackup)
} else {

View file

@ -11,9 +11,9 @@ import androidx.fragment.app.Fragment
import androidx.transition.TransitionInflater
import androidx.transition.TransitionManager
import by.kirich1409.viewbindingdelegate.viewBinding
import coil.load
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.tabs.TabLayoutMediator
import com.squareup.picasso.Picasso
import com.tangem.common.CardIdFormatter
import com.tangem.common.core.CardIdDisplayFormat
import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget
@ -22,7 +22,12 @@ import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show
import com.tangem.tap.features.FragmentOnBackPressedHandler
import com.tangem.tap.features.addBackPressHandler
import com.tangem.tap.features.onboarding.products.wallet.redux.*
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupState
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupStep
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletAction
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletStep
import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.AccessCodeDialog
import com.tangem.tap.store
import com.tangem.wallet.R
@ -124,11 +129,11 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
}
private fun loadImageIntoImageView(url: String?, view: ImageView) {
Picasso.get()
.load(url)
.error(R.drawable.card_placeholder_black)
.placeholder(R.drawable.card_placeholder_black)
?.into(view)
view.load(url) {
placeholder(R.drawable.card_placeholder_black)
error(R.drawable.card_placeholder_black)
fallback(R.drawable.card_placeholder_black)
}
}
private fun setupCreateWalletState() = with(binding) {

View file

@ -106,7 +106,7 @@ class FeeReducer : SendInternalReducer {
private fun getFeePrecision(sendState: SendState): FeePrecision {
val blockchain = sendState.walletManager?.wallet?.blockchain
return if (blockchain?.fullNameWithoutTestnet == Blockchain.Tron.fullName &&
return if ((blockchain?.fullNameWithoutTestnet == Blockchain.Arbitrum.fullName || blockchain?.fullNameWithoutTestnet == Blockchain.Tron.fullName) &&
sendState.amountState.typeOfAmount is AmountType.Token) {
FeePrecision.CAN_BE_LOWER
} else {

View file

@ -7,6 +7,7 @@ import com.tangem.tap.common.extensions.stripZeroPlainString
import com.tangem.tap.features.send.redux.ReceiptAction.RefreshReceipt
import com.tangem.tap.features.send.redux.SendScreenAction
import com.tangem.tap.features.send.redux.states.*
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN
import com.tangem.tap.store
import java.math.BigDecimal
@ -15,10 +16,6 @@ import java.math.BigDecimal
*/
class ReceiptReducer : SendInternalReducer {
companion object {
const val EMPTY = "-"
}
private lateinit var sendState: SendState
private lateinit var amountState: AmountState
private lateinit var feeState: FeeState
@ -137,9 +134,9 @@ class ReceiptReducer : SendInternalReducer {
)
} else {
ReceiptTokenFiat(
amountFiat = EMPTY,
feeFiat = EMPTY,
totalFiat = EMPTY,
amountFiat = UNKNOWN_AMOUNT_SIGN,
feeFiat = UNKNOWN_AMOUNT_SIGN,
totalFiat = UNKNOWN_AMOUNT_SIGN,
willSentToken = tokensToSend.stripZeroPlainString(),
willSentFeeCoin = feeCoin.stripZeroPlainString(),
symbols = symbols
@ -169,7 +166,7 @@ class ReceiptReducer : SendInternalReducer {
ReceiptTokenCrypto(
amountToken = tokensToSend.stripZeroPlainString(),
feeCoin = feeCoin.stripZeroPlainString().addPrecisionSign(),
totalFiat = EMPTY,
totalFiat = UNKNOWN_AMOUNT_SIGN,
symbols = symbols
)
}
@ -206,7 +203,7 @@ class ReceiptReducer : SendInternalReducer {
return when {
!isToken && sendState.coinIsConvertible() -> sendState.coinConverter!!.toFiatWithPrecision(value).stripZeroPlainString()
isToken && sendState.tokenIsConvertible() -> sendState.tokenConverter!!.toFiatWithPrecision(value).stripZeroPlainString()
else -> EMPTY
else -> UNKNOWN_AMOUNT_SIGN
}
}

View file

@ -17,12 +17,13 @@ import com.tangem.tap.features.BaseStoreFragment
import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.Error
import com.tangem.tap.features.send.redux.FeeAction
import com.tangem.tap.features.send.redux.SendAction
import com.tangem.tap.features.send.redux.reducers.ReceiptReducer
import com.tangem.tap.features.send.redux.states.*
import com.tangem.tap.features.send.ui.FeeUiHelper
import com.tangem.tap.features.send.ui.SendFragment
import com.tangem.tap.features.send.ui.dialogs.SendTransactionFailsDialog
import com.tangem.tap.features.send.ui.dialogs.TezosWarningDialog
import com.tangem.tap.features.wallet.redux.WalletState.Companion.ROUGH_SIGN
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN
import com.tangem.tap.features.wallet.ui.adapters.WarningMessagesAdapter
import com.tangem.tap.store
import com.tangem.wallet.R
@ -257,12 +258,12 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
val mainLayout = clReceiptContainer as ViewGroup
val totalLayout = llTotalContainer.llTotal as ViewGroup
val totalTokenLayout = llTotalContainer.flTotalTokenCrypto as ViewGroup
fun getString(id: Int, vararg formatStrings: String): String =
mainLayout.context.getString(id, *formatStrings)
val rough = getString(R.string.sign_rough)
fun roughOrEmpty(value: String): String =
if (value == ReceiptReducer.EMPTY) value else "$rough $value"
fun getString(id: Int, vararg formatStrings: String): String = mainLayout.getString(id, *formatStrings)
fun roughOrEmpty(value: String): String {
return if (value == UNKNOWN_AMOUNT_SIGN) value else "$ROUGH_SIGN $value"
}
when (state.visibleTypeOfReceipt) {
ReceiptLayoutType.FIAT -> {

View file

@ -1,15 +1,20 @@
package com.tangem.tap.features.shop.redux
import android.content.Intent
import com.tangem.tap.common.redux.NotificationAction
import com.tangem.tap.common.shop.GooglePayService
import com.tangem.tap.common.shop.data.ProductType
import com.tangem.tap.common.shop.data.TangemProduct
import com.tangem.wallet.R
import org.rekotlin.Action
sealed class ShopAction : Action {
object LoadProducts : ShopAction() {
data class Success(val products: List<TangemProduct>) : ShopAction()
object Failure : ShopAction(), NotificationAction {
override val messageResource = R.string.common_server_unavailable
}
}
data class ApplyPromoCode(val promoCode: String) : ShopAction() {

View file

@ -11,6 +11,7 @@ import com.tangem.tap.store
import kotlinx.coroutines.launch
import org.rekotlin.Action
import org.rekotlin.Middleware
import timber.log.Timber
class ShopMiddleware {
@ -88,10 +89,13 @@ private fun handle(action: Action) {
}
ShopAction.LoadProducts -> {
scope.launch {
val result = shopService.getProducts()
result.onSuccess {
store.dispatchOnMain(ShopAction.LoadProducts.Success(it))
}
shopService.getProducts().fold(
onSuccess = { store.dispatchOnMain(ShopAction.LoadProducts.Success(it)) },
onFailure = {
Timber.e(it)
store.dispatchOnMain(ShopAction.LoadProducts.Failure)
}
)
}
}
is ShopAction.CheckIfGooglePayAvailable -> {

View file

@ -65,5 +65,6 @@ private fun internalReduce(action: Action, state: ShopState): ShopState {
}
ShopAction.FinishSuccessfulOrder -> state
ShopAction.ResetState -> ShopState()
ShopAction.LoadProducts.Failure -> state
}
}

View file

@ -30,7 +30,7 @@ import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.extensions.makeWalletManagerForApp
import com.tangem.tap.domain.tokens.LoadAvailableCoinsService
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.redux.WalletAction
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@ -309,7 +309,10 @@ class TokensMiddleware {
if (currencies.isNotEmpty()) {
currencies.forEach { currency ->
store.state.walletState.getWalletData(currency)?.let {
store.dispatch(WalletAction.MultiWallet.RemoveWallet(it))
store.dispatch(WalletAction.MultiWallet.RemoveWallet(
walletData = it,
fromWalletDetails = false
))
}
}
}

View file

@ -34,18 +34,18 @@ data class TokensState(
typealias ContractAddress = String
fun List<WalletData>.toTokensContractAddresses(): List<ContractAddress> {
return mapNotNull { (it.currency as? com.tangem.tap.features.wallet.redux.Currency.Token)?.token?.contractAddress }.distinct()
return mapNotNull { (it.currency as? com.tangem.tap.features.wallet.models.Currency.Token)?.token?.contractAddress }.distinct()
}
fun List<WalletData>.toNonCustomTokens(derivationStyle: DerivationStyle?): List<Token> {
return filter { !it.currency.isCustomCurrency(derivationStyle) }
.mapNotNull { (it.currency as? com.tangem.tap.features.wallet.redux.Currency.Token)?.token }
.mapNotNull { (it.currency as? com.tangem.tap.features.wallet.models.Currency.Token)?.token }
.distinct()
}
fun List<WalletData>.toNonCustomTokensWithBlockchains(derivationStyle: DerivationStyle?): List<TokenWithBlockchain> {
return mapNotNull {
if (it.currency !is com.tangem.tap.features.wallet.redux.Currency.Token) return@mapNotNull null
if (it.currency !is com.tangem.tap.features.wallet.models.Currency.Token) return@mapNotNull null
if (it.currency.isCustomCurrency(derivationStyle)) return@mapNotNull null
TokenWithBlockchain(it.currency.token, it.currency.blockchain)
}.distinct()
@ -56,7 +56,7 @@ fun List<WalletData>.toNonCustomBlockchains(derivationStyle: DerivationStyle?):
if (it.currency.isCustomCurrency(derivationStyle)) {
null
} else {
(it.currency as? com.tangem.tap.features.wallet.redux.Currency.Blockchain)?.blockchain
(it.currency as? com.tangem.tap.features.wallet.models.Currency.Blockchain)?.blockchain
}
}.distinct()
}

View file

@ -0,0 +1,97 @@
package com.tangem.tap.features.wallet.models
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.DerivationStyle
import com.tangem.blockchain.common.Token
import com.tangem.domain.common.extensions.toCoinId
import com.tangem.domain.features.addCustomToken.CustomCurrency
import com.tangem.tap.common.redux.global.CryptoCurrencyName
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.tokens.redux.TokenWithBlockchain
sealed interface Currency {
val coinId: String?
get() = when (this) {
is Blockchain -> blockchain.toCoinId()
is Token -> token.id
}
val blockchain: com.tangem.blockchain.common.Blockchain
val currencySymbol: CryptoCurrencyName
val derivationPath: String?
val currencyName: String
get() = when (this) {
is Blockchain -> blockchain.fullName
is Token -> token.name
}
data class Token(
val token: com.tangem.blockchain.common.Token,
override val blockchain: com.tangem.blockchain.common.Blockchain,
override val derivationPath: String?
) : Currency {
override val currencySymbol = token.symbol
}
data class Blockchain(
override val blockchain: com.tangem.blockchain.common.Blockchain,
override val derivationPath: String?
) : Currency {
override val currencySymbol: CryptoCurrencyName = blockchain.currency
}
fun isCustomCurrency(derivationStyle: DerivationStyle?): Boolean {
if (this is Token && this.token.id == null) return true
if (derivationPath == null || derivationStyle == null) return false
return derivationPath != blockchain.derivationPath(derivationStyle)?.rawPath
}
fun isBlockchain(): Boolean = this is Blockchain
fun isToken(): Boolean = this is Token
companion object {
fun fromBlockchainNetwork(
blockchainNetwork: BlockchainNetwork,
token: com.tangem.blockchain.common.Token? = null
): Currency {
return if (token != null) {
Token(
token = token,
blockchain = blockchainNetwork.blockchain,
derivationPath = blockchainNetwork.derivationPath
)
} else {
Blockchain(
blockchain = blockchainNetwork.blockchain,
derivationPath = blockchainNetwork.derivationPath
)
}
}
fun fromCustomCurrency(customCurrency: CustomCurrency): Currency {
return when (customCurrency) {
is CustomCurrency.CustomBlockchain -> Blockchain(
blockchain = customCurrency.network,
derivationPath = customCurrency.derivationPath?.rawPath
)
is CustomCurrency.CustomToken -> Token(
token = customCurrency.token,
blockchain = customCurrency.network,
derivationPath = customCurrency.derivationPath?.rawPath,
)
}
}
fun fromTokenWithBlockchain(tokenWithBlockchain: TokenWithBlockchain): Token {
return Token(
token = tokenWithBlockchain.token,
blockchain = tokenWithBlockchain.blockchain,
derivationPath = null
)
}
}
}

View file

@ -14,6 +14,7 @@ import com.tangem.tap.common.redux.NotificationAction
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.wallet.R
import java.math.BigDecimal
import org.rekotlin.Action
@ -74,7 +75,8 @@ sealed class WalletAction : Action {
) : MultiWallet()
data class SelectWallet(val walletData: WalletData?) : MultiWallet()
data class RemoveWallet(val walletData: WalletData) : MultiWallet()
data class RemoveWallet(val walletData: WalletData, val fromWalletDetails: Boolean = true) : MultiWallet()
data class TryToRemoveWallet(val walletData: WalletData) : MultiWallet()
data class SetPrimaryBlockchain(val blockchain: Blockchain) : MultiWallet()
data class SetPrimaryToken(val token: Token) : MultiWallet()
}

View file

@ -1,17 +1,10 @@
package com.tangem.tap.features.wallet.redux
import android.graphics.Bitmap
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.DerivationStyle
import com.tangem.blockchain.common.Token
import com.tangem.blockchain.common.Wallet
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.address.AddressType
import com.tangem.common.extensions.isZero
import com.tangem.domain.common.extensions.canHandleToken
import com.tangem.domain.common.extensions.toCoinId
import com.tangem.domain.features.addCustomToken.CustomCurrency
import com.tangem.tap.common.entities.Button
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.extensions.toQrCode
@ -23,22 +16,15 @@ import com.tangem.tap.domain.extensions.buyIsAllowed
import com.tangem.tap.domain.extensions.sellIsAllowed
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
import com.tangem.tap.features.tokens.redux.TokenWithBlockchain
import com.tangem.tap.features.wallet.models.PendingTransaction
import com.tangem.tap.features.wallet.models.TotalBalance
import com.tangem.tap.features.wallet.models.WalletRent
import com.tangem.tap.features.wallet.models.WalletWarning
import com.tangem.tap.features.wallet.models.hasPendingTransactions
import com.tangem.tap.features.wallet.models.hasSendableAmounts
import com.tangem.tap.features.wallet.models.isSendableAmount
import com.tangem.tap.features.wallet.models.*
import com.tangem.tap.features.wallet.redux.reducers.calculateTotalFiatAmount
import com.tangem.tap.features.wallet.redux.reducers.findProgressState
import com.tangem.tap.features.wallet.ui.BalanceStatus
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
import com.tangem.tap.store
import java.math.BigDecimal
import org.rekotlin.StateType
import java.math.BigDecimal
import kotlin.properties.ReadOnlyProperty
data class WalletState(
@ -315,6 +301,11 @@ data class WalletState(
)
} else this
}
companion object {
const val UNKNOWN_AMOUNT_SIGN = ""
const val ROUGH_SIGN = ""
}
}
sealed interface WalletDialog : StateDialog {
@ -439,87 +430,6 @@ data class WalletData(
private fun tokenAmountIsEmpty(): Boolean = currencyData.amount?.isZero() == true
}
sealed interface Currency {
val coinId: String?
get() = when (this) {
is Blockchain -> blockchain.toCoinId()
is Token -> token.id
}
val blockchain: com.tangem.blockchain.common.Blockchain
val currencySymbol: CryptoCurrencyName
val derivationPath: String?
data class Token(
val token: com.tangem.blockchain.common.Token,
override val blockchain: com.tangem.blockchain.common.Blockchain,
override val derivationPath: String?
) : Currency {
override val currencySymbol = token.symbol
}
data class Blockchain(
override val blockchain: com.tangem.blockchain.common.Blockchain,
override val derivationPath: String?
) : Currency {
override val currencySymbol: CryptoCurrencyName = blockchain.currency
}
fun isCustomCurrency(derivationStyle: DerivationStyle?): Boolean {
if (this is Token && this.token.id == null) return true
if (derivationPath == null || derivationStyle == null) return false
return derivationPath != blockchain.derivationPath(derivationStyle)?.rawPath
}
fun isBlockchain(): Boolean = this is Blockchain
fun isToken(): Boolean = this is Token
companion object {
fun fromBlockchainNetwork(
blockchainNetwork: BlockchainNetwork,
token: com.tangem.blockchain.common.Token? = null
): Currency {
return if (token != null) {
Token(
token = token,
blockchain = blockchainNetwork.blockchain,
derivationPath = blockchainNetwork.derivationPath
)
} else {
Blockchain(
blockchain = blockchainNetwork.blockchain,
derivationPath = blockchainNetwork.derivationPath
)
}
}
fun fromCustomCurrency(customCurrency: CustomCurrency): Currency {
return when (customCurrency) {
is CustomCurrency.CustomBlockchain -> Blockchain(
blockchain = customCurrency.network,
derivationPath = customCurrency.derivationPath?.rawPath
)
is CustomCurrency.CustomToken -> Token(
token = customCurrency.token,
blockchain = customCurrency.network,
derivationPath = customCurrency.derivationPath?.rawPath,
)
}
}
fun fromTokenWithBlockchain(tokenWithBlockchain: TokenWithBlockchain): Token {
return Token(
token = tokenWithBlockchain.token,
blockchain = tokenWithBlockchain.blockchain,
derivationPath = null
)
}
}
}
data class WalletStore(
val walletManager: WalletManager?,
val blockchainNetwork: BlockchainNetwork,

View file

@ -9,7 +9,7 @@ import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.TapWalletManager
import com.tangem.tap.features.details.redux.DetailsAction
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletDialog
import com.tangem.tap.features.wallet.redux.models.WalletDialog
import com.tangem.tap.persistence.FiatCurrenciesPrefStorage
import com.tangem.tap.scope
import com.tangem.tap.store

View file

@ -3,6 +3,8 @@ package com.tangem.tap.features.wallet.redux.middlewares
import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.Token
import com.tangem.blockchain.common.WalletManager
import com.tangem.common.extensions.guard
import com.tangem.tap.common.extensions.dispatchDialogShow
import com.tangem.tap.common.extensions.safeUpdate
import com.tangem.tap.common.redux.global.GlobalState
import com.tangem.tap.common.redux.navigation.AppScreen
@ -12,9 +14,10 @@ import com.tangem.tap.domain.extensions.makeWalletManagerForApp
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.demo.isDemoCard
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.redux.models.WalletDialog
import com.tangem.tap.scope
import com.tangem.tap.store
import kotlinx.coroutines.Dispatchers
@ -55,23 +58,55 @@ class MultiWalletMiddleware {
blockchainNetwork = action.blockchain
)
}
store.dispatch(WalletAction.LoadFiatRate(
coinsList = listOf(
Currency.Blockchain(
action.blockchain.blockchain,
action.blockchain.derivationPath
store.dispatch(
WalletAction.LoadFiatRate(
coinsList = listOf(
Currency.Blockchain(
action.blockchain.blockchain,
action.blockchain.derivationPath
)
)
)
))
store.dispatch(WalletAction.LoadWallet(
action.blockchain, action.walletManager
))
)
store.dispatch(
WalletAction.LoadWallet(
action.blockchain, action.walletManager
)
)
}
is WalletAction.MultiWallet.SaveCurrencies -> {
globalState.scanResponse?.card?.cardId?.let {
currenciesRepository.saveCurrencies(it, action.blockchainNetworks)
}
}
is WalletAction.MultiWallet.TryToRemoveWallet -> {
val walletState = store.state.walletState
val currency = action.walletData.currency
val walletCanBeRemoved = store.state.walletState.canBeRemoved(
store.state.walletState.getSelectedWalletData()
)
if (walletCanBeRemoved) {
store.dispatch(WalletAction.MultiWallet.RemoveWallet(action.walletData))
return
}
val walletManager = walletState.getWalletManager(currency).guard {
store.dispatch(WalletAction.MultiWallet.RemoveWallet(action.walletData))
return
}
val dialog = when {
currency is Currency.Blockchain &&
walletManager.cardTokens.isNotEmpty() ->
WalletDialog.TokensAreLinkedDialog(
currency.currencyName, currency.currencySymbol
)
else ->
WalletDialog.RemoveWalletDialog(currency.currencyName, action.walletData)
}
store.dispatchDialogShow(dialog)
}
is WalletAction.MultiWallet.RemoveWallet -> {
val cardId = globalState.scanResponse?.card?.cardId
when (val currency = action.walletData.currency) {
@ -94,12 +129,18 @@ class MultiWalletMiddleware {
currenciesRepository.removeToken(
cardId = it,
token = currency.token,
blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager)
blockchainNetwork = BlockchainNetwork.fromWalletManager(
walletManager
)
)
}
}
}
}
if (action.fromWalletDetails) {
store.dispatch(WalletAction.MultiWallet.SelectWallet(null))
store.dispatch(NavigationAction.PopBackTo())
}
}
// is WalletAction.MultiWallet.FindBlockchainsInUse -> {
// val scanResponse = globalState.scanResponse ?: return

View file

@ -10,7 +10,7 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.send.redux.PrepareSendScreen
import com.tangem.tap.features.send.redux.SendAction
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
import com.tangem.tap.network.exchangeServices.buyErc20Tokens

View file

@ -4,7 +4,7 @@ import com.tangem.tap.common.extensions.dispatchDialogHide
import com.tangem.tap.common.extensions.dispatchDialogShow
import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletDialog
import com.tangem.tap.features.wallet.redux.models.WalletDialog
import com.tangem.tap.store
class WalletDialogsMiddleware {

View file

@ -31,9 +31,10 @@ import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.home.redux.HomeAction
import com.tangem.tap.features.send.redux.PrepareSendScreen
import com.tangem.tap.features.wallet.models.PendingTransactionType
import com.tangem.tap.features.wallet.models.filterByCoin
import com.tangem.tap.features.wallet.models.getPendingTransactions
import com.tangem.tap.features.wallet.models.getSendableAmounts
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletData
import com.tangem.tap.features.wallet.redux.WalletState
@ -332,7 +333,10 @@ class WalletMiddleware {
}
val balance = walletManager.wallet.fundsAvailable(AmountType.Coin)
val outgoingTxs = walletManager.wallet.getPendingTransactions(PendingTransactionType.Outgoing)
val outgoingTxs = walletManager.wallet.getPendingTransactions(
PendingTransactionType.Outgoing
).filterByCoin()
val rentExempt = result.data
val show = if (outgoingTxs.isEmpty()) {
isNeedToShowWarning(balance, rentExempt)

View file

@ -0,0 +1,39 @@
package com.tangem.tap.features.wallet.redux.models
import com.tangem.blockchain.common.Amount
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletData
import com.tangem.tap.store
import com.tangem.wallet.R
sealed interface WalletDialog : StateDialog {
data class SelectAmountToSendDialog(val amounts: List<Amount>?) : WalletDialog
object SignedHashesMultiWalletDialog : WalletDialog
object ChooseTradeActionDialog : WalletDialog
data class CurrencySelectionDialog(
val currenciesList: List<FiatCurrency>,
val currentAppCurrency: FiatCurrency,
) : WalletDialog
data class RemoveWalletDialog(
val currencyTitle: String,
private val walletData: WalletData
): WalletDialog {
val messageRes: Int = R.string.token_details_hide_alert_message
val titleRes: Int = R.string.token_details_hide_alert_title
val primaryButtonRes: Int = R.string.token_details_hide_alert_hide
val action = { store.dispatch(WalletAction.MultiWallet.RemoveWallet(walletData)) }
}
data class TokensAreLinkedDialog(
val currencyTitle: String,
val currencySymbol: String
): WalletDialog {
val messageRes: Int = R.string.token_details_unable_hide_alert_message
val titleRes: Int = R.string.token_details_unable_hide_alert_title
}
}

View file

@ -7,10 +7,13 @@ import com.tangem.tap.common.extensions.toFiatString
import com.tangem.tap.common.extensions.toFormattedCurrencyString
import com.tangem.tap.domain.getFirstToken
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.models.WalletRent
import com.tangem.tap.features.wallet.models.filterByToken
import com.tangem.tap.features.wallet.models.getPendingTransactions
import com.tangem.tap.features.wallet.models.removeUnknownTransactions
import com.tangem.tap.features.wallet.redux.*
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN
import com.tangem.tap.features.wallet.ui.BalanceStatus
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
import com.tangem.tap.features.wallet.ui.TokenData
@ -102,10 +105,10 @@ class MultiWalletReducer {
}
is WalletAction.MultiWallet.TokenLoaded -> {
val currency = Currency.fromBlockchainNetwork(action.blockchain, action.token)
val wallet = state.getWalletManager(currency)?.wallet.guard {
throw NullPointerException("MultiWallet.TokenLoaded: WalletManager must be no NULL")
val walletManager = state.getWalletManager(currency).guard {
throw NullPointerException("MultiWallet.TokenLoaded: WalletManager must be not NULL")
}
val wallet = walletManager.wallet
val pendingTransactions = wallet.getPendingTransactions()
val tokenPendingTransactions = pendingTransactions.filterByToken(action.token)
val tokenBalanceStatus = when {
@ -125,9 +128,8 @@ class MultiWalletReducer {
action.amount.decimals, action.amount.currencySymbol
),
fiatAmountFormatted = tokenWalletData.fiatRate?.let {
action.amount.value
?.toFiatString(it, store.state.globalState.appCurrency.symbol)
},
action.amount.value?.toFiatString(it, store.state.globalState.appCurrency.symbol)
} ?: UNKNOWN_AMOUNT_SIGN,
blockchainAmount = wallet.amounts[AmountType.Coin]?.value ?: BigDecimal.ZERO
),
pendingTransactions = pendingTransactions.removeUnknownTransactions(),
@ -136,7 +138,8 @@ class MultiWalletReducer {
token = action.token,
blockchain = action.blockchain.blockchain,
derivationPath = action.blockchain.derivationPath
)
),
walletRent = findWalletRent(state.getWalletStore(walletManager.wallet))
)
state.updateWalletData(newTokenWalletData)
}
@ -157,8 +160,15 @@ class MultiWalletReducer {
// is WalletAction.MultiWallet.FindTokensInUse -> state
// is WalletAction.MultiWallet.FindBlockchainsInUse -> state
is WalletAction.MultiWallet.SaveCurrencies -> state
is WalletAction.MultiWallet.TryToRemoveWallet -> state
}
}
private fun findWalletRent(walletStore: WalletStore?): WalletRent? {
return walletStore?.walletsData?.firstOrNull {
it.walletRent != null
}?.walletRent
}
}
private fun addTokens(

View file

@ -9,15 +9,16 @@ import com.tangem.tap.common.extensions.toFormattedCurrencyString
import com.tangem.tap.common.extensions.toFormattedFiatValue
import com.tangem.tap.domain.getFirstToken
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.models.filterByToken
import com.tangem.tap.features.wallet.models.getPendingTransactions
import com.tangem.tap.features.wallet.models.removeUnknownTransactions
import com.tangem.tap.features.wallet.redux.*
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN
import com.tangem.tap.features.wallet.ui.BalanceStatus
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
import com.tangem.tap.features.wallet.ui.TokenData
import com.tangem.tap.store
import java.math.RoundingMode
class OnWalletLoadedReducer {
@ -54,6 +55,8 @@ class OnWalletLoadedReducer {
}
val fiatAmount = walletData.fiatRate?.let { coinAmountValue?.toFiatValue(it) }
val fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(fiatCurrency.symbol) ?: UNKNOWN_AMOUNT_SIGN
val newWalletData = walletData.copy(
currencyData = walletData.currencyData.copy(
status = balanceStatus,
@ -63,7 +66,7 @@ class OnWalletLoadedReducer {
amount = coinAmountValue,
amountFormatted = formattedAmount,
fiatAmount = fiatAmount,
fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(fiatCurrency.symbol)
fiatAmountFormatted = fiatAmountFormatted,
),
pendingTransactions = pendingTransactions.removeUnknownTransactions(),
mainButton = WalletMainButton.SendButton(isCoinSendButtonEnabled),
@ -81,8 +84,9 @@ class OnWalletLoadedReducer {
else -> BalanceStatus.VerifiedOnline
}
val tokenAmountValue = wallet.getTokenAmount(token)?.value
val tokenFiatAmount =
tokenWalletData?.fiatRate?.let { rate -> tokenAmountValue?.toFiatValue(rate) }
val tokenFiatAmount = tokenWalletData?.fiatRate?.let { tokenAmountValue?.toFiatValue(it) }
val tokenFiatAmountFormatted = tokenFiatAmount?.toFormattedFiatValue(fiatCurrency.symbol)
?: UNKNOWN_AMOUNT_SIGN
val isTokenSendButtonEnabled = newWalletData.shouldEnableTokenSendButton()
&& pendingTransactions.isEmpty()
@ -96,7 +100,7 @@ class OnWalletLoadedReducer {
token.symbol
),
fiatAmount = tokenFiatAmount,
fiatAmountFormatted = tokenFiatAmount?.toFormattedFiatValue(fiatCurrency.symbol)
fiatAmountFormatted = tokenFiatAmountFormatted,
),
pendingTransactions = tokenPendingTransactions.removeUnknownTransactions(),
mainButton = WalletMainButton.SendButton(isTokenSendButtonEnabled),
@ -140,9 +144,8 @@ class OnWalletLoadedReducer {
wallet.blockchain.decimals(),
wallet.blockchain.currency
)
val fiatRate = walletState.primaryWallet?.fiatRate
val fiatAmountRaw = fiatRate?.multiply(amount)?.setScale(2, RoundingMode.DOWN)
val fiatAmount = fiatRate?.let { amount?.toFiatString(it, fiatCurrencyName) }
val fiatAmount = walletState.primaryWallet?.fiatRate?.let { amount?.toFiatValue(it) }
val fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(fiatCurrencyName) ?: UNKNOWN_AMOUNT_SIGN
val pendingTransactions = wallet.getPendingTransactions()
val sendButtonEnabled = amount?.isZero() == false && pendingTransactions.isEmpty()
@ -159,8 +162,8 @@ class OnWalletLoadedReducer {
blockchainAmount = amount,
amount = amount,
amountFormatted = formattedAmount,
fiatAmountFormatted = fiatAmount,
fiatAmount = fiatAmountRaw
fiatAmount = fiatAmount,
fiatAmountFormatted = fiatAmountFormatted
),
pendingTransactions = pendingTransactions.removeUnknownTransactions(),
mainButton = WalletMainButton.SendButton(sendButtonEnabled),

View file

@ -19,7 +19,7 @@ import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.wallet.models.WalletRent
import com.tangem.tap.features.wallet.redux.AddressData
import com.tangem.tap.features.wallet.redux.Artwork
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.redux.ErrorType
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.features.wallet.redux.TradeCryptoState
@ -217,8 +217,8 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
currency = walletBlockchain.currency,
),
fiatAmount = fiatAmount,
fiatAmountFormatted = fiatAmount.toFiatRateString(
fiatCurrencyName = store.state.globalState.appCurrency.symbol
fiatAmountFormatted = fiatAmount.toFormattedFiatValue(
fiatCurrencyName = state.globalState.appCurrency.symbol
),
amountToCreateAccount = action.amountToCreateAccount,
)
@ -439,7 +439,7 @@ private fun setMultiWalletFiatRate(
wallet?.getTokenAmount(currency.token)?.value?.toFiatValue(rate)
}
if (currencyData.status == BalanceStatus.NoAccount && fiatAmount == null) {
fiatAmount = BigDecimal.ZERO
fiatAmount = BigDecimal.ZERO.setScale(2)
}
val fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(appCurrency.symbol)

View file

@ -26,8 +26,8 @@ data class BalanceWidgetData(
val blockchainAmount: BigDecimal? = BigDecimal.ZERO,
val amount: BigDecimal? = null,
val amountFormatted: String? = null,
val fiatAmountFormatted: String? = null,
val fiatAmount: BigDecimal? = null,
val fiatAmountFormatted: String? = null,
val token: TokenData? = null,
val amountToCreateAccount: String? = null,
val errorMessage: String? = null

View file

@ -1,11 +1,7 @@
package com.tangem.tap.features.wallet.ui
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.view.*
import android.widget.TextView
import androidx.activity.OnBackPressedCallback
import androidx.annotation.ColorRes
@ -15,31 +11,21 @@ import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.transition.TransitionInflater
import by.kirich1409.viewbindingdelegate.viewBinding
import com.squareup.picasso.Picasso
import com.tangem.tangem_sdk_new.extensions.dpToPx
import com.tangem.tap.common.SnackbarHandler
import com.tangem.tap.common.TestActions
import com.tangem.tap.common.extensions.appendIfNotNull
import com.tangem.tap.common.extensions.beginDelayedTransition
import com.tangem.tap.common.extensions.fitChipsByGroupWidth
import com.tangem.tap.common.extensions.getColor
import com.tangem.tap.common.extensions.getString
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.loadCurrenciesIcon
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.extensions.toQrCode
import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.recyclerView.SpaceItemDecoration
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.onboarding.getQRReceiveMessage
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.models.PendingTransaction
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.features.wallet.redux.ErrorType
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletData
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.redux.*
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN
import com.tangem.tap.features.wallet.ui.adapters.PendingTransactionsAdapter
import com.tangem.tap.features.wallet.ui.adapters.WalletDetailWarningMessagesAdapter
import com.tangem.tap.features.wallet.ui.images.loadCurrencyIcon
import com.tangem.tap.features.wallet.ui.test.TestWalletDetails
import com.tangem.tap.store
import com.tangem.wallet.R
@ -143,16 +129,18 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
handleCurrencyIcon(selectedWallet)
handleWarnings(selectedWallet)
updateViewMeasurements()
binding.srlWalletDetails.setOnRefreshListener {
if (selectedWallet.currencyData.status != BalanceStatus.Loading) {
store.dispatch(WalletAction.LoadWallet(
blockchain = BlockchainNetwork(
selectedWallet.currency.blockchain,
selectedWallet.currency.derivationPath,
emptyList()
blockchain = BlockchainNetwork(
selectedWallet.currency.blockchain,
selectedWallet.currency.derivationPath,
emptyList()
)
)
))
)
}
}
@ -161,13 +149,27 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
}
}
private fun updateViewMeasurements() {
val tvFiatAmount = binding.lWalletDetails.lBalance.tvFiatAmount
val paddingStart = when (tvFiatAmount.text) {
UNKNOWN_AMOUNT_SIGN -> 16f
else -> 12f
}
tvFiatAmount.setPadding(
tvFiatAmount.dpToPx(paddingStart).toInt(),
tvFiatAmount.paddingTop,
tvFiatAmount.paddingEnd,
tvFiatAmount.paddingBottom,
)
}
private fun setupCurrency(currencyData: BalanceWidgetData, currency: Currency) = with(binding) {
tvCurrencyTitle.text = currencyData.currency
if (currency is Currency.Token) {
binding.tvCurrencySubtitle.text = currency.blockchain.tokenDisplayName()
binding.tvCurrencySubtitle.show()
tvCurrencySubtitle.text = currency.blockchain.tokenDisplayName()
tvCurrencySubtitle.show()
} else {
binding.tvCurrencySubtitle.hide()
tvCurrencySubtitle.hide()
}
}
@ -199,9 +201,9 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
}
private fun handleCurrencyIcon(wallet: WalletData) = with(binding.lWalletDetails.lBalance) {
Picasso.get().loadCurrenciesIcon(
imageView = ivCurrency,
textView = tvTokenLetter,
loadCurrencyIcon(
currencyImageView = ivCurrency,
currencyTextView = tvTokenLetter,
blockchain = wallet.currency.blockchain,
token = (wallet.currency as? Currency.Token)?.token
)
@ -295,7 +297,10 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
lBalance.root.show()
lBalance.groupBalance.hide()
lBalance.tvError.show()
lBalance.tvError.setWarningStatus(R.string.wallet_balance_blockchain_unreachable, data.errorMessage)
lBalance.tvError.setWarningStatus(
R.string.wallet_balance_blockchain_unreachable,
data.errorMessage
)
}
BalanceStatus.NoAccount -> {
lBalance.root.hide()
@ -314,9 +319,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
return when (item.itemId) {
R.id.menu_remove -> {
store.state.walletState.getSelectedWalletData()?.let { walletData ->
store.dispatch(WalletAction.MultiWallet.RemoveWallet(walletData))
store.dispatch(WalletAction.MultiWallet.SelectWallet(null))
store.dispatch(NavigationAction.PopBackTo())
store.dispatch(WalletAction.MultiWallet.TryToRemoveWallet(walletData))
true
}
false
@ -327,10 +330,6 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.wallet_details, menu)
val walletCanBeRemoved = store.state.walletState.canBeRemoved(
store.state.walletState.getSelectedWalletData()
)
menu.getItem(0).isEnabled = walletCanBeRemoved
}
private fun TextView.setWarningStatus(mainMessage: Int, error: String? = null) {
@ -346,7 +345,11 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
setStatus(getString(mainMessage), R.color.darkGray4, null)
}
private fun TextView.setStatus(text: String, @ColorRes color: Int, @DrawableRes drawable: Int?) {
private fun TextView.setStatus(
text: String,
@ColorRes color: Int,
@DrawableRes drawable: Int?
) {
this.text = text
setTextColor(getColor(color))
setCompoundDrawablesWithIntrinsicBounds(drawable ?: 0, 0, 0, 0)

View file

@ -12,7 +12,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.transition.TransitionInflater
import by.kirich1409.viewbindingdelegate.viewBinding
import com.squareup.picasso.Picasso
import coil.load
import com.tangem.tap.MainActivity
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.recyclerView.SpaceItemDecoration
@ -165,11 +165,11 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
}
private fun setupCardImage(cardImage: Artwork?) {
Picasso.get()
.load(cardImage?.artworkId)
.placeholder(R.drawable.card_placeholder_black)
?.error(R.drawable.card_placeholder_black)
?.into(binding.ivCard)
binding.ivCard.load(cardImage?.artworkId) {
placeholder(R.drawable.card_placeholder_black)
error(R.drawable.card_placeholder_black)
fallback(R.drawable.card_placeholder_black)
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {

View file

@ -6,18 +6,17 @@ import androidx.core.view.isVisible
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.squareup.picasso.Picasso
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.domain.common.TapWorkarounds.derivationStyle
import com.tangem.tap.common.extensions.getString
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.loadCurrenciesIcon
import com.tangem.tap.common.extensions.show
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletData
import com.tangem.tap.features.wallet.ui.BalanceStatus
import com.tangem.tap.features.wallet.ui.images.loadCurrencyIcon
import com.tangem.tap.store
import com.tangem.wallet.R
import com.tangem.wallet.databinding.ItemCurrencyWalletBinding
@ -98,16 +97,16 @@ class WalletAdapter
lContent.root.show()
}
Picasso.get().loadCurrenciesIcon(
imageView = ivCurrency,
textView = tvTokenLetter,
loadCurrencyIcon(
currencyImageView = ivCurrency,
currencyTextView = tvTokenLetter,
token = (wallet.currency as? Currency.Token)?.token,
blockchain = wallet.currency.blockchain,
)
lContent.tvCurrency.text = wallet.currencyData.currency
lContent.tvAmountFiat.text = wallet.currencyData.fiatAmountFormatted ?: ""
lContent.tvAmount.text = wallet.currencyData.amountFormatted ?: ""
lContent.tvAmountFiat.text = wallet.currencyData.fiatAmountFormatted
lContent.tvAmount.text = wallet.currencyData.amountFormatted
lContent.tvStatus.isVisible = statusMessage != null
lContent.tvStatus.text = statusMessage

View file

@ -5,16 +5,12 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.appcompat.view.ContextThemeWrapper
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.*
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.tangem.blockchain.common.Amount
import com.tangem.tap.common.extensions.toFormattedString
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletDialog
import com.tangem.tap.features.wallet.redux.models.WalletDialog
import com.tangem.tap.store
import com.tangem.wallet.R
import com.tangem.wallet.databinding.DialogWalletSendBinding

View file

@ -0,0 +1,109 @@
package com.tangem.tap.features.wallet.ui.images
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.widget.ImageView
import android.widget.TextView
import androidx.constraintlayout.utils.widget.ImageFilterView
import coil.imageLoader
import coil.load
import coil.request.ImageRequest
import coil.transform.RoundedCornersTransformation
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.IconsUtil
import com.tangem.blockchain.common.Token
import com.tangem.domain.common.extensions.toNetworkId
import com.tangem.tap.common.extensions.getColor
import com.tangem.tap.common.extensions.getRoundIconRes
import com.tangem.tap.common.extensions.getTextColor
import com.tangem.tap.domain.extensions.getCustomIconUrl
import com.tangem.tap.domain.tokens.getIconUrl
import com.tangem.wallet.R
private const val QCX = "QCX"
private const val VOYR = "VOYRME"
fun loadCurrencyIcon(
currencyImageView: ImageFilterView,
currencyTextView: TextView,
token: Token?,
blockchain: Blockchain,
) {
when {
token == null -> currencyImageView.loadIcon(
iconUrl = getIconUrl(blockchain.toNetworkId()),
placeholderRes = blockchain.getRoundIconRes(),
onStart = {
if (blockchain.isTestnet()) {
currencyImageView.saturation = 0f
} else {
currencyImageView.colorFilter = null
}
}
)
token.symbol == QCX -> currencyImageView.load(R.drawable.ic_qcx)
token.symbol == VOYR -> currencyImageView.load(R.drawable.ic_voyr)
else -> currencyImageView.loadIcon(
iconUrl = getTokenIconUrl(token, blockchain),
placeholderRes = R.drawable.shape_circle,
onStart = {
currencyTextView.text = token.symbol.take(1)
currencyTextView.setTextColor(token.getTextColor())
if (blockchain.isTestnet()) {
currencyImageView.saturation = 0f
}
},
onError = {
currencyImageView.colorFilter = PorterDuffColorFilter(
/* color = */
token.getColor(),
/* mode = */
PorterDuff.Mode.SRC_ATOP,
)
},
onSuccess = {
if (!blockchain.isTestnet()) {
currencyImageView.colorFilter = null
}
}
)
}
}
private inline fun ImageView.loadIcon(
iconUrl: String?,
placeholderRes: Int,
crossinline onStart: () -> Unit = {},
crossinline onSuccess: () -> Unit = {},
crossinline onError: () -> Unit = {},
) {
ImageRequest.Builder(context)
.data(iconUrl)
.placeholder(placeholderRes)
.error(placeholderRes)
.fallback(placeholderRes)
.transformations(
RoundedCornersTransformation(
topLeft = 32f,
topRight = 32f,
bottomLeft = 32f,
bottomRight = 32f
)
)
.listener(
onStart = { onStart() },
onSuccess = { _, _ -> onSuccess() },
onError = { _, _ -> onError() }
)
.target(imageView = this)
.build()
.also(context.imageLoader::enqueue)
}
private fun getTokenIconUrl(token: Token, blockchain: Blockchain): String? {
return token.id?.let(::getIconUrl)
?: token.getCustomIconUrl()
?: IconsUtil.getTokenIconUri(blockchain, token)
?.toString()
}

View file

@ -3,7 +3,7 @@ package com.tangem.tap.features.wallet.ui.wallet
import android.content.Context
import androidx.appcompat.app.AlertDialog
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletDialog
import com.tangem.tap.features.wallet.redux.models.WalletDialog
import com.tangem.tap.store
import com.tangem.wallet.R

View file

@ -10,13 +10,9 @@ import com.tangem.tap.common.extensions.fitChipsByGroupWidth
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.models.PendingTransaction
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.features.wallet.redux.TradeCryptoState
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletData
import com.tangem.tap.features.wallet.redux.WalletMainButton
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.redux.*
import com.tangem.tap.features.wallet.ui.BalanceWidget
import com.tangem.tap.features.wallet.ui.MultipleAddressUiHelper
import com.tangem.tap.features.wallet.ui.WalletFragment

View file

@ -1,10 +0,0 @@
<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="M17,12C17,9.24 14.76,7 12,7C9.24,7 7,9.24 7,12C7,14.76 9.24,17 12,17C14.76,17 17,14.76 17,12ZM12,15C10.35,15 9,13.65 9,12C9,10.35 10.35,9 12,9C13.65,9 15,10.35 15,12C15,13.65 13.65,15 12,15ZM5,15H3V19C3,20.1 3.9,21 5,21H9V19H5V15ZM5,5H9V3H5C3.9,3 3,3.9 3,5V9H5V5ZM19,3H15V5H19V9H21V5C21,3.9 20.1,3 19,3ZM19,19H15V21H19C20.1,21 21,20.1 21,19V15H19V19Z"
android:fillColor="#000"
android:fillType="evenOdd" />
</vector>

View 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.2,4.4C3.5,4.4 2.8,5 2.8,6V6.1C3.2,5.9 3.7,5.8 4.2,5.8H19.8C20.3,5.8 20.8,5.9 21.2,6.1V6C21.2,5 20.5,4.4 19.8,4.4H4.2ZM4.2,7.4C3.4,7.4 2.8,8 2.8,8.6V9.2C3.2,8.9 3.7,8.8 4.2,8.8H8.7C9.6,8.8 10.3,9.6 10.5,10.4L10.5,10.4C10.7,11.3 11.4,11.8 12,11.8C12.6,11.8 13.3,11.3 13.5,10.4C13.7,9.6 14.4,8.8 15.3,8.8H19.8C20.4,8.8 20.8,8.9 21.2,9.2V8.6C21.2,8 20.7,7.4 19.8,7.4H4.2ZM21.2,12.3C21.2,11.1 20.4,10.4 19.8,10.4H15.3C15.3,10.4 15.3,10.4 15.3,10.5C15.2,10.5 15.1,10.6 15.1,10.8L15.1,10.8C14.7,12.2 13.5,13.4 12,13.4C10.5,13.4 9.3,12.2 8.9,10.8C8.9,10.6 8.8,10.5 8.8,10.5C8.7,10.4 8.7,10.4 8.7,10.4H4.2C3.6,10.4 2.8,11.1 2.8,12.3V18.3C2.8,19.2 3.5,19.8 4.2,19.8H19.8C20.5,19.8 21.2,19.2 21.2,18.3V12.3ZM1.2,12.3V18.3C1.2,19.9 2.5,21.4 4.2,21.4H19.8C21.5,21.4 22.8,19.9 22.8,18.3V12.3V8.6V6C22.8,4.3 21.5,2.8 19.8,2.8H4.2C2.5,2.8 1.2,4.3 1.2,6V8.6V12.3Z"
android:fillColor="#000000"
android:fillType="evenOdd" />
</vector>

View file

@ -22,8 +22,9 @@
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:menu="@menu/wallet"
app:navigationIcon="@drawable/ic_scan_card_24"
app:navigationIconTint="@color/iconGray"
app:navigationIcon="@drawable/ic_wallet_24"
app:navigationIconTint="@color/darkGray6"
app:titleCentered="true"
app:title="@string/wallet_title" />
</com.google.android.material.appbar.AppBarLayout>
@ -151,28 +152,6 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/rv_pending_transaction" />
<include
android:id="@+id/l_buttons_long"
layout="@layout/layout_wallet_long_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:layout_marginTop="30dp"
app:layout_constraintTop_toBottomOf="@id/l_address"
app:layout_constraintVertical_bias="1"
app:layout_constraintBottom_toBottomOf="parent" />
<include
android:id="@+id/l_buttons_short"
layout="@layout/layout_wallet_short_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:layout_marginTop="30dp"
app:layout_constraintTop_toBottomOf="@id/l_address"
app:layout_constraintVertical_bias="1"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
@ -192,6 +171,28 @@
android:text="@string/main_manage_tokens"
android:elevation="4dp"
app:cornerRadius="8dp"
app:layout_constraintBottom_toBottomOf="parent" />
tools:visibility="gone" />
<include
android:id="@+id/l_buttons_long"
layout="@layout/layout_wallet_long_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginBottom="16dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:visibility="gone" />
<include
android:id="@+id/l_buttons_short"
layout="@layout/layout_wallet_short_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginBottom="16dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:visibility="gone" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -35,7 +35,8 @@
android:visibility="gone"
app:drawableStartCompat="@drawable/ic_ok"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_currency" />
app:layout_constraintTop_toBottomOf="@id/tv_currency"
tools:visibility="visible" />
<TextView
android:id="@+id/tv_status_error"
@ -89,10 +90,10 @@
app:layout_constraintTop_toBottomOf="@id/tv_currency" />
<TextView
android:id="@+id/tv_amount"
android:id="@+id/tv_fiat_amount"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginEnd="15dp"
android:ellipsize="end"
android:gravity="end|bottom"
android:maxLines="1"
@ -100,27 +101,32 @@
android:textSize="20sp"
android:textStyle="bold"
app:autoSizeMaxTextSize="20sp"
app:autoSizeMinTextSize="12sp"
app:autoSizeMinTextSize="10sp"
app:autoSizeStepGranularity="1sp"
app:autoSizeTextType="uniform"
app:layout_constraintBottom_toBottomOf="@id/tv_currency"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toEndOf="@id/tv_currency"
tools:text="0.43 BTC" />
tools:text="3.58 USD" />
<TextView
android:id="@+id/tv_fiat_amount"
android:layout_width="wrap_content"
android:id="@+id/tv_amount"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:layout_marginEnd="16dp"
android:gravity="end|bottom"
android:maxLines="1"
android:paddingTop="4dp"
android:paddingEnd="16dp"
android:textColor="@color/darkGray2"
android:textSize="14sp"
app:autoSizeMaxTextSize="14sp"
app:autoSizeMinTextSize="8sp"
app:autoSizeStepGranularity="1sp"
app:autoSizeTextType="uniform"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_amount"
tools:text="USD 3 588" />
app:layout_constraintStart_toEndOf="@+id/tv_status_verified"
app:layout_constraintTop_toBottomOf="@id/tv_fiat_amount"
tools:text="123.12387628 XTZ" />
<TextView
android:id="@+id/tv_placeholder"
@ -132,8 +138,7 @@
android:textSize="14sp"
android:visibility="invisible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_amount"
tools:text="USD 3 588" />
app:layout_constraintTop_toBottomOf="@id/tv_fiat_amount" />
<View
@ -179,8 +184,7 @@
app:layout_constraintHorizontal_bias="1"
app:layout_constraintStart_toEndOf="@id/tv_base_currency"
app:layout_constraintTop_toBottomOf="@id/v_divider"
tools:text="0.00434143 ETH"
/>
tools:text="0.00434143 ETH" />
<androidx.constraintlayout.widget.Group
android:id="@+id/group_base_currency"

View file

@ -7,13 +7,12 @@
android:layout_height="120dp">
<TextView
android:id="@+id/tv_amount"
android:id="@+id/tv_fiat_amount"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:ellipsize="end"
android:maxLines="2"
android:paddingStart="16dp"
android:paddingStart="12dp"
android:paddingTop="16dp"
android:paddingEnd="16dp"
android:textColor="@color/darkGray6"
@ -23,10 +22,10 @@
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="2000.43 USD" />
tools:text="496304.28 RUB" />
<TextView
android:id="@+id/tv_fiat_amount"
android:id="@+id/tv_amount"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingStart="16dp"
@ -37,8 +36,8 @@
app:layout_constraintEnd_toStartOf="@id/iv_currency"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_amount"
tools:text="3.588 ETH" />
app:layout_constraintTop_toBottomOf="@id/tv_fiat_amount"
tools:text="0.43 BTC" />
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/iv_currency"
@ -73,7 +72,7 @@
android:paddingEnd="16dp"
android:textSize="14sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_fiat_amount"
app:layout_constraintTop_toBottomOf="@id/tv_amount"
tools:text="@string/wallet_balance_verified" />
<TextView
@ -87,17 +86,17 @@
android:paddingTop="16dp"
android:paddingEnd="16dp"
android:textSize="14sp"
android:visibility="visible"
android:visibility="gone"
app:layout_constraintEnd_toStartOf="@+id/iv_currency"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_fiat_amount"
tools:text="@string/lorem_ipsum" />
app:layout_constraintTop_toBottomOf="@id/tv_amount"
tools:text="Some error" />
<androidx.constraintlayout.widget.Group
android:id="@+id/group_balance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
android:visibility="visible"
app:constraint_referenced_ids="tv_amount, tv_fiat_amount, tv_status" />

View file

@ -17,11 +17,8 @@
android:id="@+id/btn_confirm_long"
style="@style/TapButtonWithIcon"
android:layout_width="0dp"
android:layout_marginTop="30dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="8dp"
android:text="@string/wallet_button_send"
app:icon="@drawable/ic_send"
app:icon="@drawable/ic_send"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/guideline"

View file

@ -6,38 +6,32 @@
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_trade"
style="@style/TapButtonWithIcon"
android:layout_width="0dp"
android:layout_marginStart="16dp"
android:layout_marginTop="30dp"
android:layout_marginBottom="8dp"
android:text="@string/wallet_button_trade"
app:icon="@drawable/ic_arrows_up_down"
android:id="@+id/btn_trade"
style="@style/TapButtonWithIcon"
android:layout_width="0dp"
android:text="@string/wallet_button_trade"
app:icon="@drawable/ic_arrows_up_down"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/btn_confirm"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:iconGravity="textEnd"
android:textAlignment="center"
app:layout_constraintVertical_bias="1" />
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:iconGravity="textEnd"
android:textAlignment="center"
app:layout_constraintVertical_bias="1" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_confirm"
style="@style/TapButtonWithIcon"
android:layout_width="0dp"
android:layout_marginStart="8dp"
android:layout_marginTop="30dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="8dp"
android:text="@string/wallet_button_send"
app:icon="@drawable/ic_send"
app:iconGravity="textEnd"
android:id="@+id/btn_confirm"
style="@style/TapButtonWithIcon"
android:layout_width="0dp"
android:layout_marginStart="8dp"
android:text="@string/wallet_button_send"
app:icon="@drawable/ic_send"
app:iconGravity="textEnd"
android:textAlignment="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/btn_trade"
app:layout_constraintVertical_bias="1" />
app:layout_constraintStart_toEndOf="@id/btn_trade"
app:layout_constraintVertical_bias="1" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -5,6 +5,6 @@
android:id="@+id/details_menu"
android:title="@string/details_title"
android:icon="@drawable/ic_more_vertical_24"
app:iconTint="@color/iconGray"
app:iconTint="@color/darkGray6"
app:showAsAction="ifRoom" />
</menu>

View file

@ -59,11 +59,6 @@
<string name="send_fee_label">Gebühr</string>
<string name="send_total_label">Gesamt</string>
<string name="send_max_amount_label">Höchstbetrag</string>
<string name="send_validation_invalid_address">Ungültige Adresse</string>
<string name="send_validation_invalid_amount">Ungültiger Betrag</string>
<string name="send_validation_amount_exceeds_balance">Der Betrag geht über die Bilanz hinaus</string>
<string name="send_validation_invalid_total">Der Gesamtbetrag geht über die Bilanz hinaus</string>
<string name="send_validation_invalid_fee">Die Gebühr geht über die Bilanz hinaus</string>
<string name="send_fee_picker_low">Niedrig</string>
<string name="send_fee_picker_normal">Normal</string>
<string name="send_fee_picker_priority">Priorität</string>
@ -73,7 +68,6 @@
<string name="send_total_subtitle_asset_format">%s %s und %s %s werden gesendet</string>
<string name="send_balance_subtitle_format">Bilanz: %s %s</string>
<string name="send_transaction_success">Die Transaktion wurde erfolgreich signiert und an den Blockchain-Knoten gesendet. Die Walletbilanz wird aktualisiert</string>
<string name="send_error_no_account_xlm">Um ein Konto zu erstellen, senden Sie 1+ XLM an diese Adresse</string>
<string name="details_title">Details</string>
<string name="details_row_title_cid">KartenID</string>
<string name="details_row_title_issuer">Emittent</string>
@ -165,21 +159,12 @@ RECHTLICHER HAFTUNGSAUSSCHLUSS
<string name="disclaimer_title">Nutzungsbedingungen</string>
<string name="wallet_notification_no_internet">Keine Internetverbindung</string>
<string name="send_error_no_target_account">Das Zielkonto ist nicht erstellt. Der abzusendende Betrag soll %1$s %2$s + Gebühr oder mehr sein</string>
<string name="send_error_fee_request_failed">Erhalt der Gebühr fehlgeschlagen</string>
<string name="send_error_unknown">Unbekannter Fehler</string>
<string name="send_error_payid_verification_failed">Verifikation der PayString ist fehlgeschlagen</string>
<string name="send_error_payid_unsupported_by_blockchain">PayString wird von der Blockchain nicht unterstützt</string>
<string name="send_error_payid_not_registered">PayString ist nicht registriert</string>
<string name="send_error_payid_request_failed">PayString-Anfrage ist fehlgeschlagen</string>
<string name="send_error_address_same_as_wallet">Die Adresse stimmt mit der Adresse Ihrer Brieftasche überein</string>
<string name="send_error_insufficient_balance">Nicht genug Geld</string>
<string name="send_error_blockchain_internal">Interner Fehler der Blockchain</string>
<string name="send_error_invalid_fee_value">Falsche Gebühr</string>
<string name="send_error_dust_amount_format">Minimaler Betrag ist %s</string>
<string name="send_error_dust_change">Restbestand zu klein</string>
<string name="wallet_notification_address_copied">Die Adresse wurde erfolgreich kopiert</string>
@ -192,8 +177,4 @@ RECHTLICHER HAFTUNGSAUSSCHLUSS
<string name="details_notification_erase_wallet_not_possible">Ihr Kontostand in dieser Brieftasche ist nicht Null, oder Sie haben unbestätigte Transaktionen. Es ist unmöglich die Funktion der Brieftasche zu löschen.</string>
<string name="details_notification_security_option_already_active">Dies ist die derzeit aktive Wahl</string>
<string name="xtz_withdrawal_message_warning">Damit Sie beim nächsten Aufladen Ihrer Brieftasche keine erhöhte Provision zahlen, soll der Betrag um %s XTZ reduziert werden</string>
<string name="xtz_withdrawal_message_reduce">Um %s XTZ reduzieren</string>
<string name="xtz_withdrawal_message_ignore">Nein, alles senden</string>
</resources>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="send_error_insufficient_balance">Nicht genug Geld</string>
<string name="send_error_blockchain_internal">Interner Fehler der Blockchain</string>
<string name="send_error_invalid_fee_value">Falsche Gebühr</string>
<string name="send_error_dust_amount_format">Minimaler Betrag ist %s</string>
<string name="send_error_dust_change">Restbestand zu klein</string>
<string name="send_error_no_target_account">Das Zielkonto ist nicht erstellt. Der abzusendende Betrag soll %s %s + Gebühr oder mehr sein</string>
<string name="send_error_no_account_xlm">Um ein Konto zu erstellen, senden Sie 1+ XLM an diese Adresse</string>
<string name="send_error_fee_request_failed">Erhalt der Gebühr fehlgeschlagen</string>
<string name="send_error_unknown">Unbekannter Fehler</string>
<string name="xtz_withdrawal_message_warning">Damit Sie beim nächsten Aufladen Ihrer Brieftasche keine erhöhte Provision zahlen, soll der Betrag um %s XTZ reduziert werden</string>
<string name="xtz_withdrawal_message_reduce">Um %s XTZ reduzieren</string>
<string name="xtz_withdrawal_message_ignore">Nein, alles senden</string>
<string name="send_validation_invalid_address">Ungültige Adresse</string>
<string name="send_validation_invalid_amount">Ungültiger Betrag</string>
<string name="send_validation_amount_exceeds_balance">Der Betrag geht über die Bilanz hinaus</string>
<string name="send_validation_invalid_total">Der Gesamtbetrag geht über die Bilanz hinaus</string>
<string name="send_validation_invalid_fee">Die Gebühr geht über die Bilanz hinaus</string>
</resources>

View file

@ -47,5 +47,15 @@
<string name="alert_funds_restoration_message">If you chose the wrong network during your crypto transfer from the exchange, this guide will help you recover your funds</string>
<string name="common_custom">Custom</string>
<string name="common_notice">Notice</string>
<string name="common_attention">Attention</string>
<string name="common_server_unavailable">The server is not available, please try again later</string>
<string name="token_details_hide_alert_title">Hide %s</string>
<string name="token_details_hide_alert_hide">Hide</string>
<string name="token_details_hide_alert_message">You hide the token from the main screen, but you can add it back at any time.</string>
<string name="token_details_unable_hide_alert_title">Unable to hide %s</string>
<string name="token_details_unable_hide_alert_message">The %s token is the main currency on the %s network and cannot be hidden as long as you have other tokens on this network in the list.</string>
<string name="wallet_connect_network_not_found_format">%s network not found. Please, add it first and try again.</string>
</resources>

View file

@ -60,11 +60,6 @@
<string name="send_fee_label">Commissions </string>
<string name="send_total_label">Total </string>
<string name="send_max_amount_label">Somme maximale </string>
<string name="send_validation_invalid_address">Adresse incorrecte </string>
<string name="send_validation_invalid_amount">Somme incorrecte </string>
<string name="send_validation_amount_exceeds_balance">Le montant dépasse le solde </string>
<string name="send_validation_invalid_total">Le total dépasse le solde </string>
<string name="send_validation_invalid_fee">Les commissions dépassent le solde </string>
<string name="send_fee_picker_low">Bas </string>
<string name="send_fee_picker_normal">Normal </string>
<string name="send_fee_picker_priority">Priorité </string>
@ -74,7 +69,6 @@
<string name="send_total_subtitle_asset_format">Sera envoyé %s %s et %s %s</string>
<string name="send_balance_subtitle_format">Solde : %s %s</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_error_no_account_xlm">Pour créer un compte, envoyez 1+ XLM à cette adresse</string>
<string name="details_title">Référénces </string>
<string name="details_row_title_cid">ID de la carte </string>
<string name="details_row_title_issuer">Emetteur </string>
@ -163,20 +157,11 @@ Avertissement
<string name="disclaimer_title"> Conditions d\'utilisation</string>
<string name="wallet_notification_no_internet">Pas de connexion internet</string>
<string name="send_error_no_target_account">Le compte cible n\'a pas été créé. Le montant à envoyer doit être de %1$s %2$s + commissions ou plus</string>
<string name="send_error_fee_request_failed">Échec de réception des commissions</string>
<string name="send_error_unknown">Erreur inconnue</string>
<string name="send_error_payid_verification_failed">La vérification de PayString a échoué</string>
<string name="send_error_payid_unsupported_by_blockchain">PayString non pris en charge par la blockchain</string>
<string name="send_error_payid_not_registered">PayString non enregistré</string>
<string name="send_error_payid_request_failed">La demande de PayString a échoué</string>
<string name="send_error_address_same_as_wallet">L\'adresse est la même que celle de votre portefeuille</string>
<string name="send_error_insufficient_balance">Solde insuffisant</string>
<string name="send_error_blockchain_internal">Erreur interne de la blockchain</string>
<string name="send_error_invalid_fee_value">Commission non valide</string>
<string name="send_error_dust_amount_format">Le montant minimal est de %ы</string>
<string name="send_error_dust_change">Le reste est trop petit</string>
<string name="wallet_notification_address_copied">L\'adresse a été copiée avec succès</string>
<string name="wallet_create_payid_empty">Saisissez d\'abord le PayString requis</string>
<string name="initial_message_scan_header">Posez pour scanner</string>
@ -184,8 +169,5 @@ Avertissement
<string name="details_notification_erase_wallet_not_possible">Votre solde sur ce portefeuille n\'est pas nul, ou vous avez des transactions non confirmées. Impossible de supprimer la fonction du portefeuille</string>
<string name="details_notification_security_option_already_active">Cette option est déjà active</string>
<string name="xtz_withdrawal_message_warning">Pour ne pas payer une commission élevée la prochaine fois que vous rechargez votre portefeuille, veuillez réduire le montant de %s XTZ</string>
<string name="xtz_withdrawal_message_reduce">Réduire de% s XTZ</string>
<string name="xtz_withdrawal_message_ignore">Non, envoyer toute la somme</string>
</resources>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="send_error_insufficient_balance">Solde insuffisant</string>
<string name="send_error_blockchain_internal">Erreur interne de la blockchain</string>
<string name="send_error_invalid_fee_value">Commission non valide</string>
<string name="send_error_dust_amount_format">Le montant minimal est de %s</string>
<string name="send_error_dust_change">Le reste est trop petit</string>
<string name="send_error_no_target_account">Le compte cible n\'a pas été créé. Le montant à envoyer doit être de %s %s + commissions ou plus</string>
<string name="send_error_no_account_xlm">Pour créer un compte, envoyez 1+ XLM à cette adresse</string>
<string name="send_error_fee_request_failed">Échec de réception des commissions</string>
<string name="send_error_unknown">Erreur inconnue</string>
<string name="send_validation_invalid_address">Adresse incorrecte</string>
<string name="send_validation_invalid_amount">Somme incorrecte</string>
<string name="send_validation_amount_exceeds_balance">Le montant dépasse le solde</string>
<string name="send_validation_invalid_total">Le total dépasse le solde</string>
<string name="send_validation_invalid_fee">Les commissions dépassent le solde</string>
<string name="xtz_withdrawal_message_warning">Pour ne pas payer une commission élevée la prochaine fois que vous rechargez votre portefeuille, veuillez réduire le montant de %s XTZ</string>
<string name="xtz_withdrawal_message_reduce">Réduire de %s XTZ</string>
<string name="xtz_withdrawal_message_ignore">Non, envoyer toute la somme</string>
</resources>

View file

@ -47,5 +47,15 @@
<string name="alert_funds_restoration_message">If you chose the wrong network during your crypto transfer from the exchange, this guide will help you recover your funds</string>
<string name="common_custom">Custom</string>
<string name="common_server_unavailable">The server is not available, please try again later</string>
<string name="common_notice">Notice</string>
<string name="common_attention">Attention</string>
<string name="token_details_hide_alert_title">Hide %s</string>
<string name="token_details_hide_alert_hide">Hide</string>
<string name="token_details_hide_alert_message">You hide the token from the main screen, but you can add it back at any time.</string>
<string name="token_details_unable_hide_alert_title">Unable to hide %s</string>
<string name="token_details_unable_hide_alert_message">The %s token is the main currency on the %s network and cannot be hidden as long as you have other tokens on this network in the list.</string>
<string name="wallet_connect_network_not_found_format">%s network not found. Please, add it first and try again.</string>
</resources>

View file

@ -58,10 +58,7 @@
<string name="send_fee_label">Commissione</string>
<string name="send_total_label">Totale</string>
<string name="send_max_amount_label">Importo totale</string>
<string name="send_validation_invalid_address">Indirizzo non valido</string>
<string name="send_validation_invalid_amount">Importo non valido</string>
<string name="send_validation_invalid_total">Il totale supera il saldo</string>
<string name="send_validation_invalid_fee">La commissione supera il saldo </string>
<string name="send_fee_picker_low">Insufficiente</string>
<string name="send_fee_picker_normal">Normale</string>
<string name="send_fee_picker_priority">Prioritario</string>
@ -71,7 +68,6 @@
<string name="send_total_subtitle_asset_format">Sarà inviato %1$s %2$s e %1$s %2$s</string>
<string name="send_balance_subtitle_format">Saldo: %1$s %2$s</string>
<string name="send_transaction_success">La transazione è stata firmata con successo e inviata al nodo blockchain. Il saldo del portafoglio verrà aggiornato dopo un po\' di tempo </string>
<string name="send_error_no_account_xlm">Per creare un account, invia 1+ XLM a questo indirizzo</string>
<string name="details_title">Requisiti</string>
<string name="details_row_title_cid">ID carta</string>
<string name="details_row_title_issuer">Emittente</string>
@ -159,26 +155,16 @@ Salvo quanto diversamente previsto dalla legge, i proprietari o i contributori d
Questa nota legale è stata modificata l\'ultima volta 01.10.2020.
</string>
<string name="send_error_no_target_account">Per creare un account, invia %1$s %2$s a questo indirizzo</string>
<string name="send_error_fee_request_failed">Impossibile ottenere la commissione</string>
<string name="common_accept">Accetta</string>
<string name="send_validation_amount_exceeds_balance">L\'importo supera il saldo</string>
<string name="disclaimer_title">Termini del servizio</string>
<string name="wallet_notification_no_internet">Nessuna connessione a Internet</string>
<string name="send_error_unknown">Errore sconosciuto</string>
<string name="send_error_payid_verification_failed">Verifica del PayString fallita</string>
<string name="send_error_payid_unsupported_by_blockchain">PayString non supportato dalla blockchain</string>
<string name="send_error_payid_not_registered">PayString non registrato</string>
<string name="send_error_payid_request_failed">Richiesta PayString fallita</string>
<string name="send_error_address_same_as_wallet">L\'indirizzo corrisponde all\'indirizzo del tuo portafoglio</string>
<string name="send_error_insufficient_balance">Saldo insufficiente</string>
<string name="send_error_blockchain_internal">Errore interno della blockchain</string>
<string name="send_error_invalid_fee_value">Commissione non valida</string>
<string name="send_error_dust_amount_format">L\'importo minimo è di %s</string>
<string name="send_error_dust_change">L\'importo residuo è molto basso</string>
<string name="wallet_notification_address_copied">L\'indirizzo è stato copiato con successo</string>
@ -191,8 +177,4 @@ Questa nota legale è stata modificata l\'ultima volta 01.10.2020.
<string name="details_notification_erase_wallet_not_possible">Il tuo saldo su questo portafoglio non è pari a zero o hai transazioni non confermate. Non è possibile eliminare la funzione portafoglio.</string>
<string name="details_notification_security_option_already_active">E\' l\'opzione attualmente attiva</string>
<string name="xtz_withdrawal_message_warning">Per evitare di pagare una commissione maggiore la prossima volta che ricarichi il tuo portafoglio, riduci l\'importo di %s XTZ</string>
<string name="xtz_withdrawal_message_reduce">Riduci di %s XTZ</string>
<string name="xtz_withdrawal_message_ignore">No, invia l\'intero importo</string>
</resources>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="send_error_insufficient_balance">Saldo insufficiente</string>
<string name="send_error_blockchain_internal">Errore interno della blockchain</string>
<string name="send_error_invalid_fee_value">Commissione non valida</string>
<string name="send_error_dust_amount_format">L\'importo minimo è di %s</string>
<string name="send_error_dust_change">L\'importo residuo è molto basso</string>
<string name="send_error_unknown">Errore sconosciuto</string>
<string name="send_validation_amount_exceeds_balance">L\'importo supera il saldo</string>
<string name="send_error_no_target_account">Per creare un account, invia %s %s a questo indirizzo</string>
<string name="send_error_fee_request_failed">Impossibile ottenere la commissione</string>
<string name="send_error_no_account_xlm">Per creare un account, invia 1+ XLM a questo indirizzo</string>
<string name="send_validation_invalid_address">Indirizzo non valido</string>
<string name="send_validation_invalid_amount">Importo non valido</string>
<string name="send_validation_invalid_total">Il totale supera il saldo</string>
<string name="send_validation_invalid_fee">La commissione supera il saldo</string>
<string name="xtz_withdrawal_message_warning">Per evitare di pagare una commissione maggiore la prossima volta che ricarichi il tuo portafoglio, riduci l\'importo di %s XTZ</string>
<string name="xtz_withdrawal_message_reduce">Riduci di %s XTZ</string>
<string name="xtz_withdrawal_message_ignore">No, invia l\'intero importo</string>
</resources>

View file

@ -47,5 +47,16 @@
<string name="alert_funds_restoration_message">If you chose the wrong network during your crypto transfer from the exchange, this guide will help you recover your funds</string>
<string name="common_custom">Custom</string>
<string name="common_server_unavailable">The server is not available, please try again later</string>
<string name="common_notice">Notice</string>
<string name="common_attention">Attention</string>
<string name="token_details_hide_alert_title">Hide %s</string>
<string name="token_details_hide_alert_hide">Hide</string>
<string name="token_details_hide_alert_message">You hide the token from the main screen, but you can add it back at any time.</string>
<string name="token_details_unable_hide_alert_title">Unable to hide %s</string>
<string name="token_details_unable_hide_alert_message">The %s token is the main currency on the %s network and cannot be hidden as long as you have other tokens on this network in the list.</string>
<string name="wallet_connect_network_not_found_format">%s network not found. Please, add it first and try again.</string>
</resources>

View file

@ -60,11 +60,6 @@
<string name="send_fee_label">Комиссия</string>
<string name="send_total_label">Всего</string>
<string name="send_max_amount_label">Максимальная сумма</string>
<string name="send_validation_invalid_address">Неверный адрес</string>
<string name="send_validation_invalid_amount">Недопустимая сумма</string>
<string name="send_validation_amount_exceeds_balance">Сумма превышает баланс</string>
<string name="send_validation_invalid_total">Общая сумма превышает баланс</string>
<string name="send_validation_invalid_fee">Комиссия превышает баланс</string>
<string name="send_fee_picker_low">Низкий</string>
<string name="send_fee_picker_normal">Нормальный</string>
<string name="send_fee_picker_priority">Приоритетный</string>
@ -108,30 +103,19 @@
<string name="initial_message_change_passcode_body">Приложите, чтобы изменить пароль</string>
<string name="payid_service_error_loading">Неверный PayString</string>
<string name="disclaimer_title">Условия использования</string>
<string name="send_error_fee_request_failed">Не удалось получить комиссию</string>
<string name="send_error_no_target_account">Целевая учетная запись не создана. Сумма для отправки должна быть %1$s %2$s + плата за создание или больше</string>
<string name="wallet_notification_no_internet">Нет соединения с интернетом</string>
<string name="send_error_unknown">Неизвестная ошибка</string>
<string name="send_error_payid_verification_failed">Ошибка проверки PayString</string>
<string name="send_error_payid_unsupported_by_blockchain">PayString не поддерживается блокчейном</string>
<string name="send_error_payid_not_registered">PayString не зарегистрирован</string>
<string name="send_error_payid_request_failed">Не удалось выполнить запрос PayString</string>
<string name="send_error_address_same_as_wallet">Адрес совпадает с адресом кошелька</string>
<string name="send_error_insufficient_balance">Недостаточный баланс</string>
<string name="send_error_blockchain_internal">Внутренняя ошибка блокчейна</string>
<string name="send_error_invalid_fee_value">Неверная комиссия</string>
<string name="send_error_dust_amount_format">Минимальная сумма: %s</string>
<string name="send_error_dust_change">Сдача слишком мала</string>
<string name="send_error_no_account_xlm">Для создания учетной записи отправьте 1+ XLM на этот адрес</string>
<string name="wallet_notification_address_copied">Адрес скопирован в буфер обмена</string>
<string name="wallet_create_payid_empty">Сначала введите желаемую PayString</string>
<string name="initial_message_scan_header">Приложите, чтобы отсканировать</string>
<string name="initial_message_tap_header">Приложите карту</string>
<string name="details_notification_erase_wallet_not_possible">Ваш баланс на этом кошельке не равен нулю, или у Вас есть неподтвержденные транзакции</string>
<string name="details_notification_security_option_already_active">Это текущая активная опция</string>
<string name="xtz_withdrawal_message_warning">Чтобы не платить повышенную комиссию при следующем пополнении кошелька, уменьшите сумму на %s XTZ</string>
<string name="xtz_withdrawal_message_reduce">Уменьшить на %s XTZ</string>
<string name="xtz_withdrawal_message_ignore">Нет, отправить все</string>
<string name="scan_card_error">Ошибка сканирования карты. Попробуй снова.</string>
<string name="wallet_button_buy">Купить</string>
<string name="wallet_button_sell">Продать</string>
@ -347,7 +331,7 @@
<string name="token_symbol_address_format">%1s (%2s)</string>
<string name="alert_signed_hashes_message">Эта карта не является векселем на предъявителя. В настоящее время мы не можем сопоставить количество подписей на карте с информацией в блокчейне. Это нормально, но в редких случаях может означать, что предыдущий владелец скрывает автономную подпись, что является проблемой безопасности.\n\nНе принимайте эту карту в качестве физического платежа от кого-то, кому Вы не доверяете.\n\nВо всех остальных отношениях - это совершенно безопасно.\n\nTangem — единственный аппаратный кошелек, предлагающий защиту от подсчета подписей.</string>
<string name="wallet_connect">WalletConnect</string>
<string name="wallet_connect_create_tx_message">Запрос на создание транзакции для %2s\n%3s\n\nСумма: %4s\nКомиссия: %5s\nВсего: %6s\nБаланс: %7s</string>
<string name="wallet_connect_create_tx_message">Карта %1s\nЗапрос на создание транзакции для %2s\n%3s\n\nСумма: %4s\nКомиссия: %5s\nВсего: %6s\nБаланс: %7s</string>
<string name="wallet_connect_request_session_start">Запрос на запуск сеанса для карты с идентификатором %1s\nдля %2s\n\nURL: %3s</string>
<string name="wallet_connect_sessions_title">Сессии WalletConnect</string>
<string name="wallet_connect_session_opened">Сеанс WalletConnect открыт с %s</string>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="send_error_insufficient_balance">Недостаточный баланс</string>
<string name="send_error_blockchain_internal">Внутренняя ошибка блокчейна</string>
<string name="send_error_invalid_fee_value">Неверная комиссия</string>
<string name="send_error_dust_amount_format">Минимальная сумма: %s</string>
<string name="send_error_dust_change">Сдача слишком мала</string>
<string name="send_error_no_account_xlm">Для создания учетной записи отправьте 1+ XLM на этот адрес</string>
<string name="send_error_fee_request_failed">Не удалось получить комиссию</string>
<string name="send_error_no_target_account">Целевая учетная запись не создана. Сумма для отправки должна быть %1$s %2$s + плата за создание или больше</string>
<string name="send_error_unknown">Неизвестная ошибка</string>
<string name="send_validation_invalid_address">Неверный адрес</string>
<string name="send_validation_invalid_amount">Недопустимая сумма</string>
<string name="send_validation_amount_exceeds_balance">Сумма превышает баланс</string>
<string name="send_validation_invalid_total">Общая сумма превышает баланс</string>
<string name="send_validation_invalid_fee">Комиссия превышает баланс</string>
<string name="xtz_withdrawal_message_warning">Чтобы не платить повышенную комиссию при следующем пополнении кошелька, уменьшите сумму на %s XTZ</string>
<string name="xtz_withdrawal_message_reduce">Уменьшить на %s XTZ</string>
<string name="xtz_withdrawal_message_ignore">Нет, отправить все</string>
</resources>

View file

@ -47,9 +47,20 @@
<string name="alert_funds_restoration_message">Если вы совершили ошибку с выбором сети при переводе средств с биржи, эта инструкция поможет вам восстановить средства</string>
<string name="common_custom">Пользовательский</string>
<string name="common_notice">Уведомление</string>
<string name="common_attention">Внимание</string>
<string name="common_server_unavailable">Сервер недоступен, повторите попытку позднее</string>
<string name="main_page_balance">Баланс</string>
<string name="main_processing_full_amount">В сумме учтены не все монеты</string>
<string name="main_manage_tokens">Управление токенами</string>
<string name="token_item_no_rate">Нет цены</string>
<string name="token_details_hide_alert_title">Скрыть %s</string>
<string name="token_details_hide_alert_hide">Скрыть</string>
<string name="token_details_hide_alert_message">Вы скрываете токен с главного экрана, но в любой момент сможете добавить его обратно.</string>
<string name="token_details_unable_hide_alert_title">Невозможно скрыть %s</string>
<string name="token_details_unable_hide_alert_message">Токен %s является основной валютой в сети %s и не может быть скрыт, до тех пор пока у вас в списке есть другие токены этой сети.</string>
<string name="wallet_connect_network_not_found_format">Сеть %s не найдена. Пожалуйста, добавьте её и попробуйте заново.</string>
</resources>

View file

@ -70,11 +70,6 @@
<string name="send_fee_label">Fee</string>
<string name="send_total_label">Total</string>
<string name="send_max_amount_label">Maximum amount</string>
<string name="send_validation_invalid_address">Invalid address</string>
<string name="send_validation_invalid_amount">Invalid amount</string>
<string name="send_validation_amount_exceeds_balance">Amount Exceeds Balance</string>
<string name="send_validation_invalid_total">Total Exceeds Balance</string>
<string name="send_validation_invalid_fee">Fee Exceeds Balance</string>
<string name="send_fee_picker_low">Low</string>
<string name="send_fee_picker_normal">Normal</string>
<string name="send_fee_picker_priority">Priority</string>
@ -173,23 +168,14 @@
\nThis disclaimer was amended for the last time on October 1st, 2020.</string>
<string name="disclaimer_title">Terms of service</string>
<string name="send_error_fee_request_failed">Failed to get fee</string>
<string name="send_error_no_target_account">Target account is not created. Amount to send should be %1$s %2$s + fee or more to create</string>
<string name="wallet_notification_no_internet">No internet connection</string>
<string name="wallet_notification_no_internet">No internet connection</string>
<string name="send_error_unknown">Unknown error</string>
<string name="send_error_payid_verification_failed">PayString verification failed</string>
<string name="send_error_payid_unsupported_by_blockchain">PayString unsupported by blockchain</string>
<string name="send_error_payid_not_registered">PayString not registered</string>
<string name="send_error_payid_request_failed">PayString request failed</string>
<string name="send_error_address_same_as_wallet">Address is the same as wallet address</string>
<string name="send_error_insufficient_balance">Insufficient balance</string>
<string name="send_error_blockchain_internal">Blockchain internal error</string>
<string name="send_error_invalid_fee_value">Invalid Fee</string>
<string name="send_error_dust_amount_format">Minimum amount is %s</string>
<string name="send_error_dust_change">Change is too small</string>
<string name="send_error_no_account_xlm">To create account send 1+ XLM to this address</string>
<string name="wallet_notification_address_copied">Address was copied to clipboard</string>
@ -201,9 +187,4 @@
<string name="details_notification_erase_wallet_not_possible">Your balance on this wallet is not zero, or you have unconfirmed transactions</string>
<string name="details_notification_security_option_already_active">This is the currently active option</string>
<string name="xtz_withdrawal_message_warning">To avoid paying an increased commission the next time you top up your wallet, reduce the amount by %s XTZ</string>
<string name="xtz_withdrawal_message_reduce">Reduce by %s XTZ</string>
<string name="xtz_withdrawal_message_ignore">No, send all</string>
</resources>
</resources>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="send_error_insufficient_balance">Insufficient balance</string>
<string name="send_error_blockchain_internal">Blockchain internal error</string>
<string name="send_error_invalid_fee_value">Invalid Fee</string>
<string name="send_error_dust_amount_format">Minimum amount is %s</string>
<string name="send_error_dust_change">Change is too small</string>
<string name="send_error_no_account_xlm">To create account send 1+ XLM to this address</string>
<string name="send_error_fee_request_failed">Failed to get fee</string>
<string name="send_error_no_target_account">Target account is not created. Amount to send should be %s %s + fee or more to create</string>
<string name="send_error_unknown">Unknown error</string>
<string name="send_validation_invalid_address">Invalid address</string>
<string name="send_validation_invalid_amount">Invalid amount</string>
<string name="send_validation_amount_exceeds_balance">Amount Exceeds Balance</string>
<string name="send_validation_invalid_total">Total Exceeds Balance</string>
<string name="send_validation_invalid_fee">Fee Exceeds Balance</string>
<string name="xtz_withdrawal_message_warning">To avoid paying an increased commission the next time you top up your wallet, reduce the amount by %s XTZ</string>
<string name="xtz_withdrawal_message_reduce">Reduce by %s XTZ</string>
<string name="xtz_withdrawal_message_ignore">No, send all</string>
</resources>

View file

@ -47,9 +47,20 @@
<string name="alert_funds_restoration_message">If you chose the wrong network during your crypto transfer from the exchange, this guide will help you recover your funds</string>
<string name="common_custom">Custom</string>
<string name="common_notice">Notice</string>
<string name="common_attention">Attention</string>
<string name="common_server_unavailable">The server is not available, please try again later</string>
<string name="main_page_balance">Total balance</string>
<string name="main_processing_full_amount">The amount does not include some of your funds</string>
<string name="main_manage_tokens">Manage tokens</string>
<string name="token_item_no_rate">No rate</string>
<string name="token_details_hide_alert_title">Hide %s</string>
<string name="token_details_hide_alert_hide">Hide</string>
<string name="token_details_hide_alert_message">You hide the token from the main screen, but you can add it back at any time.</string>
<string name="token_details_unable_hide_alert_title">Unable to hide %s</string>
<string name="token_details_unable_hide_alert_message">The %s token is the main currency on the %s network and cannot be hidden as long as you have other tokens on this network in the list.</string>
<string name="wallet_connect_network_not_found_format">%s network not found. Please, add it first and try again.</string>
</resources>

View file

@ -1,5 +0,0 @@
<resources>
<string name="sign_rough"></string>
</resources>

View file

@ -141,7 +141,7 @@
<string name="wallet_connect_card_number" translatable="false">Card: %s</string>
<string name="wallet_connect_scan_card_message" translatable="false">Tap card to bind to wallet connect</string>
<string name="wallet_connect_create_tx_message" translatable="false">Card: %1s
<string name="wallet_connect_create_tx_message" translatable="false">Card: %1s\n
Request to create transaction for %2s
\n%3s

View file

@ -59,7 +59,7 @@ dependencies {
implementation implementation(project(path: ':network'))
implementation implementation(project(path: ':common'))
implementation 'com.tangem:blockchain:develop-88'
implementation 'com.tangem:blockchain:develop-93'
// implementation 'com.tangem:blockchain:0.0.1'
implementation 'com.tangem.tangem-sdk-kotlin:core:develop-154'
implementation 'com.tangem.tangem-sdk-kotlin:android:develop-154'

View file

@ -16,11 +16,13 @@ suspend fun TangemTechService.getTokens(
suspend fun TangemTechService.getListOfCoins(
networkIds: List<String>,
active: Boolean? = null,
searchText: String? = null,
offset: Int? = null,
limit: Int? = null
): Result<CoinsResponse> = coins(
networkIds = networkIds.joinToString(","),
active = active,
searchText = searchText,
offset = offset,
limit = limit

View file

@ -56,6 +56,17 @@ platform :android do
"android.injected.signing.key.alias" => options[:key_alias],
"android.injected.signing.key.password" => options[:key_password],
})
gradle(
task: "assemble",
build_type: "Release",
properties: {
'versionCode' => options[:versionCode],
'versionName' => options[:versionName],
"android.injected.signing.store.file" => options[:keystore],
"android.injected.signing.store.password" => options[:store_password],
"android.injected.signing.key.alias" => options[:key_alias],
"android.injected.signing.key.password" => options[:key_password],
})
end
desc "Submit a new Beta Build to Firebase App Distribution"

View file

@ -13,4 +13,4 @@ org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryErro
org.gradle.parallel=true
org.gradle.daemon=true
android.useAndroidX=true
android.enableJetifier=true
android.enableJetifier=true